123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267(************************************************************************)(* * The Rocq Prover / The Rocq Development Team *)(* v * Copyright INRIA, CNRS and contributors *)(* <O___,, * (see version control and CREDITS file for authors & dates) *)(* \VV/ **************************************************************)(* // * This file is distributed under the terms of the *)(* * GNU Lesser General Public License Version 2.1 *)(* * (see LICENSE file for the text of the license) *)(************************************************************************)openUtil(* Type of regular trees:
- Var denotes tree variables (like de Bruijn indices)
the first int is the depth of the occurrence, and the second int
is the index in the array of trees introduced at that depth.
Warning: Var's indices both start at 0!
- Node denotes the usual tree node, labelled with 'a, to the
exception that it takes an array of arrays as argument
- Rec(j,v1..vn) introduces infinite tree. It denotes
v(j+1) with parameters 0..n-1 replaced by
Rec(0,v1..vn)..Rec(n-1,v1..vn) respectively.
*)type'at=Varofint*int|Nodeof'a*'atarrayarray|Recofint*'atarray(* Building trees *)letmk_rec_callsi=Array.initi(funj->Var(0,j))letmk_nodelabsons=Node(lab,sons)(* The usual lift operation *)letreclift_rtree_recdepthn=functionVar(i,j)ast->ifi<depththentelseVar(i+n,j)|Node(l,sons)->Node(l,Array.map(Array.map(lift_rtree_recdepthn))sons)|Rec(j,defs)->Rec(j,Array.map(lift_rtree_rec(depth+1)n)defs)letliftnt=ifInt.equaln0thentelselift_rtree_rec0ntletrecsubstmksub=function|Var(i,j)->beginmatchEsubst.expand_rel(i+1)subwith|Util.Inl(k,v)->mkkjv|Util.Inr(m,_)->Var(m-1,j)end|Node(l,sons)->Node(l,Array.map(Array.map(substmksub))sons)|Rec(j,defs)->Rec(j,Array.map(substmk(Esubst.subs_liftsub))defs)type'aclos=Closof'atarray*'aclosEsubst.substype'aexpansion=ExpVarofint*int|ExpNodeof'a*'aclosEsubst.subs*'atarrayarray(* To avoid looping, we must check that every body introduces a node
or a parameter *)letrecexpand0sub=function|Var(i,j)->beginmatchEsubst.expand_rel(i+1)subwith|Util.Inl(k,v)->letClos(v,sub)=vinexpand0(Esubst.subs_shft(k,sub))(Rec(j,v))|Util.Inr(m,_)->ExpVar(m-1,j)end|Rec(j,defs)->letsub=Esubst.subs_cons(Clos(defs,sub))subinexpand0subdefs.(j)|Node(l,sons)->ExpNode(l,sub,sons)letexpandt=matchexpand0(Esubst.subs_id0)twith|ExpVar(i,j)->Var(i,j)|ExpNode(l,sub,sons)->letrecmkkj(Clos(v,sub))=substmk(Esubst.subs_shft(k,sub))(Rec(j,v))inletmapt=substmksubtinletsons=Array.map(funv->Array.mapmapv)sonsinNode(l,sons)(* Given a vector of n bodies, builds the n mutual recursive trees.
Recursive calls are made with parameters (0,0) to (0,n-1). We check
the bodies actually build something by checking it is not
directly one of the parameters of depth 0. Some care is taken to
accept definitions like rec X=Y and Y=f(X,Y) *)letmk_recdefs=letreccheckhistod=matchexpanddwith|Var(0,j)->ifInt.Set.memjhistothenfailwith"invalid rec call"elsecheck(Int.Set.addjhisto)defs.(j)|_->()inArray.mapi(funid->check(Int.Set.singletoni)d;Rec(i,defs))defs(*
let v(i,j) = lift i (mk_rec_calls(j+1)).(j);;
let r = (mk_rec[|(mk_rec[|v(1,0)|]).(0)|]).(0);;
let r = mk_rec[|v(0,1);v(1,0)|];;
the last one should be accepted
*)(* Tree destructors, expanding loops when necessary *)letdest_vart=matchexpand0(Esubst.subs_id0)twith|ExpVar(i,j)->(i,j)|_->failwith"Rtree.dest_var"letdest_nodet=matchexpandtwithNode(l,sons)->(l,sons)|_->failwith"Rtree.dest_node"letdest_headt=matchexpand0(Esubst.subs_id0)twith|ExpVar_->failwith"Rtree.dest_head"|ExpNode(l,_,_)->lletis_nodet=matchexpandtwithNode_->true|_->falseletrecmapft=matchtwithVar(i,j)->Var(i,j)|Node(a,sons)->Node(fa,Array.map(Array.map(mapf))sons)|Rec(j,defs)->Rec(j,Array.map(mapf)defs)moduleSmart=structletmapft=matchtwithVar_->t|Node(a,sons)->leta'=faandsons'=Array.Smart.map(Array.Smart.map(mapf))sonsinifa'==a&&sons'==sonsthentelseNode(a',sons')|Rec(j,defs)->letdefs'=Array.Smart.map(mapf)defsinifdefs'==defsthentelseRec(j,defs')endmoduleKind=structtype'artree='attype'at={node:'artree;subs:'aclosEsubst.subs}letvarij=Var(i,j)letnodelsons=Node(l,sons)type'akind=Varofint*int|Nodeof'a*'atarrayarrayletmaket={node=t;subs=Esubst.subs_id0}letkindt:'akind=matchexpand0t.subst.nodewith|ExpVar(i,j)->Var(i,j)|ExpNode(l,subs,sons)->letmapnode={node;subs}inletsons=Array.map(funv->Array.mapmapv)sonsinNode(l,sons)letreprt=matchexpand0t.subst.nodewith|ExpVar(i,j)->varij|ExpNode(l,subs,sons)->letrecmkkj(Clos(v,sub))=substmk(Esubst.subs_shft(k,sub))(Rec(j,v))inletmapt=substmksubstinletsons=Array.map(funv->Array.mapmapv)sonsinnodelsonsend(** Structural equality test, parametrized by an equality on elements *)letrecraw_eqcmptt'=matcht,t'with|Var(i,j),Var(i',j')->Int.equalii'&&Int.equaljj'|Node(x,a),Node(x',a')->cmpxx'&&Array.equal(Array.equal(raw_eqcmp))aa'|Rec(i,a),Rec(i',a')->Int.equalii'&&Array.equal(raw_eqcmp)aa'|_->falseletraw_eq2cmp(t,u)(t',u')=raw_eqcmptt'&&raw_eqcmpuu'(** Equivalence test on expanded trees. It is parametrized by two
equalities on elements:
- [cmp] is used when checking for already seen trees
- [cmp'] is used when comparing node labels. *)letequivcmpcmp'=letreccomparehistott'=List.mem_f(raw_eq2cmp)(t,t')histo||matchexpandt,expandt'with|Node(x,v),Node(x',v')->cmp'xx'&&Int.equal(Array.lengthv)(Array.lengthv')&&Array.for_all2(Array.for_all2(compare((t,t')::histo)))vv'|_->falseincompare[](** The main comparison on rtree tries first physical equality, then
the structural one, then the logical equivalence *)letequalcmptt'=t==t'||raw_eqcmptt'||equivcmpcmptt'(** Intersection of rtrees of same arity *)letrecintercmpinterlbldefnhistott'=trylet(i,j)=List.assoc_f(raw_eq2cmp)(t,t')histoinVar(n-i-1,j)withNot_found->matcht,t'with|Var(i,j),Var(i',j')->assert(Int.equalii'&&Int.equaljj');t|Node(x,a),Node(x',a')->(matchinterlblxx'with|None->mk_nodedef[||]|Somex''->Node(x'',Array.map2(Array.map2(intercmpinterlbldefnhisto))aa'))|Rec(i,v),Rec(i',v')->(* If possible, we preserve the shape of input trees *)ifInt.equalii'&&Int.equal(Array.lengthv)(Array.lengthv')thenlethisto=((t,t'),(n,i))::histoinRec(i,Array.map2(intercmpinterlbldef(n+1)histo)vv')else(* Otherwise, mutually recursive trees are transformed into nested trees *)lethisto=((t,t'),(n,0))::histoinRec(0,[|intercmpinterlbldef(n+1)histo(expandt)(expandt')|])|Rec_,_->intercmpinterlbldefnhisto(expandt)t'|_,Rec_->intercmpinterlbldefnhistot(expandt')|_->assertfalseletintercmpinterlbldeftt'=intercmpinterlbldef0[]tt'(** Inclusion of rtrees. We may want a more efficient implementation. *)letinclcmpinterlbldeftt'=equalcmpt(intercmpinterlbldeftt')(** Tests if a given tree is infinite, i.e. has a branch of infinite length.
This corresponds to a cycle when visiting the expanded tree.
We use a specific comparison to detect already seen trees. *)letis_infinitecmpt=letrecis_infhistot=List.mem_f(raw_eqcmp)thisto||matchexpandtwith|Node(_,v)->Array.exists(Array.exists(is_inf(t::histo)))v|_->falseinis_inf[]t(* Pretty-print a tree (not so pretty) *)openPpletrecpr_treeprlt=matchtwith|Var(i,j)->str"#"++inti++str":"++intj|Node(lab,[||])->prllab|Node(lab,v)->hov0(prllab++str","++spc()++str"["++hv0(prvect_with_seppr_comma(funa->str"("++hv0(prvect_with_seppr_comma(pr_treeprl)a)++str")")v)++str"]")|Rec(i,v)->ifInt.equal(Array.lengthv)0thenstr"Rec{}"elseifInt.equal(Array.lengthv)1thenhv2(str"Rec{"++pr_treeprlv.(0)++str"}")elsehv2(str"Rec{"++inti++str","++brk(1,0)++prvect_with_seppr_comma(pr_treeprl)v++str"}")