123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335openBwdopenBwd.Infixtypeseg=stringtypepath=seglisttypebwd_path=segbwdmoduleSegMap=Map.Make(String)type'adata_node={root:'aoption;children:'adata_nodeSegMap.t;}type'atag_node={tag_root:'aoption;tag_default_child:'aoption;tag_children:'atag_nodeSegMap.t}type('data,'tag)node='datadata_node*'tagtag_node(*
Invariants:
1. t.tag_children must be a subset of d.children
2. t.tag_root must be used by d.root
3. t.tag_default_child must be used by some d.children
Non-invariants:
1. The tag trie need not be minimum.
2. This module prefers removing tag_default_child.
*)type('data,'tag)t=('data,'tag)nodeoptionletempty:_t=Noneletis_empty:_t->bool=Option.is_nonelet[@inline]non_empty(n:_node):_t=Somen(** {1 Making (non-empty) trees} *)(* invariants: the input tag_children must already be a subset of d.children (invariant 1) *)letmk_tag_noded(tag_root,(tag_default_child,tag_children)):_tag_node=lettag_root=matchd.rootwithNone->None|_->tag_rootinlettag_default_child=ifInt.equal(SegMap.cardinald.children)(SegMap.cardinaltag_children)thenNoneelsetag_default_childin{tag_root;tag_default_child;tag_children}letmk_tag_node'dt:_tag_node=mk_tag_noded(t,(t,SegMap.empty))letmk_nodedtag_params:_node=d,mk_tag_nodedtag_paramsletmk_node'dtag:_node=d,mk_tag_node'dtag(* invariants: the input is already valid *)letdrop_tag_default_child(d,t)=ift.tag_default_child=Nonethen(d,t)elselettag_children=SegMap.merge(fun_childtag_child->matchchild,tag_childwith|None,_->assertfalse|Somed,None->Some(mk_tag_node'dt.tag_default_child)|Some_,Somet->Somet)d.childrent.tag_childrenind,{twithtag_default_child=None;tag_children}(* invariants: input tag tree must be a subset (if default tags were ignored) *)letmk_tree(root,children)tag_params:_t=ifOption.is_noneroot&&SegMap.is_emptychildrenthenemptyelsenon_empty@@mk_node{root;children}tag_paramslet[@inline]root_node(data,tag)={root=Somedata;children=SegMap.empty},{tag_root=Sometag;tag_default_child=None;tag_children=SegMap.empty}let[@inline]root_optv=Option.maproot_nodevlet[@inline]rootv=non_empty@@root_nodevlet[@inline]prefix_nodepathn:_node=letfseg(d,t)={root=None;children=SegMap.singletonsegd},{tag_root=None;tag_default_child=None;tag_children=SegMap.singletonsegt}inList.fold_rightfpathnlet[@inline]prefixpath=Option.map@@prefix_nodepathlet[@inline]singleton(path,(d,t))=prefixpath(root(d,t))(** {1 Equality} *)letget_children_node(d,t)=SegMap.merge(fun_d't'->matchd',t'with|None,_->assertfalse|Somed,None->Some(d,mk_tag_node'dt.tag_default_child)|Somed,Somet->Some(d,t))d.childrent.tag_childrenletget_children_node2(d,t1,t2)=SegMap.merge(fun_d_t1t2'->matchd_t1,t2'with|None,_->assertfalse|Some(d,t1),None->Some(d,t1,mk_tag_node'dt2.tag_default_child)|Some(d,t1),Somet2->Some(d,t1,t2))(get_children_node(d,t1))t2.tag_childrenletsplit_childrencombined=SegMap.mapfstcombined,SegMap.mapsndcombinedletsplit_option=functionNone->None,None|Some(d,t)->Somed,Sometletrecequal_tag_nodeeq_tag(d,t1,t2)=t1==t2||Option.equaleq_tagt1.tag_roott2.tag_root&&equal_tag_childreneq_tag(d,t1,t2)andequal_tag_childreneq_tag(d,t1,t2)=(Option.equaleq_tagt1.tag_default_childt2.tag_default_child&&SegMap.is_emptyt1.tag_children&&SegMap.is_emptyt2.tag_children)||SegMap.for_all(fun_->equal_tag_nodeeq_tag)(get_children_node2(d,t1,t2))letrecequal_data_nodeeqn1n2=n1==n2||Option.equaleqn1.rootn2.root&&SegMap.equal(equal_data_nodeeq)n1.childrenn2.childrenletequal_nodeeq_dataeq_tag(d1,t1)(d2,t2)=(d1==d2||equal_data_nodeeq_datad1d2)&&(t1==t2||equal_tag_nodeeq_tag(d1,t1,t2))letequaleq_dataeq_tag=Option.equal(equal_nodeeq_dataeq_tag)(** {1 Getting data} *)letfind_child_nodeseg(d,t):_nodeoption=matchSegMap.find_optsegd.childrenwith|None->None|Somed->matchSegMap.find_optsegt.tag_childrenwith|Somet->Some(d,t)|None->Some(mk_node'dt.tag_default_child)letrecfind_node_contpathnk=matchpathwith|[]->kn|seg::path->Option.bind(find_child_nodesegn)@@funn->find_node_contpathnkletfind_subtreepathv=Option.bindv@@funn->find_node_contpathnnon_emptyletfind_root_node(d,t)=matchd.rootwith|None->None|Somer->Some(r,Option.gett.tag_root)letfind_singletonpathv=Option.bindv@@funn->find_node_contpathnfind_root_nodeletfind_rootv=Option.bindvfind_root_node(** {1 Updating} *)letrecupdate_node_contpath(d,t)(k:(_,'tag)t->(_,'tag)t)=matchpathwith|[]->k@@non_empty(d,t)|seg::path->letchild,tag_child=split_option@@matchfind_child_nodeseg(d,t)with|None->prefixpath@@kempty|Somen->update_node_contpathnkinletchildren=SegMap.updateseg(fun_->child)d.childrenandtag_children=SegMap.updateseg(fun_->tag_child)t.tag_childreninmk_tree(d.root,children)(t.tag_root,(t.tag_default_child,tag_children))letupdate_contpathvk=matchvwith|None->prefixpath@@kempty|Somen->update_node_contpathnkletupdate_subtreepathfv=update_contpathvfletupdate_rootf=function|None->root_opt@@fNone|Some(d,t)->letroot,tag_root=split_option@@f(find_root_node(d,t))inmk_tree(root,d.children)(tag_root,(t.tag_default_child,t.tag_children))letupdate_singletonpathfv=update_contpathv(update_rootf)(** {1 Union} *)letunion_optionmr1r2=matchr1,r2with|None,None->None|Somer,None|None,Somer->Somer|Somer1,Somer2->Some(mr1r2)(* this function is optimized for the cases where the merging is rare *)letrecunion_node~prefixmn1n2=let(nd1,nt1)asn1=drop_tag_default_childn1and(nd2,nt2)asn2=drop_tag_default_childn2inletroot,tag_root=split_option@@union_option(mprefix)(find_root_noden1)(find_root_noden2)inlettag_exclusive_children=SegMap.union(fun_seg_t1_t2->None)nt1.tag_childrennt2.tag_childreninlettag_overlapping_children=refSegMap.emptyinletchildren=SegMap.union(funsegd1d2->lett1=SegMap.findsegnt1.tag_childrenandt2=SegMap.findsegnt2.tag_childreninletd,t=union_node~prefix:(prefix#<seg)m(d1,t1)(d2,t2)intag_overlapping_children:=SegMap.addsegt!tag_overlapping_children;Somed)nd1.childrennd2.childreninlettag_children=SegMap.union(fun___->assertfalse)tag_exclusive_children!tag_overlapping_childrenin{root;children},{tag_root;tag_default_child=None;tag_children}letunion_~prefixm=union_option(union_node~prefixm)let[@inline]union?(prefix=Emp)m=union_~prefixmletunion_subtree?(prefix=Emp)mv1(path,v2)=update_contpathv1@@funv1->union_~prefix:(prefix<><path)mv1v2letunion_root?(prefix=Emp)mv1v2=matchv1with|None->rootv2|Some(d1,t1)->letroot,tag_root=split_option@@union_option(mprefix)(find_root_node(d1,t1))(Somev2)innon_empty({d1withroot},{t1withtag_root})letunion_singleton?(prefix=Emp)mv1(path,v2)=update_contpathv1@@funv1->union_root~prefix:(prefix<><path)mv1v2(** {1 Detaching subtrees} *)letapply_and_update_contpatht(k:_t->'ans*_t):'ans*_t=matchtwith|None->letans,t=kemptyinans,prefixpatht|Somen->letans=refNoneinlett=update_node_contpathn(funt->leta,t=ktinans:=Somea;t)inOption.get!ans,tletdetach_subtreepatht=apply_and_update_contpatht@@funt->t,emptyletdetach_root=function|None->None,empty|Some(d,t)->find_root_node(d,t),mk_tree(None,d.children)(None,(t.tag_default_child,t.tag_children))letdetach_singletonpatht=apply_and_update_contpathtdetach_root(** {1 Iteration} *)letreciter_node~prefixfn=Option.iter(fprefix)(find_root_noden);SegMap.iter(funseg->iter_node~prefix:(prefix#<seg)f)(get_children_noden)letiter?(prefix=Emp)fv=Option.iter(iter_node~prefixf)vletrecfilter_map_node~prefixfn:_t=letroot,tag_root=split_option@@Option.bind(find_root_noden)(fprefix)inletchildren,tag_children=split_children@@SegMap.filter_map(funseg->filter_map_node~prefix:(prefix#<seg)f)(get_children_noden)inmk_tree(root,children)(tag_root,(None,tag_children))letfilter_map?(prefix=Emp)fv=Option.bindv@@filter_map_node~prefixfletmap?prefixf=filter_map?prefix@@funprefix(d,t)->Some(fprefix(d,t))letfilter?prefixf=filter_map?prefix@@funprefix(d,t)->iffprefix(d,t)thenSome(d,t)elseNone(** {1 Conversion from/to Seq} *)letto_seq_with_bwd_paths(typedata)(typetag)?prefix(t:(data,tag)t)=letmoduleS=Algaeff.Sequencer.Make(structtypeelt=bwd_path*(data*tag)end)inS.run@@fun()->iter?prefix(funp(d,t)->S.yield(p,(d,t)))tletto_seq_valuest=Seq.mapsnd@@to_seq_with_bwd_pathstletto_seq?prefixt=Seq.map(fun(p,v)->Bwd.to_listp,v)@@to_seq_with_bwd_paths?prefixtletof_seq_with_merger?prefixm=Seq.fold_left(union_singleton?prefixm)emptyletof_seqs=of_seq_with_merger~prefix:Emp(fun__y->y)s(** {1 Tags} *)type'datauntagged=('data,unit)tlet[@inline]retagt:_t->_t=function|None->None|Some(d,_)->non_empty@@mk_node'd(Somet)let[@inline]untagt=retag()tletretag_subtreepatht(v:_t):_t=update_subtreepath(retagt)vletreciter_tag_node(f:'a->unit)(t:'atag_node)=Option.iterft.tag_root;Option.iterft.tag_default_child;SegMap.iter(fun_->iter_tag_nodef)t.tag_childrenletset_of_tags(typetag)(cmp:tag->tag->int)(v:('data,tag)t):tagSeq.t=letmoduleTagSet=Set.Make(structtypet=tagletcompare=cmpend)inletset=refTagSet.emptyinOption.iter(fun(_,n)->iter_tag_node(funt->set:=TagSet.addt!set)n)v;TagSet.to_seq!set