123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509(* This file is free software, part of Zipperposition. See file "license" for more details. *)(** {1 Non-Perfect Discrimination Tree} *)moduleT=TermmoduleS=SubstmoduleTC=T.Classic(** {2 Term traversal} *)(** Term traversal in prefix order. This is akin to lazy transformation
to a flatterm. *)typeiterator={cur_term:T.t;stack:T.tlistlist;(* skip: drop head, next: first of head *)}leteq_ty=Type.close_forall@@Type.(==>)[Type.var_of_int0;Type.var_of_int0]Type.propleteq_id=ID.make"zip_eq_proxy"letneq_id=ID.make"zip_neq_proxy"leteq_const=T.const~ty:eq_tyeq_idletneq_const=T.const~ty:eq_tyneq_idlet[@inline]mk_eqab=T.appeq_const[T.of_ty(T.tya);a;b]let[@inline]mk_neqab=T.appneq_const[T.of_ty(T.tya);a;b]letneg_id=ID.make"zip_neg_proxy"letneg_const=letty=Type.(==>)[Type.prop]Type.propinT.const~tyneg_idlet[@inline]mk_negf=T.appneg_const[f]letrecopen_term~stackt=matchT.viewtwith|T.AppBuiltin(Eq,([a;b]|[_;a;b]))whenType.is_prop(T.tyt)->open_term~stack(mk_eqab)|T.AppBuiltin(Neq,([a;b]|[_;a;b]))whenType.is_prop(T.tyt)->open_term~stack(mk_neqab)|T.AppBuiltin(Not,[a])->open_term~stack(mk_nega)|T.Var_|T.DB_|T.AppBuiltin_|T.Fun_|T.Const_->Some{cur_term=t;stack=[]::stack;}|_whennot(Unif.Ty.type_is_unifiable(T.tyt))||Type.is_fun(T.tyt)->Some{cur_term=t;stack=[]::stack;}(* opaque constant/partial application *)|T.App(f,_)when(T.is_varf)->Some{cur_term=t;stack=[]::stack;}(* higher-order term *)|T.App(_,l)->Some{cur_term=t;stack=l::stack;}typeview_head=|As_star|As_appofID.t*T.tlistletview_head(t:T.t):view_head=ifnot(T.is_appt)||not(Unif.Ty.type_is_unifiable(T.tyt))||Type.is_fun(T.tyt)||T.is_ho_apptthenAs_starelse(lets,l=T.as_apptinbeginmatchT.viewswith|T.Constid->As_app(id,l)|_->As_starend)letrecnext_recstack=matchstackwith|[]->None|[]::stack'->next_recstack'|(t::next')::stack'->open_term~stack:(next'::stack')tletskipiter=matchiter.stackwith|[]->None|_next::stack'->next_recstack'letnextiter=next_reciter.stack(* Iterate on a term *)letiterateterm=open_term~stack:[]term(** {2 Unix index} *)moduleMake(E:Index.EQUATION)=structmoduleE=Etyperhs=E.rhsmoduleLeaf=Index.MakeLeaf(E)typet={star:toption;(* by variable *)map:tID.Map.t;(* by symbol *)leaf:Leaf.t;(* leaves *)}(* The discrimination tree *)letempty()={map=ID.Map.empty;star=None;leaf=Leaf.empty;}letis_emptyn=n.star=None&&ID.Map.is_emptyn.map&&Leaf.is_emptyn.leafexceptionNoSuchTrieletfind_submapkey=tryID.Map.findkeymapwithNot_found->raiseNoSuchTrie(** get/add/remove the leaf for the given term. The
continuation k takes the leaf, and returns a leaf option
that replaces the old leaf.
This function returns the new trie. *)letgoto_leaftrietk=(* the root of the tree *)letroot=triein(* function to go to the given leaf, building it if needed.
[iter] is an iterator on the current subterm *)letrecgototrieiterrebuild=matchiterwith|None->beginmatchktrie.leafwith|leaf'whenleaf'==trie.leaf->root(* no change, return same tree *)|leaf'->rebuild{triewithleaf=leaf';}end|Somei->matchview_headi.cur_termwith|As_star->letsubtrie=matchtrie.starwith|None->empty()|Sometrie'->trie'inletrebuildsubtrie=ifis_emptysubtriethenrebuild{triewithstar=None;}elserebuild{triewithstar=Somesubtrie;}ingotosubtrie(nexti)rebuild|As_app(s,_)->letsubtrie=tryfind_subtrie.mapswithNoSuchTrie->empty()inletrebuildsubtrie=ifis_emptysubtriethenrebuild{triewithmap=ID.Map.removestrie.map;}elserebuild{triewithmap=ID.Map.addssubtrietrie.map;}ingotosubtrie(nexti)rebuildingototrie(iteratet)(funt->t)letaddtrieeqn=lett,_,_=E.extracteqninletkleaf=Leaf.addleafteqningoto_leaftrietkletremovetrieeqn=lett,_,_=E.extracteqninletkleaf=Leaf.removeleafteqningoto_leaftrietkletadd_seqdtseq=Iter.foldadddtseqletadd_listdt=List.fold_leftadddtletremove_seqdtseq=Iter.foldremovedtseqletretrieve?(subst=S.empty)~signdttk=(* extended callback *)letk'(t',eqn,subst)=let_,r,sign'=E.extracteqninifsign=sign'thenk(t',r,eqn,subst)in(* recursive traversal of the trie, following paths compatible with t *)letrectraversetrieiter=matchiterwith|None->Leaf.fold_match~subst(Scoped.setdttrie.leaf)tk';|Somei->matchview_headi.cur_termwith|As_star->beginmatchtrie.starwith|None->()|Somesubtrie->traversesubtrie(nexti)(* match "*" against "*" *)end|As_app(s,_)->begintryletsubtrie=find_subtrie.mapsintraversesubtrie(nexti)withNoSuchTrie->()end;beginmatchtrie.starwith|None->()|Somesubtrie->traversesubtrie(skipi)(* skip subterm *)endintrytraverse(fstdt)(iterate(fstt));withe->raisee(** iterate on all (term -> value) in the tree *)letreciterdtk=Leaf.iterdt.leafk;beginmatchdt.starwith|None->()|Sometrie'->itertrie'kend;ID.Map.iter(fun_trie'->itertrie'k)dt.mapletsizedt=letn=ref0initerdt(fun__->incrn);!nlet_as_graph=CCGraph.make(funt->letprefixs=matcht.starwith|None->s|Somet'->Iter.cons("*",t')sands2=ID.Map.to_itert.map|>Iter.map(fun(sym,t')->ID.to_stringsym,t')inprefixs2)letto_dotoutt=letpp=CCGraph.Dot.pp~eq:(==)~tbl:(CCGraph.mk_table~eq:(==)~hash:Hashtbl.hash128)~attrs_v:(funt->letlen=Leaf.sizet.leafinletshape=iflen>0then"box"else"circle"in[`Shapeshape;`Label(string_of_intlen)])~attrs_e:(fune->[`Labele])~name:"NPDtree"~graph:_as_graphinFormat.fprintfout"@[<2>%a@]@."ppt;()end(** {2 General purpose index} *)moduleSIMap=Iter.Map.Make(structtypet=ID.t*intletcompare(s1,i1)(s2,i2)=ifi1=i2thenID.compares1s2elsei1-i2end)moduleMakeTerm(X:Set.OrderedType)=structmoduleLeaf=Index.MakeLeaf(X)typeelt=X.ttypet={star:toption;(* by variable *)map:tSIMap.t;(* by symbol+arity *)leaf:Leaf.t;(* leaves *)}(** The discrimination tree *)letempty()={map=SIMap.empty;star=None;leaf=Leaf.empty;}letis_emptyn=n.star=None&&SIMap.is_emptyn.map&&Leaf.is_emptyn.leafexceptionNoSuchTrieletfind_submapkey=trySIMap.findkeymapwithNot_found->raiseNoSuchTrie(** get/add/remove the leaf for the given term. The
continuation k takes the leaf, and returns a leaf option
that replaces the old leaf.
This function returns the new trie. *)letgoto_leaftrietk=(* the root of the tree *)letroot=triein(* function to go to the given leaf, building it if needed. *)letrecgototrieiterrebuild=matchiterwith|None->beginmatchktrie.leafwith|leaf'whenleaf'==trie.leaf->root(* no change, return same tree *)|leaf'->rebuild{triewithleaf=leaf';}end|Somei->matchview_headi.cur_termwith|As_star->letsubtrie=matchtrie.starwith|None->empty()|Sometrie'->trie'inletrebuildsubtrie=ifis_emptysubtriethenrebuild{triewithstar=None;}elserebuild{triewithstar=Somesubtrie;}ingotosubtrie(nexti)rebuild|As_app(s,l)->letarity=List.lengthlinletsubtrie=tryfind_subtrie.map(s,arity)withNoSuchTrie->empty()inletrebuildsubtrie=ifis_emptysubtriethenrebuild{triewithmap=SIMap.remove(s,arity)trie.map;}elserebuild{triewithmap=SIMap.add(s,arity)subtrietrie.map;}ingotosubtrie(nexti)rebuildingototrie(iteratet)(funt->t)letaddtrietdata=letkleaf=Leaf.addleaftdataingoto_leaftrietkletadd_trie=CCFun.uncurry(addtrie)letadd_seq=Iter.foldadd_letadd_list=List.fold_leftadd_letremovetrietdata=letkleaf=Leaf.removeleaftdataingoto_leaftrietkletupdate_leaftrietdata_filter=letkleaf=Leaf.update_leafleaftdata_filteringoto_leaftrietkletremove_trie=CCFun.uncurry(removetrie)letremove_seqdtseq=Iter.foldremove_dtseqletremove_listdtseq=List.fold_leftremove_dtseq(* skip one term in the tree. Calls [k] with [acc] on corresponding
subtries. *)letskip_treetriek=(* [n]: number of branches to skip (corresponding to subterms) *)letrecskiptrienk=ifn=0thenktrieelse(beginmatchtrie.starwith|None->()|Sometrie'->skiptrie'(n-1)kend;SIMap.iter(fun(_,arity)trie'->skiptrie'(n+arity-1)k)trie.map)inskiptrie1kletretrieve_unifiables_auxfold_unifydttk=(* recursive traversal of the trie, following paths compatible with t *)letrectraversetrieiter=matchiterwith|None->fold_unify(Scoped.setdttrie.leaf)tk;|Somei->matchview_headi.cur_termwith|As_star->(* skip one term in all branches of the trie *)skip_treetrie(funsubtrie->traversesubtrie(nexti))|As_app(s,l)->letarity=List.lengthlinbegintryletsubtrie=SIMap.find(s,arity)trie.mapintraversesubtrie(nexti)withNot_found->()end;beginmatchtrie.starwith|None->()|Somesubtrie->traversesubtrie(skipi)(* skip subterm of [t] *)endintrytraverse(fstdt)(iterate(fstt));withe->raiseeletretrieve_unifiables=retrieve_unifiables_aux(Leaf.fold_unify)letretrieve_unifiables_complete?(unif_alg=JP_unif.unify_scoped)=retrieve_unifiables_aux(Leaf.fold_unify_complete~unif_alg)letretrieve_generalizations?(subst=S.empty)dttk=(* recursive traversal of the trie, following paths compatible with t *)letrectraversetrieiter=matchiterwith|None->Leaf.fold_match~subst(Scoped.setdttrie.leaf)tk;|Somei->matchview_headi.cur_termwith|As_star->beginmatchtrie.starwith|None->()|Somesubtrie->traversesubtrie(nexti)(* match "*" against "*" only *)end|As_app(s,l)->letarity=List.lengthlinbegintryletsubtrie=SIMap.find(s,arity)trie.mapintraversesubtrie(nexti)withNot_found->()end;beginmatchtrie.starwith|None->()|Somesubtrie->traversesubtrie(skipi)(* skip subterm *)endintrytraverse(fstdt)(iterate(fstt));withe->raiseeletretrieve_specializations?(subst=S.empty)dttk=(* recursive traversal of the trie, following paths compatible with t *)letrectraversetrieiter=matchiterwith|None->Leaf.fold_matched~subst(Scoped.setdttrie.leaf)tk;|Somei->matchview_headi.cur_termwith|As_star->(* match * against any subterm *)skip_treetrie(funsubtrie->traversesubtrie(nexti))|As_app(s,l)->(* only same symbol *)letarity=List.lengthlinbegintryletsubtrie=SIMap.find(s,arity)trie.mapintraversesubtrie(nexti)withNot_found->()endintrytraverse(fstdt)(iterate(fstt));withe->raisee(** iterate on all (term -> value) in the tree *)letreciterdtk=Leaf.iterdt.leafk;beginmatchdt.starwith|None->()|Sometrie'->itertrie'kend;SIMap.iter(fun_trie'->itertrie'k)dt.mapletrecfolddtkacc=letacc=Leaf.folddt.leafacckinletacc=matchdt.starwith|None->acc|Sometrie'->foldtrie'kaccinSIMap.fold(fun_trie'acc->foldtrie'kacc)dt.mapaccletsizedt=letn=ref0initerdt(fun__->incrn);!nletname="npdtree"let_as_graph=CCGraph.make(funt->letprefixs=matcht.starwith|None->s|Somet'->Iter.cons("*",t')sands2=SIMap.to_itert.map|>Iter.map(fun((sym,i),t')->letlabel=CCFormat.sprintf"%a/%d"ID.ppsymiinlabel,t')inprefixs2)(* TODO: print leaf itself *)letto_dot_outt=Util.debugf2"@[<2>print graph of size %d@]"(funk->k(sizet));letpp=CCGraph.Dot.pp~eq:(==)~tbl:(CCGraph.mk_table~eq:(==)~hash:Hashtbl.hash128)~attrs_v:(funt->letlen=Leaf.sizet.leafinletshape=iflen>0then"box"else"circle"in[`Shapeshape;`Label(string_of_intlen)])~attrs_e:(fune->[`Labele])~name:"NPDtree"~graph:_as_graphinFormat.fprintfout"@[<2>%a@]@."ppt;()end