Source file Congruence.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
(** {1 Simple and Lightweight Congruence and order} *)
module type S = Congruence_intf.S
(** The graph used for the congruence *)
module type TERM = sig
type t
val equal : t -> t -> bool
val hash : t -> int
val subterms : t -> t list
(** Subterms of the term (possibly empty list) *)
val update_subterms : t -> t list -> t
(** Replace immediate subterms by the given list.
This is used to test for equality *)
val pp : t CCFormat.printer
end
module Make(T : TERM) = struct
type term = T.t
module H = CCPersistentHashtbl.Make(struct
type t = term
let equal = T.equal
let hash = T.hash
end)
(** Maps terms to their list of immediate parents, and current
representative *)
type t = {
parents: term list H.t;
mutable next: term H.t;
}
let create ?(size=64) () = {
parents=H.create size;
next=H.create size;
}
let[@inline] set_next_ cc t next : t =
{ cc with next=H.replace cc.next t next }
let[@inline] set_parents_ cc t parents : t =
{ cc with parents=H.replace cc.parents t parents }
let[@inline] next_ cc t = H.get t cc.next |> CCOpt.get_or ~default:t
let[@inline] parents_ cc t = H.get t cc.parents |> CCOpt.get_or ~default:[]
let rec find_ cc (t:term) : term =
let next = next_ cc t in
if T.equal t next
then t
else (
let root = find_ cc next in
if not (T.equal root next) then (
cc.next <- H.replace cc.next t root;
);
root
)
let are_congruent_ cc t1 t2 =
let l1' = List.map (find_ cc) (T.subterms t1) in
let l2' = List.map (find_ cc) (T.subterms t2) in
try
let t1 = T.update_subterms t1 l1' in
let t2 = T.update_subterms t2 l2' in
T.equal t1 t2
with Type.ApplyError _ ->
false
let rec merge_ cc (t1:term) (t2:term) : t =
let t1 = find_ cc t1 in
let t2 = find_ cc t2 in
if T.equal t1 t2 then cc
else (
let left, right = parents_ cc t1, parents_ cc t2 in
let cc = set_next_ cc t1 t2 in
let cc = set_parents_ cc t2 (List.rev_append left right) in
List.fold_left
(fun cc p1 ->
List.fold_left
(fun cc p2 ->
if not (T.equal p1 p2) && are_congruent_ cc p1 p2 then (
merge_ cc p1 p2
) else cc)
cc right)
cc left
)
let rec add cc (t:term) : t =
if H.mem cc.parents t then (
assert (H.mem cc.next t);
cc
) else (
let subs = T.subterms t in
let cc = set_parents_ cc t [] in
let cc = set_next_ cc t t in
let cc = List.fold_left add cc subs in
let cc =
List.fold_left
(fun cc sub ->
let repr = find_ cc sub in
set_parents_ cc repr (t :: parents_ cc repr))
cc
subs
in
let cc =
List.fold_left
(fun cc sub ->
let parents = find_ cc sub |> parents_ cc in
List.fold_left
(fun cc parent_sub ->
if not (T.equal t parent_sub) &&
are_congruent_ cc t parent_sub then (
merge_ cc t parent_sub
) else cc)
cc parents)
cc
subs
in
cc
)
let iter cc f =
H.iter cc.next
(fun mem _ ->
let repr = find_ cc mem in
f ~mem ~repr)
let iter_roots cc f =
H.iter cc.next
(fun t next -> if T.equal t next then f t)
let[@inline] mk_eq cc t1 t2 =
let cc = add cc t1 in
let cc = add cc t2 in
merge_ cc t1 t2
let[@inline] is_eq cc t1 t2 =
let cc = add cc t1 in
let cc = add cc t2 in
T.equal (find_ cc t1) (find_ cc t2)
let pp_debug out (cc:t) : unit =
let module Fmt = CCFormat in
let pp_parent out (t,l) =
Fmt.fprintf out "(@[<hv1>parents@ :of %a@ (@[<v>%a@])@])"
T.pp t (Util.pp_list ~sep:" " T.pp) l
and pp_next out (t,u) =
Fmt.fprintf out "(@[<hv>next@ :of %a@ :is %a@])" T.pp t T.pp u
in
Fmt.fprintf out "(@[<v>cc@ :parent_tbl (@[<v>%a@])@ :next_tbl (@[<v>%a@])@])"
(Util.pp_seq pp_parent) (H.to_seq cc.parents)
(Util.pp_seq pp_next) (H.to_seq cc.next)
end
module FO = Make(struct
module T = Term
type t = T.t
let equal = T.equal
let hash = T.hash
let pp = T.pp
let subterms t = match T.Classic.view t with
| T.Classic.App (_, l) -> l
| _ -> []
let update_subterms t l = match T.view t, l with
| T.App (hd, l), l' when List.length l = List.length l' ->
T.app hd l'
| _, [] -> t
| _ -> assert false
end)