summaryrefslogtreecommitdiff
path: root/contrib/cc/ccalgo.ml
blob: e73a6221ef19a5aa53d6dbe06bfd7b5c8c064cb3 (plain)
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