123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810openUtilsLibopenLambdamoduleLog=Xlog.Make(structletname="Expand"end)(** The {!Address} module implements addresses as used in Makoto's
paper *)moduleAddress=structtypeelt=Zero|Oneletmake_add=List.map(function|0->Zero|1->One|i->raise(Invalid_argument(Printf.sprintf"%d is not part of a valid binary tree domain."i)))letelt_cmpe1e2=matche1,e2with|Zero,Zero->0|Zero,One->-1|One,Zero->1|One,One->0(** [elt_pp fmt elt] prettyp prints the element [elt] on the
formatter [fmt] *)letelt_ppfmt=function|Zero->Format.pp_print_intfmt0|One->Format.pp_print_intfmt1(** [t] is the type of addresses *)typet=eltlist(** This exception is used on some operation involving an address
[a] and some address [u] where it is assumed that [u] is a
prefix of [a]. *)exceptionNot_prefix(** [compare] is a lexical order on addresses *)letcompare=List.compareelt_cmp(** [is_prefix ~strict u v] returns [true] if {ul
{- [u] is a prefix of [v]}
{- and [u≠v] if [strict] is set to [true]}, and [false]
otherwise.*)letrecis_prefix~stricta1a2=matcha1,a2with|[],[]whenstrict->false|[],[]->true|[],_::_->true|_::_,[]->false|a::tl1,b::tl2whena=b->is_prefix~stricttl1tl2|_->false(** [extend add elt] extends the address [add] with the symbol
[elt] added at the end.*)letrecextendli=matchlwith|[]->[i]|a::tl->a::(extendtli)letconcata1a2=a1@a2(** [remove ~prefix w] returns [v] if [w=prefix v] and raises {!
Not_prefix} otherwise. *)letrecremove~prefixl=matchprefix,lwith|[],_->l|a::prefix',b::tlwhena=b->remove~prefix:prefix'tl|_,_->raiseNot_prefix(** [pp fnt add] pretty prints the address [add] on the formatter
[fmt] *)letpp=PPUtils.pp_list~sep:""elt_ppend(** The {!AddMap} module implements a trie data structure to represent
maps from {!Address.t} addresses to values of type ['a]*)moduleAddMap=struct(** ['a t] is the type of sets of addresses *)type'at=Mof('aoption*'atoption*'atoption)(** [empty] is the empty set of addresses *)letempty=M(None,None,None)(** [mem add m] returns [true] if [add] binds a value map [m] and
[false] otherwise. *)letrecmemaddm=matchadd,mwith|[],M(None,_,_)->false|[],M(Some_,_,_)->true|Address.Zero::_,M(_,None,_)->false|Address.Zero::tl,M(_,Someleft,_)->memtlleft|Address.One::_,M(_,_,None)->false|Address.One::tl,M(_,_,Someright)->memtlright(** [mem add m] returns [true] if [add] binds a value map [m] and
[false] otherwise. *)letrecfind(add:Address.t)(m:'at):'a=matchadd,mwith|[],M(None,_,_)->raiseNot_found|[],M(Somev,_,_)->v|Address.Zero::_,M(_,None,_)->raiseNot_found|Address.Zero::tl,M(_,Someleft,_)->findtlleft|Address.One::_,M(_,_,None)->raiseNot_found|Address.One::tl,M(_,_,Someright)->findtlright(** [add a v m] adds at the address [a] the value [v] into the map
[m], possibly changing the binding if [a] was already binding a
value in [m].*)letrecaddavm=matcha,mwith|[],M(_,l,r)->M(Somev,l,r)|Address.Zero::tl,M(b,None,r)->M(b,Some(addtlvempty),r)|Address.Zero::tl,M(b,Somel,r)->M(b,Some(addtlvl),r)|Address.One::tl,M(b,l,None)->M(b,l,Some(addtlvempty))|Address.One::tl,M(b,l,Somer)->M(b,l,Some(addtlvr))letreciter_auxfacc(M(b,_,_)ass)=let()=matchbwith|None->()|Somev->faccvinmatchswith|M(_,None,None)->()|M(_,Somel,None)->iter_auxf(Address.extendaccAddress.Zero)l|M(_,None,Somer)->iter_auxf(Address.extendaccAddress.One)r|M(_,Somel,Somer)->let()=iter_auxf(Address.extendaccAddress.Zero)liniter_auxf(Address.extendaccAddress.One)r(** [iter f m] iterates the function [f] on every [(k, v)] binding of
[m].*)letiterfs=iter_auxf[]sletto_lstm=letrecto_lst_auxaccadd(M(b,_,_)ass)=letacc'=matchbwith|None->acc|Somev->(add,v)::accinmatchswith|M(_,None,None)->acc'|M(_,Somel,None)->to_lst_auxacc'(Address.extendaddAddress.Zero)l|M(_,None,Somer)->to_lst_auxacc'(Address.extendaddAddress.One)r|M(_,Somel,Somer)->letacc''=to_lst_auxacc'(Address.extendaddAddress.Zero)linto_lst_auxacc''(Address.extendaddAddress.One)rinto_lst_aux[][]mendmoduleAddSet=structtypet=unitAddMap.tletempty=AddMap.emptyletmem=AddMap.memletaddelts=AddMap.addelt()sletiterfs=AddMap.iter(funa()->fa)send(** [min ~cmp lst] returns [elt,lst'] where [elt] is the minimal
element of [lst] according to the comparison function [cmp] (even
if it occurs several times), and [lst'] is [lst] with this
occurrence of [elt] removed .*)letmin~cmpl=letrecmin_auxminaccl=matchmin,lwith|_,[]->min,acc|None,a::tl->min_aux(Somea)acctl|Somev,a::tlwhen(cmpav)<0->min_aux(Somea)(v::acc)tl|_,a::tl->min_auxmin(a::acc)tlinmin_auxNone[]lletextend_map_to_lst=Utils.IntMap.add_to_list(*module Domain = Map.Make (Address)*)moduleDomain=AddMap(** This type records for linear and non linear variables at address
[a] the address of the binder that binds this variable. Therefore,
[Address.is_prefix ~strict:true k v] should always return true
whenever [k] maps to [v] in [nl_binders] or in [l_binders]. *)typebinders={nl_binders:Address.tDomain.t;l_binders:Address.tDomain.t;}(** This module implements a map from the de Bruijn indices of a
variable the address of its binder (recorded when the
corresponding binder was crossed).*)moduleBAdd=Lambda.MakeVarEnv(structtypeinfo=Address.tletpp=Address.ppend)(** A record type to keep track of maps from de Bruijn indices of
variables to addresses of their binders *)typeaddresses={nl_add:BAdd.t;l_add:BAdd.t;}(** When representing lambda-terms as trees with 0-ary (variables or
constants), 1-ary (abstraction), or 2-ary nodes, it's possible to
have a dedicated zipper type. The node information is basically
empty, and what is relevant is the local context, i.e., what is
right above a term (if it's the child of an abstraction), what is
on its right (if it's the functor of an application, [u] in [Rapp
u] being its argument), what is on its left (if it's the argument
of an application, [u] in [Lapp u] being the functor). *)typeterm_local_context=|LAbsofstring|Absofstring|LAppofLambda.term|RAppofLambda.termtypeterm_zipper=|ZTop|TZipof(term_zipper*term_local_context)(** [zip_up (z, u)] returns the term [t] such that [u] is its subterm
in context [z]. *)letreczip_up(z,t)=matchzwith|ZTop->t|TZip(z',LAbsx)->zip_up(z',Lambda.LAbs(x,t))|TZip(z',Absx)->zip_up(z',Lambda.Abs(x,t))|TZip(z',LAppu)->zip_up(z',Lambda.App(u,t))|TZip(z',RAppu)->zip_up(z',Lambda.App(t,u))typeterm_sig={add:Address.t;l_env:Lambda.env;nl_env:Lambda.env;duplicable:bool;ctx:term_zipper;term:Lambda.term;}(** [subterms_aux (levels, addresses, subtrees, binders) (t, add)]
returns a tuple [(height, consts), (subtrees', binders')] where
[height] is the height of the term [t], [consts] is the
lefto-to-right sequence of (indices of) constants it is made of,
[subtrees'] is [subtrees], a map from (the opposite of) their
heighth to terms (in order to fold over this map starting with the
highest subtrees), augmented with the subterms of [t], whose
address is [add], and [binders'] is the previous binders map (for
the term [t] is a subterm of) together with the binder map defined
by [t]. And [addresses] is a map from the level at which a binder
is introduced to its address with the tree. *)(* We assume that [t] is in eta-long form *)letrecsubterms_aux(addresses,(l_nvenv,nvenv),(subtrees:(term_siglist)Utils.IntMap.t),binders)(t,add,context)=matcht,contextwith|Lambda.Vari,_->0,(extend_map_to_lst0{add;l_env=l_nvenv;nl_env=nvenv;duplicable=false;(* variables can't be dupicated pivot *)ctx=context;term=t;}subtrees,{binderswithnl_binders=Domain.addadd(BAdd.getiaddresses.nl_add)binders.nl_binders})|Lambda.LVari,_->0,(extend_map_to_lst0{add;l_env=l_nvenv;nl_env=nvenv;duplicable=false;(* variables can't be dupicated pivot *)ctx=context;term=t;}subtrees,{binderswithl_binders=Domain.addadd(BAdd.getiaddresses.l_add)binders.l_binders})|Lambda.Const_,ZTop->0,(extend_map_to_lst0{add;l_env=l_nvenv;nl_env=nvenv;duplicable=true;ctx=ZTop;term=t;}subtrees,binders)|Lambda.Const_,TZip(_,LApp_)->(* the constant is the right child of an applicitation,
therefore, it has an atomic type (otherwise there would be a
lambda since the tern is in eta-long form *)0,(extend_map_to_lst0{add;l_env=l_nvenv;nl_env=nvenv;duplicable=true;ctx=context;term=t;}subtrees,binders)|Lambda.Const_,TZip(_,RApp_)->(* the constant is the left child of an applicitation, therefore, it has not an atomic type *)0,(extend_map_to_lst0{add;l_env=l_nvenv;nl_env=nvenv;duplicable=false;ctx=context;term=t;}subtrees,binders)|Lambda.DConst_,_->failwith"Bug: should not occur. It is expected \
constant definition expansion has \
already been performed at this stage"|Lambda.Abs(x,u),_->leth,(n_subtrees,n_binders)=subterms_aux({addresseswithnl_add=BAdd.addaddaddresses.nl_add},(l_nvenv,Lambda.VNEnv.addxnvenv),subtrees,binders)(u,Address.(extendaddZero),TZip(context,Absx))inh+1,(n_subtrees,n_binders)|Lambda.LAbs(x,u),_->leth,(n_subtrees,n_binders)=subterms_aux({addresseswithl_add=BAdd.addaddaddresses.l_add},(Lambda.VNEnv.addxl_nvenv,nvenv),subtrees,binders)(u,Address.(extendaddZero),TZip(context,LAbsx))inh+1,(n_subtrees,n_binders)|Lambda.App(u,v),_->leth_u,(n_subt,n_bs)=subterms_aux(addresses,(l_nvenv,nvenv),subtrees,binders)(u,Address.(extendaddZero),TZip(context,RAppv))inleth_v,(n_subtrees,n_binders)=subterms_aux(addresses,(l_nvenv,nvenv),n_subt,n_bs)(v,Address.(extendaddOne),TZip(context,LAppu))inletn_h=maxh_uh_vinletnn_subtrees=matchcontextwith|ZTop|TZip(_,(LApp_|Abs_|LAbs_))->extend_map_to_lst(-(n_h+1)){add;l_env=l_nvenv;nl_env=nvenv;duplicable=true;ctx=context;term=t}n_subtrees|TZip(_,RApp_)->extend_map_to_lst(-(n_h+1)){add;l_env=l_nvenv;nl_env=nvenv;duplicable=false;ctx=context;term=t}n_subtreesinn_h+1,(nn_subtrees,n_binders)|_,_->failwith"Not implemented"letsubtermst=let_,(subt,binders)=subterms_aux({nl_add=BAdd.empty;l_add=BAdd.empty;},Lambda.VNEnv.(empty,empty),Utils.IntMap.empty,{l_binders=Domain.empty;nl_binders=Domain.empty;})(t,[],ZTop)insubt,binders(** [congruent_aux ((w, w') binders add t1 t2] returns
[true] if the two terms [t1] (at address [w add]) and [t2] (at
address [w' add] are congruent *)letreccongruent_aux(w,w')bindersaddt1t2=matcht1,t2,binders.l_binders,binders.nl_binderswith|Lambda.Consti,Lambda.Constj,_,_->i=j|Lambda.Var_,Lambda.Var_,_,b_addresses|Lambda.LVar_,Lambda.LVar_,b_addresses,_->letfull_add1=Address.concatwaddinletfull_add2=Address.concatw'addinletbinder1=Domain.findfull_add1b_addressesinletbinder2=Domain.findfull_add2b_addressesinifbinder1=binder2thentrueelsebegin(* if the address of the two binders are different,
[b(t1)=wu], [b(t2)=w'u], each binders should have the same
address below [w] and [w'], resp., i.e., [b(t2)=w'u'] and
[u=u']. *)(* First check that both binder addresses are below [w] and
[w'], resp., i.e., [w] and [w'] are prefixes of [binder1]
and [binder2], resp. *)tryletu=Address.remove~prefix:wbinder1inletu'=Address.remove~prefix:w'binder2in(* We first check that [u] is a prefix of [add], i.e., that
[t1] is indeed in the scope of the binder that binds
it *)matchAddress.remove~prefix:uaddwith|[]->false|_->u=u'|exceptionAddress.Not_prefix->falsewith|Address.Not_prefix->false(* either [w] is not prefix of [binder1] or [w'] is not
prefix of [binder2] *)end|Lambda.Abs(_,t1'),Lambda.Abs(_,t2'),_,_|Lambda.LAbs(_,t1'),Lambda.LAbs(_,t2'),_,_->congruent_aux(w,w')bindersAddress.(extendaddZero)t1't2'|Lambda.App(u1,v1),Lambda.App(u2,v2),_,_->letres=congruent_aux(w,w')bindersAddress.(extendaddZero)u1u2inifresthencongruent_aux(w,w')bindersAddress.(extendaddOne)v1v2elsefalse|_->falseletcongruent~constsbinderssgsg'=if(notsg.duplicable)||(notsg'.duplicable)thenfalseelselet()=Log.debug(funm->m"Checking congruency of:@[<v>@,@[%a@]@,and@,@[%a@]@]"(Lambda.pp_term~env:(sg.l_env,sg.nl_env)consts)sg.term(Lambda.pp_term~env:(sg'.l_env,sg'.nl_env)consts)sg'.term)incongruent_aux(sg.add,sg'.add)binders[]sg.termsg'.term(** [partition ~skip ~pred l] returns [l_partition]
such that:
{ul
{- [l_partition] is a list [[l1; l2; .. ; ln]] of list of
elements such that for any [e1∈li] and [e2∈lj], [pred e1 e2] is
true if and only if [i=j]}
{- all elements of [l] {e but the ones for which [skip] returns
true} are in an element of [l_partition]} } } *)letpartition~skip~predl=letrecpartition_aux~skip~predl_partitionl=(* [partition_aux ~skip ~pred l_partition l] returns [l_partition']
such that:
{ul
{- [l_partition'] is a list [[l1; l2; .. ; ln]] of list of
elements such that for any [e1∈li] and [e2∈lj], [pred e1 e2] is
true if and only if [i=j]}
{- all elements of [l] {e but the ones for which [skip] returns
true} are in an element of [l_partition']}
{- [l_partition'] extends [l_partition = [l1 ; .. ; lk]] (which is
assumed to be a correct partition as well), i.e., for every
[1≤i≤k], there exists [1≤j≤k] such that [li⊂ lj].
} }
*)matchlwith|[]->l_partition|a::tlwhenskipa->partition_aux~skip~predl_partitiontl|a::tl->(* We need to check if [a] could got into an existing subset of
[l_partition] or if we need to create a new party for it *)letpartition',found=List.fold_left(fun(c_partition,found)part->iffoundthen(* we already found the part in [l_partition] [a]
belongs to. Hence we just add the current part to
partion *)part::c_partition,foundelsematchpartwith|[]->c_partition,found(* Such a case should not really
occur, but it's safe anyway *)|b::_whenpredab->(a::part)::c_partition,true(* [a] is in the [pred] relation with [b], it therefore
belongs to the current part to which it is added, and the
whole part is added to [c_partition]. *)|_->part::c_partition,found)(* otherwise we just keep the part [part] unchanged and add
it to [c_partition]. *)([],false)l_partitioninletpartition''=iffoundthenpartition'else[a]::partition'in(partition_aux~skip~predpartition''tl)in(* we only keep parts of the partition that have at least 2
elements *)List.filter(funelt->matcheltwith|_::_::_->true|_->false)(partition_aux~skip~pred[]l)(** This exception is raised when a solution is found while scanning a
map, in order to avoid scanning all the map *)exceptionStopof(int*(term_sig*(term_siglist)))letsubterms_ppconstsfmtsubterms=letdup_ppfmtb=ifbthenTags.red_ppfmt"(duplicable)"elseTags.blue_ppfmt"(not duplicable)"inList.iter(fun{l_env;nl_env;duplicable;add;term=t;_}->Format.fprintffmt"@[%a@ %a@] at address: %a@,"(Lambda.pp_term~env:(l_env,nl_env)consts)tdup_ppduplicableAddress.ppadd)subterms[@@alert"-deprecated"][@@warning"-unused-value-declaration"](** [is_common_prefix ~strict a lst] returns [true] if [a] is a prefix
(resp. strict prefix) of all the address fields of the elements of
[lst]. *)letis_common_prefix~strictadlst=letrecis_common_prefix_auxaccad=function|[]->acc|a::tlwhenAddress.is_prefix~strictada.add->is_common_prefix_aux(true&&acc)adtl|_->falseinis_common_prefix_auxtrueadlst(** [expand subterms level addresses (add, t)] compute the expanded
subterm of [t] at address [add] and current non-linear abstraction
level [level] as if a new [Abs] binder has been added at the [0]
level that would bien all variables whose addresses are in
[addresses]. Only (non-linear) variables are affected:
{ul
{- if [i < level], then [i] is unchanged}
{- if [i ≥ level], then [i] is replaced by [i + 1]}
{- if the current address [add] is in [addresses], then a new
variable indexed by [level] is introduced.}
}
*)letexpand_subtermaddresses(add,t)=letrecexpand_subterm_auxlevel(add,t)=ifAddSet.memaddaddressesthenLambda.Varlevelelsematchtwith|Lambda.LVar_->t|Lambda.Const_->t|Lambda.DConst_->failwith"Bug: the term should be fully expanded"|Lambda.LAbs(x,u)->Lambda.LAbs(x,expand_subterm_auxlevel(Address.(extendaddZero),u))|Lambda.Abs(x,u)->Lambda.Abs(x,expand_subterm_aux(level+1)(Address.(extendaddZero),u))|Lambda.App(u,v)->Lambda.App(expand_subterm_auxlevel(Address.(extendaddZero),u),expand_subterm_auxlevel(Address.(extendaddOne),v))|Lambda.Variwheni<level->Lambda.Vari|Lambda.Vari->Lambda.Var(i+1)|_->failwith"Not implemented"inexpand_subterm_aux0(add,t)(** [collapse ~consts t] returns [None] if [t] is unchanged through
the collapse algorithm (i.e., no subterm of atomic type occurs at
least twice in [t]), and [Some u] where [u] is the results of the
(recursive) collapse algorithm.
{b It is expected that [t] does not contain unexpanded defined
constants.}
If [consts] is provided, the mapping from constant ids to strings
(in some signature) is used to pretty prints terms if {!Log} log
level is set to some adequate level. Otherwise, each constant is
printed as [Const[i]].
*)letcollapse?(consts=funi->Abstract_syntax.Abstract_syntax.Default,Printf.sprintf"Const(%d)"i)t=letreccollapse_aux~rest=let()=Log.debug(funm->m"Starting collapse of @[%a@]"(Lambda.pp_termconsts)t)in(* We first get all the subterms and all the maps from variable
addresses to the address of their binders.
The subterms are in a map from (the opposite of) the height to
the list of subterms of that height (so that when scanning, it
starts from highest subterms unti one is found *)letheigth_to_subterms,binders=subtermstinlet()=Log.debug(funm->letpp_mapfmtmap=Utils.IntMap.iter(funklst->Format.fprintffmt"Listing the subterms of height %d@[<v2>@,%a@]@,"(-k)(subterms_ppconsts)lst)mapinm"The subterms are:@ @[<v2> @[<v>%a@]@]"pp_mapheigth_to_subterms)inlet()=Log.debug(funm->m"@[<v>@[Linear bindings:@[<v>@,@[<v>%a@]@,@]@,@]@[Non-linear bindings:@[<v>@,@[<v>%a@]@]@]@]"(PPUtils.pp_list~sep:"@,"~terminal:"@,"(funfmt(add1,add2)->Format.fprintffmt"%a ----> %a"Address.ppadd1Address.ppadd2))(AddMap.to_lstbinders.l_binders)(PPUtils.pp_list~sep:"@,"~terminal:"@,"(funfmt(add1,add2)->Format.fprintffmt"%a ----> %a"Address.ppadd1Address.ppadd2))(AddMap.to_lstbinders.nl_binders))intrybeginletpart=(* We go through all set of subterms of a given height,
starting from the greatest one *)Utils.IntMap.fold(funhsubtsintermed_res->(* for a given set of subterms of height h, we find the
parition according to the [congruent binders]
predicate. We [skip] the elements that are marked as
non-duplicable. *)letl_partitions=partition~skip:(fun{duplicable;_}->notduplicable)~pred:(funab->congruent~constsbindersab)subtsinmatchl_partitionswith|[]->(* No part with more than 2 duplicables elements were
found, we just look for a solution at the nect
height *)intermed_res|_->(* There is a part with two elements *)let()=Log.debug(funm->List.iter(funpart->m"I found the following duplicated pivots of height %d: @[<v2>@,%a@]"(-h)(subterms_ppconsts)part)(List.sort(funl1l2->List.compare_lengthsl2l1)l_partitions))in(* We need to find, for each part in the partition,
its leftmost element, i.e., the one with the
smallest (in lexicographic order) address *)letcmp{add=a1;_}{add=a2;_}=Address.comparea1a2inletl_partitions'=List.map(funpart->(* for a given partition, first find the
leftmost element *)matchmin~cmppartwith|None,_->failwith"Bug: should not occurr, \
partitions should not be \
empty"|Somev,p->v,p)l_partitionsin(* then find the part with leftmost element, based on
the leftmost element of each part of the
partition *)matchmin~cmp:(fun(m1,_)(m2,_)->cmpm1m2)l_partitions'with|None,_->failwith"Bug: Should not occurr"|Someres,_->raise(Stop(h,res)))heigth_to_subtermsNoneinmatchpart,reswith|None,None->None(* No duplicated pivot, no intermediary result *)|None,Some_->res(* No duplicated pivot, some
intermediary result. Returns
the latter, there is no need
to continue *)|Some_,_->failwith"Bug: should not happen"endwith(* An exception was raised, i.e., some part with at least two duplicated pivots was found. *)|Stop(p_h,(({add;l_env;nl_env;term=leftmost_pivot;_}asdup_p),partition))->let()=Log.debug(funm->m"Found@ leftmost@ duplicated@ pivot@ at@ address: @[%a@]: %a"Address.ppadd(Lambda.pp_term~env:(l_env,nl_env)consts)leftmost_pivot)inlet()=Log.debug(funm->m"Other congruent terms are at addresses:@[<v>@,%a@]"(PPUtils.pp_list~sep:"@,"(funfmt{add;_}->Address.ppfmtadd))partition)in(* Collects all the addresses of the duplicated pivots,
including the leftmost one *)letdup_pivot_addresses=List.fold_left(funacc{add=l_add;_}->AddSet.addl_addacc)(AddSet.empty|>AddSet.addadd)partitionin(* Find the pivot (not necessarily duplicated) of minimal
height that is above all the slected duplicated pivots,
i.e., whose address is a prefix of all the addresses of the
duplicated pivots *)letmin_pivot=Utils.IntMap.fold(funhterm_listacc->ifh>=p_hthen(* [-h ≤ -p_h] i.e., we are currently going through
subterms that have a heigth less than the one of the
duplicated pivot, so we can just skip them *)accelse(* We check if we can find a pivot whose address is a
prefix of the addresses of all the duplicated
pivots. Finding one is enough.*)matchList.fold_left(funl_intermed_rest_sig->matchl_intermed_reswith|None->ifis_common_prefix~strict:truet_sig.add(dup_p::partition)thenSomet_sigelsel_intermed_res|Some_->l_intermed_res)Noneterm_listwith|None->acc|Someres->Some(h,res))heigth_to_subtermsNoneinmatchmin_pivotwith|None->(* If we reach this part, there are duplicated pivots, hence
there should be a pivot of minimal height ancestor of all
of them, so this case should not happen. *)failwith"Bug: I didn't find a minimal height pivot from which to expand"|Some(h,t_sig)->let()=Log.debug(funm->m"I found a minimal height pivot of height %d at address: %a"(-h)Address.ppt_sig.add)inlet()=Log.debug(funm->m"The minimal height pivot is: @[%a@]"(Lambda.pp_term~env:(t_sig.l_env,t_sig.nl_env)consts)t_sig.term)inletexpanded_subt=expand_subtermdup_pivot_addresses(t_sig.add,t_sig.term)inlet()=Log.debug(funm->m"Raw minimal height pivot: %s"(Lambda.raw_to_stringt_sig.term))inlet()=Log.debug(funm->m"Expanded minimal height pivot: %s"(Lambda.raw_to_stringexpanded_subt))inletexpanded_t=Lambda.(App(Abs("Z",expanded_subt),leftmost_pivot))inletresult=zip_up(t_sig.ctx,expanded_t)inlet()=Log.debug(funm->letl_ref=Lambda.VNEnv.current_levelt_sig.nl_envinletnl_env=Lambda.VNEnv.shift~info:"Z"~level:(l_ref-1)t_sig.nl_envinm"Its expanded version is: @[%a@]"(Lambda.pp_term~env:(t_sig.l_env,nl_env)consts)expanded_t)inlet()=Log.debug(funm->m"The whole term is: @[%a@]"(Lambda.pp_termconsts)result)incollapse_aux~res:(Someresult)(result)inletres=collapse_aux~res:Nonetinmatchreswith|None->let()=Log.debug(funm->m"I didn't find a collapsed term")inres|Somet->let()=Log.debug(funm->m"I found the collapsed term @[%a@]"(Lambda.pp_termconsts)t)inres