summaryrefslogtreecommitdiff
path: root/contrib/cc
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/cc')
-rw-r--r--contrib/cc/CCSolve.v22
-rw-r--r--contrib/cc/README20
-rw-r--r--contrib/cc/ccalgo.ml357
-rw-r--r--contrib/cc/ccalgo.mli84
-rw-r--r--contrib/cc/ccproof.ml157
-rw-r--r--contrib/cc/ccproof.mli45
-rw-r--r--contrib/cc/cctac.ml4247
7 files changed, 932 insertions, 0 deletions
diff --git a/contrib/cc/CCSolve.v b/contrib/cc/CCSolve.v
new file mode 100644
index 00000000..fab6f775
--- /dev/null
+++ b/contrib/cc/CCSolve.v
@@ -0,0 +1,22 @@
+(************************************************************************)
+(* 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: CCSolve.v,v 1.4.2.1 2004/07/16 19:29:58 herbelin Exp $ *)
+
+Ltac CCsolve :=
+ repeat
+ match goal with
+ | H:?X1 |- ?X2 =>
+ let Heq := fresh "Heq" in
+ (assert (Heq : X2 = X1); [ congruence | rewrite Heq; exact H ])
+ | H:?X1,G:(?X2 -> ?X3) |- _ =>
+ let Heq := fresh "Heq" in
+ (assert (Heq : X2 = X1);
+ [ congruence
+ | rewrite Heq in G; generalize (G H); clear G; intro G ])
+ end.
diff --git a/contrib/cc/README b/contrib/cc/README
new file mode 100644
index 00000000..073b140e
--- /dev/null
+++ b/contrib/cc/README
@@ -0,0 +1,20 @@
+
+cctac: congruence-closure for coq
+
+author: Pierre Corbineau,
+ Stage de DEA au LSV, ENS Cachan
+ Thèse au LRI, Université Paris Sud XI
+
+Files :
+
+- ccalgo.ml : congruence closure algorithm
+- ccproof.ml : proof generation code
+- cctac.ml4 : the tactic itself
+- CCSolve.v : a small Ltac tactic based on congruence
+
+Known Bugs : the congruence tactic can fail due to type dependencies.
+
+Related documents:
+ Peter J. Downey, Ravi Sethi, and Robert E. Tarjan.
+ Variations on the common subexpression problem.
+ JACM, 27(4):758-771, October 1980.
diff --git a/contrib/cc/ccalgo.ml b/contrib/cc/ccalgo.ml
new file mode 100644
index 00000000..e73a6221
--- /dev/null
+++ b/contrib/cc/ccalgo.ml
@@ -0,0 +1,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
+
+
diff --git a/contrib/cc/ccalgo.mli b/contrib/cc/ccalgo.mli
new file mode 100644
index 00000000..47cdb3ea
--- /dev/null
+++ b/contrib/cc/ccalgo.mli
@@ -0,0 +1,84 @@
+(************************************************************************)
+(* 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.mli,v 1.6.2.1 2004/07/16 19:29:58 herbelin Exp $ *)
+
+type pa_constructor
+ (*{head: int; arity: int; args: (int * int) list}*)
+
+module PacMap:Map.S with type key=int * int
+
+type term =
+ Symb of Term.constr
+ | Appli of term * term
+ | Constructor of Names.constructor*int*int
+
+type rule =
+ Congruence
+ | Axiom of Names.identifier
+ | Injection of int*int*int*int
+
+type equality =
+ {lhs : int;
+ rhs : int;
+ rule : rule}
+
+module ST :
+sig
+ type t
+ val empty : unit -> t
+ val enter : int -> int * int -> t -> unit
+ val query : int * int -> t -> int
+ val delete : int -> t -> unit
+ val delete_list : int list -> t -> unit
+end
+
+module UF :
+sig
+ type t
+ exception Discriminable of int * int * int * int * t
+ val empty : unit -> t
+ val find : t -> int -> int
+ val size : t -> int -> int
+ val get_constructor : t -> int -> Names.constructor
+ val pac_arity : t -> int -> int * int -> int
+ val mem_node_pac : t -> int -> int * int -> int
+ val add_pacs : t -> int -> pa_constructor PacMap.t ->
+ int list * equality list
+ val term : t -> int -> term
+ val subterms : t -> int -> int * int
+ val add : t -> term -> int
+ val union : t -> int -> int -> equality -> int list * equality list
+ val join_path : t -> int -> int ->
+ ((int*int)*equality) list*
+ ((int*int)*equality) list
+end
+
+
+val combine_rec : UF.t -> int list -> equality list
+val process_rec : UF.t -> equality list -> int list
+
+val cc : UF.t -> unit
+
+val make_uf :
+ (Names.identifier * (term * term)) list -> UF.t
+
+val add_one_diseq : UF.t -> (term * term) -> int * int
+
+val add_disaxioms :
+ UF.t -> (Names.identifier * (term * term)) list ->
+ (Names.identifier * (int * int)) list
+
+val check_equal : UF.t -> int * int -> bool
+
+val find_contradiction : UF.t ->
+ (Names.identifier * (int * int)) list ->
+ (Names.identifier * (int * int))
+
+
+
diff --git a/contrib/cc/ccproof.ml b/contrib/cc/ccproof.ml
new file mode 100644
index 00000000..fa525e65
--- /dev/null
+++ b/contrib/cc/ccproof.ml
@@ -0,0 +1,157 @@
+(************************************************************************)
+(* 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: ccproof.ml,v 1.8.2.1 2004/07/16 19:29:58 herbelin Exp $ *)
+
+(* This file uses the (non-compressed) union-find structure to generate *)
+(* proof-trees that will be transformed into proof-terms in cctac.ml4 *)
+
+open Util
+open Names
+open Ccalgo
+
+type proof=
+ Ax of identifier
+ | SymAx of identifier
+ | Refl of term
+ | Trans of proof*proof
+ | Congr of proof*proof
+ | Inject of proof*constructor*int*int
+
+let pcongr=function
+ Refl t1, Refl t2 -> Refl (Appli (t1,t2))
+ | p1, p2 -> Congr (p1,p2)
+
+let rec ptrans=function
+ Refl _, p ->p
+ | p, Refl _ ->p
+ | Trans(p1,p2), p3 ->ptrans(p1,ptrans (p2,p3))
+ | Congr(p1,p2), Congr(p3,p4) ->pcongr(ptrans(p1,p3),ptrans(p2,p4))
+ | Congr(p1,p2), Trans(Congr(p3,p4),p5) ->
+ ptrans(pcongr(ptrans(p1,p3),ptrans(p2,p4)),p5)
+ | p1, p2 ->Trans (p1,p2)
+
+let rec psym=function
+ Refl p->Refl p
+ | SymAx s->Ax s
+ | Ax s-> SymAx s
+ | Inject (p,c,n,a)-> Inject (psym p,c,n,a)
+ | Trans (p1,p2)-> ptrans (psym p2,psym p1)
+ | Congr (p1,p2)-> pcongr (psym p1,psym p2)
+
+let pcongr=function
+ Refl t1, Refl t2 ->Refl (Appli (t1,t2))
+ | p1, p2 -> Congr (p1,p2)
+
+let build_proof uf=
+
+ let rec equal_proof i j=
+ if i=j then Refl (UF.term uf i) else
+ let (li,lj)=UF.join_path uf i j in
+ ptrans (path_proof i li,psym (path_proof j lj))
+
+ and edge_proof ((i,j),eq)=
+ let pi=equal_proof i eq.lhs in
+ let pj=psym (equal_proof j eq.rhs) in
+ let pij=
+ match eq.rule with
+ Axiom s->Ax s
+ | Congruence ->congr_proof eq.lhs eq.rhs
+ | Injection (ti,tj,c,a) ->
+ let p=equal_proof ti tj in
+ let p1=constr_proof ti ti c 0
+ and p2=constr_proof tj tj c 0 in
+ match UF.term uf c with
+ Constructor (cstr,nargs,nhyps) ->
+ Inject(ptrans(psym p1,ptrans(p,p2)),cstr,nhyps,a)
+ | _ -> anomaly "injection on non-constructor terms"
+ in ptrans(ptrans (pi,pij),pj)
+
+ and constr_proof i j c n=
+ try
+ let nj=UF.mem_node_pac uf j (c,n) in
+ let (ni,arg)=UF.subterms uf j in
+ let p=constr_proof ni nj c (n+1) in
+ let targ=UF.term uf arg in
+ ptrans (equal_proof i j, pcongr (p,Refl targ))
+ with Not_found->equal_proof i j
+
+ and path_proof i=function
+ [] -> Refl (UF.term uf i)
+ | x::q->ptrans (path_proof (snd (fst x)) q,edge_proof x)
+
+ and congr_proof i j=
+ let (i1,i2) = UF.subterms uf i
+ and (j1,j2) = UF.subterms uf j in
+ pcongr (equal_proof i1 j1, equal_proof i2 j2)
+
+ and discr_proof i ci j cj=
+ let p=equal_proof i j
+ and p1=constr_proof i i ci 0
+ and p2=constr_proof j j cj 0 in
+ ptrans(psym p1,ptrans(p,p2))
+ in
+ function
+ `Prove_goal (i,j) | `Refute_hyp (i,j) -> equal_proof i j
+ | `Discriminate (i,ci,j,cj)-> discr_proof i ci j cj
+
+let rec nth_arg t n=
+ match t with
+ Appli (t1,t2)->
+ if n>0 then
+ nth_arg t1 (n-1)
+ else t2
+ | _ -> anomaly "nth_arg: not enough args"
+
+let rec type_proof axioms p=
+ match p with
+ Ax s->List.assoc s axioms
+ | SymAx s-> let (t1,t2)=List.assoc s axioms in (t2,t1)
+ | Refl t-> t,t
+ | Trans (p1,p2)->
+ let (s1,t1)=type_proof axioms p1
+ and (t2,s2)=type_proof axioms p2 in
+ if t1=t2 then (s1,s2) else anomaly "invalid cc transitivity"
+ | Congr (p1,p2)->
+ let (i1,j1)=type_proof axioms p1
+ and (i2,j2)=type_proof axioms p2 in
+ Appli (i1,i2),Appli (j1,j2)
+ | Inject (p,c,n,a)->
+ let (ti,tj)=type_proof axioms p in
+ nth_arg ti (n-a),nth_arg tj (n-a)
+
+let by_contradiction uf diseq axioms disaxioms=
+ try
+ let id,cpl=find_contradiction uf diseq in
+ let prf=build_proof uf (`Refute_hyp cpl) in
+ if List.assoc id disaxioms=type_proof axioms prf then
+ `Refute_hyp (id,prf)
+ else
+ anomaly "wrong proof generated"
+ with Not_found ->
+ errorlabstrm "Congruence" (Pp.str "I couldn't solve goal")
+
+let cc_proof axioms disaxioms glo=
+ try
+ let uf=make_uf axioms in
+ let diseq=add_disaxioms uf disaxioms in
+ match glo with
+ Some cpl ->
+ let goal=add_one_diseq uf cpl in cc uf;
+ if check_equal uf goal then
+ let prf=build_proof uf (`Prove_goal goal) in
+ if cpl=type_proof axioms prf then
+ `Prove_goal prf
+ else anomaly "wrong proof generated"
+ else by_contradiction uf diseq axioms disaxioms
+ | None -> cc uf; by_contradiction uf diseq axioms disaxioms
+ with UF.Discriminable (i,ci,j,cj,uf) ->
+ let prf=build_proof uf (`Discriminate (i,ci,j,cj)) in
+ `Discriminate (UF.get_constructor uf ci,prf)
+
+
diff --git a/contrib/cc/ccproof.mli b/contrib/cc/ccproof.mli
new file mode 100644
index 00000000..887ed070
--- /dev/null
+++ b/contrib/cc/ccproof.mli
@@ -0,0 +1,45 @@
+(************************************************************************)
+(* 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: ccproof.mli,v 1.6.2.1 2004/07/16 19:29:59 herbelin Exp $ *)
+
+open Ccalgo
+open Names
+
+type proof =
+ Ax of identifier
+ | SymAx of identifier
+ | Refl of term
+ | Trans of proof * proof
+ | Congr of proof * proof
+ | Inject of proof * constructor * int * int
+
+val pcongr : proof * proof -> proof
+val ptrans : proof * proof -> proof
+val psym : proof -> proof
+val pcongr : proof * proof -> proof
+
+val build_proof :
+ UF.t ->
+ [ `Discriminate of int * int * int * int
+ | `Prove_goal of int * int
+ | `Refute_hyp of int * int ]
+ -> proof
+
+val type_proof :
+ (identifier * (term * term)) list -> proof -> term * term
+
+val cc_proof :
+ (identifier * (term * term)) list ->
+ (identifier * (term * term)) list ->
+ (term * term) option ->
+ [ `Discriminate of constructor * proof
+ | `Prove_goal of proof
+ | `Refute_hyp of identifier * proof ]
+
+
diff --git a/contrib/cc/cctac.ml4 b/contrib/cc/cctac.ml4
new file mode 100644
index 00000000..49fe46fe
--- /dev/null
+++ b/contrib/cc/cctac.ml4
@@ -0,0 +1,247 @@
+(************************************************************************)
+(* 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 *)
+(************************************************************************)
+
+(*i camlp4deps: "parsing/grammar.cma" i*)
+
+(* $Id: cctac.ml4,v 1.13.2.1 2004/07/16 19:29:59 herbelin Exp $ *)
+
+(* This file is the interface between the c-c algorithm and Coq *)
+
+open Evd
+open Proof_type
+open Names
+open Libnames
+open Nameops
+open Inductiveops
+open Declarations
+open Term
+open Termops
+open Tacmach
+open Tactics
+open Tacticals
+open Ccalgo
+open Tacinterp
+open Ccproof
+open Pp
+open Util
+open Format
+
+exception Not_an_eq
+
+let fail()=raise Not_an_eq
+
+let constant dir s = lazy (Coqlib.gen_constant "CC" dir s)
+
+let f_equal_theo = constant ["Init";"Logic"] "f_equal"
+
+let eq_rect_theo = constant ["Init";"Logic"] "eq_rect"
+
+(* decompose member of equality in an applicative format *)
+
+let rec decompose_term env t=
+ match kind_of_term t with
+ App (f,args)->
+ let tf=decompose_term env f in
+ let targs=Array.map (decompose_term env) args in
+ Array.fold_left (fun s t->Appli (s,t)) tf targs
+ | Construct c->
+ let (_,oib)=Global.lookup_inductive (fst c) in
+ let nargs=mis_constructor_nargs_env env c in
+ Constructor (c,nargs,nargs-oib.mind_nparams)
+ | _ ->(Symb t)
+
+(* decompose equality in members and type *)
+
+let rec eq_type_of_term term=
+ match kind_of_term term with
+ App (f,args)->
+ (try
+ let ref = reference_of_constr f in
+ if ref=Coqlib.glob_eq && (Array.length args)=3
+ then (true,args.(0),args.(1),args.(2))
+ else
+ if ref=(Lazy.force Coqlib.coq_not_ref) &&
+ (Array.length args)=1 then
+ let (pol,t,a,b)=eq_type_of_term args.(0) in
+ if pol then (false,t,a,b) else fail ()
+ else fail ()
+ with Not_found -> fail ())
+ | Prod (_,eq,ff) ->
+ (try
+ let ref = reference_of_constr ff in
+ if ref=(Lazy.force Coqlib.coq_False_ref) then
+ let (pol,t,a,b)=eq_type_of_term eq in
+ if pol then (false,t,a,b) else fail ()
+ else fail ()
+ with Not_found -> fail ())
+ | _ -> fail ()
+
+(* read an equality *)
+
+let read_eq env term=
+ let (pol,_,t1,t2)=eq_type_of_term term in
+ (pol,(decompose_term env t1,decompose_term env t2))
+
+(* rebuild a term from applicative format *)
+
+let rec make_term=function
+ Symb s->s
+ | Constructor(c,_,_)->mkConstruct c
+ | Appli (s1,s2)->
+ make_app [(make_term s2)] s1
+and make_app l=function
+ Symb s->applistc s l
+ | Constructor(c,_,_)->applistc (mkConstruct c) l
+ | Appli (s1,s2)->make_app ((make_term s2)::l) s1
+
+(* store all equalities from the context *)
+
+let rec read_hyps env=function
+ []->[],[]
+ | (id,_,e)::hyps->let eq,diseq=read_hyps env hyps in
+ try let pol,cpl=read_eq env e in
+ if pol then
+ ((id,cpl)::eq),diseq
+ else
+ eq,((id,cpl)::diseq)
+ with Not_an_eq -> eq,diseq
+
+(* build a problem ( i.e. read the goal as an equality ) *)
+
+let make_prb gl=
+ let env=pf_env gl in
+ let eq,diseq=read_hyps env gl.it.evar_hyps in
+ try
+ let pol,cpl=read_eq env gl.it.evar_concl in
+ if pol then (eq,diseq,Some cpl) else assert false with
+ Not_an_eq -> (eq,diseq,None)
+
+(* indhyps builds the array of arrays of constructor hyps for (ind largs) *)
+
+let build_projection intype outtype (cstr:constructor) special default gls=
+ let env=pf_env gls in
+ let (h,argv) =
+ try destApplication intype with
+ Invalid_argument _ -> (intype,[||]) in
+ let ind=destInd h in
+ let types=Inductive.arities_of_constructors env ind in
+ let lp=Array.length types in
+ let ci=(snd cstr)-1 in
+ let branch i=
+ let ti=Term.prod_appvect types.(i) argv in
+ let rc=fst (Sign.decompose_prod_assum ti) in
+ let head=
+ if i=ci then special else default in
+ Sign.it_mkLambda_or_LetIn head rc in
+ let branches=Array.init lp branch in
+ let casee=mkRel 1 in
+ let pred=mkLambda(Anonymous,intype,outtype) in
+ let case_info=make_default_case_info (pf_env gls) RegularStyle ind in
+ let body= mkCase(case_info, pred, casee, branches) in
+ let id=pf_get_new_id (id_of_string "t") gls in
+ mkLambda(Name id,intype,body)
+
+(* generate an adhoc tactic following the proof tree *)
+
+let rec proof_tac axioms=function
+ Ax id->exact_check (mkVar id)
+ | SymAx id->tclTHEN symmetry (exact_check (mkVar id))
+ | Refl t->reflexivity
+ | Trans (p1,p2)->let t=(make_term (snd (type_proof axioms p1))) in
+ (tclTHENS (transitivity t)
+ [(proof_tac axioms p1);(proof_tac axioms p2)])
+ | Congr (p1,p2)->
+ fun gls->
+ let (f1,f2)=(type_proof axioms p1)
+ and (x1,x2)=(type_proof axioms p2) in
+ let tf1=make_term f1 and tx1=make_term x1
+ and tf2=make_term f2 and tx2=make_term x2 in
+ let typf=pf_type_of gls tf1 and typx=pf_type_of gls tx1
+ and typfx=pf_type_of gls (mkApp(tf1,[|tx1|])) in
+ let id=pf_get_new_id (id_of_string "f") gls in
+ let appx1=mkLambda(Name id,typf,mkApp(mkRel 1,[|tx1|])) in
+ let lemma1=
+ mkApp(Lazy.force f_equal_theo,[|typf;typfx;appx1;tf1;tf2|])
+ and lemma2=
+ mkApp(Lazy.force f_equal_theo,[|typx;typfx;tf2;tx1;tx2|]) in
+ (tclTHENS (transitivity (mkApp(tf2,[|tx1|])))
+ [tclTHEN (apply lemma1) (proof_tac axioms p1);
+ tclFIRST
+ [tclTHEN (apply lemma2) (proof_tac axioms p2);
+ reflexivity;
+ fun gls ->
+ errorlabstrm "Congruence"
+ (Pp.str
+ "I don't know how to handle dependent equality")]]
+ gls)
+ | Inject (prf,cstr,nargs,argind) as gprf->
+ (fun gls ->
+ let ti,tj=type_proof axioms prf in
+ let ai,aj=type_proof axioms gprf in
+ let cti=make_term ti in
+ let ctj=make_term tj in
+ let cai=make_term ai in
+ let intype=pf_type_of gls cti in
+ let outtype=pf_type_of gls cai in
+ let special=mkRel (1+nargs-argind) in
+ let default=make_term ai in
+ let proj=build_projection intype outtype cstr special default gls in
+ let injt=
+ mkApp (Lazy.force f_equal_theo,[|intype;outtype;proj;cti;ctj|]) in
+ tclTHEN (apply injt) (proof_tac axioms prf) gls)
+
+let refute_tac axioms disaxioms id p gls =
+ let t1,t2=List.assoc id disaxioms in
+ let tt1=make_term t1 and tt2=make_term t2 in
+ let intype=pf_type_of gls tt1 in
+ let neweq=
+ mkApp(constr_of_reference Coqlib.glob_eq,
+ [|intype;tt1;tt2|]) in
+ let hid=pf_get_new_id (id_of_string "Heq") gls in
+ let false_t=mkApp (mkVar id,[|mkVar hid|]) in
+ tclTHENS (true_cut (Name hid) neweq)
+ [proof_tac axioms p; simplest_elim false_t] gls
+
+let discriminate_tac axioms cstr p gls =
+ let t1,t2=type_proof axioms p in
+ let tt1=make_term t1 and tt2=make_term t2 in
+ let intype=pf_type_of gls tt1 in
+ let concl=pf_concl gls in
+ let outsort=mkType (new_univ ()) in
+ let xid=pf_get_new_id (id_of_string "X") gls in
+ let tid=pf_get_new_id (id_of_string "t") gls in
+ let identity=mkLambda(Name xid,outsort,mkLambda(Name tid,mkRel 1,mkRel 1)) in
+ let trivial=pf_type_of gls identity in
+ let outtype=mkType (new_univ ()) in
+ let pred=mkLambda(Name xid,outtype,mkRel 1) in
+ let hid=pf_get_new_id (id_of_string "Heq") gls in
+ let proj=build_projection intype outtype cstr trivial concl gls in
+ let injt=mkApp (Lazy.force f_equal_theo,
+ [|intype;outtype;proj;tt1;tt2;mkVar hid|]) in
+ let endt=mkApp (Lazy.force eq_rect_theo,
+ [|outtype;trivial;pred;identity;concl;injt|]) in
+ let neweq=mkApp(constr_of_reference Coqlib.glob_eq,[|intype;tt1;tt2|]) in
+ tclTHENS (true_cut (Name hid) neweq)
+ [proof_tac axioms p;exact_check endt] gls
+
+(* wrap everything *)
+
+let cc_tactic gls=
+ Library.check_required_library ["Coq";"Init";"Logic"];
+ let (axioms,disaxioms,glo)=make_prb gls in
+ match (cc_proof axioms disaxioms glo) with
+ `Prove_goal p -> proof_tac axioms p gls
+ | `Refute_hyp (id,p) -> refute_tac axioms disaxioms id p gls
+ | `Discriminate (cstr,p) -> discriminate_tac axioms cstr p gls
+
+(* Tactic registration *)
+
+TACTIC EXTEND CC
+ [ "Congruence" ] -> [ tclSOLVE [tclTHEN (tclREPEAT introf) cc_tactic] ]
+END
+