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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
|
(************************************************************************)
(* v * The Coq Proof Assistant / The Coq Development Team *)
(* <O___,, * CNRS-Ecole Polytechnique-INRIA Futurs-Universite Paris Sud *)
(* \VV/ **************************************************************)
(* // * This file is distributed under the terms of the *)
(* * GNU Lesser General Public License Version 2.1 *)
(************************************************************************)
(* $Id: ccalgo.ml,v 1.6.2.1 2004/07/16 19:29:58 herbelin Exp $ *)
(* This file implements the basic congruence-closure algorithm by *)
(* Downey,Sethi and Tarjan. *)
open Util
open Names
open Term
let init_size=251
type pa_constructor=
{head_constr: int;
arity:int;
nhyps:int;
args:int list;
term_head:int}
module PacMap=Map.Make(struct type t=int*int let compare=compare end)
type term=
Symb of constr
| Appli of term*term
| Constructor of constructor*int*int (* constructor arity+ nhyps *)
type rule=
Congruence
| Axiom of identifier
| Injection of int*int*int*int (* terms+head+arg position *)
type equality = {lhs:int;rhs:int;rule:rule}
let swap eq=
let swap_rule=match eq.rule with
Congruence -> Congruence
| Injection (i,j,c,a) -> Injection (j,i,c,a)
| Axiom id -> anomaly "no symmetry for axioms"
in {lhs=eq.rhs;rhs=eq.lhs;rule=swap_rule}
(* Signature table *)
module ST=struct
(* l: sign -> term r: term -> sign *)
type t = {toterm:(int*int,int) Hashtbl.t;
tosign:(int,int*int) Hashtbl.t}
let empty ()=
{toterm=Hashtbl.create init_size;
tosign=Hashtbl.create init_size}
let enter t sign st=
if Hashtbl.mem st.toterm sign then
anomaly "enter: signature already entered"
else
Hashtbl.replace st.toterm sign t;
Hashtbl.replace st.tosign t sign
let query sign st=Hashtbl.find st.toterm sign
let delete t st=
try let sign=Hashtbl.find st.tosign t in
Hashtbl.remove st.toterm sign;
Hashtbl.remove st.tosign t
with
Not_found -> ()
let rec delete_list l st=
match l with
[]->()
| t::q -> delete t st;delete_list q st
end
(* Basic Union-Find algo w/o path compression *)
module UF = struct
module IndMap=Map.Make(struct type t=inductive let compare=compare end)
type representative=
{mutable nfathers:int;
mutable fathers:int list;
mutable constructors:pa_constructor PacMap.t;
mutable inductives:(int * int) IndMap.t}
type cl = Rep of representative| Eqto of int*equality
type vertex = Leaf| Node of (int*int)
type node =
{clas:cl;
vertex:vertex;
term:term;
mutable node_constr: int PacMap.t}
type t={mutable size:int;
map:(int,node) Hashtbl.t;
syms:(term,int) Hashtbl.t;
sigtable:ST.t}
let empty ():t={size=0;
map=Hashtbl.create init_size;
syms=Hashtbl.create init_size;
sigtable=ST.empty ()}
let rec find uf i=
match (Hashtbl.find uf.map i).clas with
Rep _ -> i
| Eqto (j,_) ->find uf j
let get_representative uf i=
let node=Hashtbl.find uf.map i in
match node.clas with
Rep r ->r
| _ -> anomaly "get_representative: not a representative"
let get_constructor uf i=
match (Hashtbl.find uf.map i).term with
Constructor (cstr,_,_)->cstr
| _ -> anomaly "get_constructor: not a constructor"
let fathers uf i=
(get_representative uf i).fathers
let size uf i=
(get_representative uf i).nfathers
let add_father uf i t=
let r=get_representative uf i in
r.nfathers<-r.nfathers+1;
r.fathers<-t::r.fathers
let pac_map uf i=
(get_representative uf i).constructors
let pac_arity uf i sg=
(PacMap.find sg (get_representative uf i).constructors).arity
let add_node_pac uf i sg j=
let node=Hashtbl.find uf.map i in
if not (PacMap.mem sg node.node_constr) then
node.node_constr<-PacMap.add sg j node.node_constr
let mem_node_pac uf i sg=
PacMap.find sg (Hashtbl.find uf.map i).node_constr
exception Discriminable of int * int * int * int * t
let add_pacs uf i pacs =
let rep=get_representative uf i in
let pending=ref [] and combine=ref [] in
let add_pac sg pac=
try
let opac=PacMap.find sg rep.constructors in
if (snd sg)>0 then () else
let tk=pac.term_head
and tl=opac.term_head in
let rec f n lk ll q=
if n > 0 then match (lk,ll) with
k::qk,l::ql->
let eq=
{lhs=k;rhs=l;rule=Injection(tk,tl,pac.head_constr,n)}
in f (n-1) qk ql (eq::q)
| _-> anomaly
"add_pacs : weird error in injection subterms merge"
else q in
combine:=f pac.nhyps pac.args opac.args !combine
with Not_found -> (* Still Unknown Constructor *)
rep.constructors <- PacMap.add sg pac rep.constructors;
pending:=
(fathers uf (find uf pac.term_head)) @rep.fathers@ !pending;
let (c,a)=sg in
if a=0 then
let (ind,_)=get_constructor uf c in
try
let th2,hc2=IndMap.find ind rep.inductives in
raise (Discriminable (pac.term_head,c,th2,hc2,uf))
with Not_found ->
rep.inductives<-
IndMap.add ind (pac.term_head,c) rep.inductives in
PacMap.iter add_pac pacs;
!pending,!combine
let term uf i=(Hashtbl.find uf.map i).term
let subterms uf i=
match (Hashtbl.find uf.map i).vertex with
Node(j,k) -> (j,k)
| _ -> anomaly "subterms: not a node"
let signature uf i=
let j,k=subterms uf i in (find uf j,find uf k)
let nodes uf= (* cherche les noeuds binaires *)
Hashtbl.fold
(fun i node l->
match node.vertex with
Node (_,_)->i::l
| _ ->l) uf.map []
let next uf=
let n=uf.size in uf.size<-n+1; n
let new_representative pm im=
{nfathers=0;
fathers=[];
constructors=pm;
inductives=im}
let rec add uf t=
try Hashtbl.find uf.syms t with
Not_found ->
let b=next uf in
let new_node=
match t with
Symb s ->
{clas=Rep (new_representative PacMap.empty IndMap.empty);
vertex=Leaf;term=t;node_constr=PacMap.empty}
| Appli (t1,t2) ->
let i1=add uf t1 and i2=add uf t2 in
add_father uf (find uf i1) b;
add_father uf (find uf i2) b;
{clas=Rep (new_representative PacMap.empty IndMap.empty);
vertex=Node(i1,i2);term=t;node_constr=PacMap.empty}
| Constructor (c,a,n) ->
let pacs=
PacMap.add (b,a)
{head_constr=b;arity=a;nhyps=n;args=[];term_head=b}
PacMap.empty in
let inds=
if a=0 then
let (ind,_)=c in
IndMap.add ind (b,b) IndMap.empty
else IndMap.empty in
{clas=Rep (new_representative pacs inds);
vertex=Leaf;term=t;node_constr=PacMap.empty}
in
Hashtbl.add uf.map b new_node;
Hashtbl.add uf.syms t b;
b
let link uf i j eq= (* links i -> j *)
let node=Hashtbl.find uf.map i in
Hashtbl.replace uf.map i {node with clas=Eqto (j,eq)}
let union uf i1 i2 eq=
let r1= get_representative uf i1
and r2= get_representative uf i2 in
link uf i1 i2 eq;
r2.nfathers<-r1.nfathers+r2.nfathers;
r2.fathers<-r1.fathers@r2.fathers;
add_pacs uf i2 r1.constructors
let rec down_path uf i l=
match (Hashtbl.find uf.map i).clas with
Eqto(j,t)->down_path uf j (((i,j),t)::l)
| Rep _ ->l
let rec min_path=function
([],l2)->([],l2)
| (l1,[])->(l1,[])
| (((c1,t1)::q1),((c2,t2)::q2)) when c1=c2 -> min_path (q1,q2)
| cpl -> cpl
let join_path uf i j=
assert (find uf i=find uf j);
min_path (down_path uf i [],down_path uf j [])
end
let rec combine_rec uf=function
[]->[]
| t::pending->
let combine=combine_rec uf pending in
let s=UF.signature uf t in
let u=snd (UF.subterms uf t) in
let f (c,a) pac pacs=
if a=0 then pacs else
let sg=(c,a-1) in
UF.add_node_pac uf t sg pac.term_head;
PacMap.add sg {pac with args=u::pac.args;term_head=t} pacs
in
let pacs=PacMap.fold f (UF.pac_map uf (fst s)) PacMap.empty in
let i=UF.find uf t in
let (p,c)=UF.add_pacs uf i pacs in
let combine2=(combine_rec uf p)@c@combine in
try {lhs=t;rhs=ST.query s uf.UF.sigtable;rule=Congruence}::combine2 with
Not_found->
ST.enter t s uf.UF.sigtable;combine2
let rec process_rec uf=function
[]->[]
| eq::combine->
let pending=process_rec uf combine in
let i=UF.find uf eq.lhs
and j=UF.find uf eq.rhs in
if i=j then
pending
else
if (UF.size uf i)<(UF.size uf j) then
let l=UF.fathers uf i in
let (p,c)=UF.union uf i j eq in
let _ =ST.delete_list l uf.UF.sigtable in
let inj_pending=process_rec uf c in
inj_pending@p@l@pending
else
let l=UF.fathers uf j in
let (p,c)=UF.union uf j i (swap eq) in
let _ =ST.delete_list l uf.UF.sigtable in
let inj_pending=process_rec uf c in
inj_pending@p@l@pending
let rec cc_rec uf=function
[]->()
| pending->
let combine=combine_rec uf pending in
let pending0=process_rec uf combine in
cc_rec uf pending0
let cc uf=cc_rec uf (UF.nodes uf)
let rec make_uf=function
[]->UF.empty ()
| (ax,(t1,t2))::q->
let uf=make_uf q in
let i1=UF.add uf t1 in
let i2=UF.add uf t2 in
let j1=UF.find uf i1 and j2=UF.find uf i2 in
if j1=j2 then uf else
let (_,inj_combine)=
UF.union uf j1 j2 {lhs=i1;rhs=i2;rule=Axiom ax} in
let _ = process_rec uf inj_combine in uf
let add_one_diseq uf (t1,t2)=(UF.add uf t1,UF.add uf t2)
let add_disaxioms uf disaxioms=
let f (id,cpl)=(id,add_one_diseq uf cpl) in
List.map f disaxioms
let check_equal uf (i1,i2) = UF.find uf i1 = UF.find uf i2
let find_contradiction uf diseq =
List.find (fun (id,cpl) -> check_equal uf cpl) diseq
|