12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058(************************************************************************)(* * The Coq Proof Assistant / The Coq 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) *)(************************************************************************)type'acmp='a->'a->inttype'aeq='a->'a->boolmoduletypeS=moduletypeofListmoduletypeExtS=sigincludeSvalcompare:'acmp->'alistcmpvalequal:'aeq->'alisteqvalis_empty:'alist->boolvalmem_f:'aeq->'a->'alist->boolvalfor_all_i:(int->'a->bool)->int->'alist->boolvalfor_all2eq:('a->'b->bool)->'alist->'blist->boolvalexists_i:(int->'a->bool)->int->'alist->boolvalprefix_of:'aeq->'alist->'alist->boolvalsame_length:'alist->'blist->boolvalinterval:int->int->intlistvalmake:int->'a->'alistvaladdn:int->'a->'alist->'alistvalinit:int->(int->'a)->'alistvalappend:'alist->'alist->'alistvalconcat:'alistlist->'alistvalflatten:'alistlist->'alistvalassign:'alist->int->'a->'alistvalfilter:('a->bool)->'alist->'alistvalfilter2:('a->'b->bool)->'alist->'blist->'alist*'blistvalfilteri:(int->'a->bool)->'alist->'alistvalfilter_with:boollist->'alist->'alistvalmap_filter:('a->'boption)->'alist->'blistvalmap_filter_i:(int->'a->'boption)->'alist->'blistvalpartitioni :(int->'a->bool)->'alist->'alist*'alistvalmap:('a->'b)->'alist->'blistvalmap2:('a->'b->'c)->'alist->'blist->'clistvalmap_left:('a->'b)->'alist->'blistvalmap_i:(int->'a->'b)->int->'alist->'blistvalmap2_i:(int->'a->'b->'c)->int->'alist->'blist->'clistvalmap3:('a->'b->'c->'d)->'alist->'blist->'clist->'dlistvalmap4:('a->'b->'c->'d->'e)->'alist->'blist->'clist->'dlist->'elistvalmap_of_array:('a->'b)->'aarray->'blistvalmap_append:('a->'blist)->'alist->'blistvalmap_append2:('a->'b->'clist)->'alist->'blist->'clistvalextend:boollist->'a->'alist->'alistvalcount:('a->bool)->'alist->intvalindex:'aeq->'a->'alist->intvalsafe_index:'aeq->'a->'alist->intoptionvalindex0:'aeq->'a->'alist->intvalfold_left_until:('c->'a->'cCSig.until)->'c->'alist->'cvalfold_right_i:(int->'a->'b->'b)->int->'alist->'b->'bvalfold_left_i:(int->'a->'b->'a)->int->'a->'blist->'avalfold_right_and_left:('a->'b->'blist->'a)->'blist->'a->'avalfold_left3:('a->'b->'c->'d->'a)->'a->'blist->'clist->'dlist->'avalfold_left2_set:exn->('a->'b->'c->'blist->'clist->'a)->'a->'blist->'clist->'avalfold_left_map:('a->'b->'a*'c)->'a->'blist->'a*'clistvalfold_right_map:('b->'a->'c*'a)->'blist->'a->'clist*'avalfold_left2_map:('a->'b->'c->'a*'d)->'a->'blist->'clist->'a*'dlistvalfold_right2_map:('b->'c->'a->'d*'a)->'blist->'clist->'a->'dlist*'avalfold_left3_map:('a->'b->'c->'d->'a*'e)->'a->'blist->'clist->'dlist->'a*'elistvalfold_left4_map:('a->'b->'c->'d->'e->'a*'r)->'a->'blist->'clist->'dlist->'elist->'a*'rlistvalexcept:'aeq->'a->'alist->'alistvalremove:'aeq->'a->'alist->'alistvalremove_first:('a->bool)->'alist->'alistvalextract_first:('a->bool)->'alist->'alist*'avalfind_map:('a->'boption)->'alist->'bexceptionIndexOutOfRangevalgoto:int->'alist->'alist*'alistvalsplit_when:('a->bool)->'alist->'alist*'alistvalsep_last:'alist->'a*'alistvaldrop_last:'alist->'alistvallast:'alist->'avallastn:int->'alist->'alistvalchop:int->'alist->'alist*'alistvalfirstn:int->'alist->'alistvalskipn:int->'alist->'alistvalskipn_at_least:int->'alist->'alistvaldrop_prefix:'aeq->'alist->'alist->'alistvalinsert:('a->'a->bool)->'a->'alist->'alistvalshare_tails:'alist->'alist->'alist*'alist*'alistvalmap_assoc:('a->'b)->('c*'a)list->('c*'b)listvalassoc_f:'aeq->'a->('a*'b)list->'bvalremove_assoc_f:'aeq->'a->('a*'b)list->('a*'b)listvalmem_assoc_f:'aeq->'a->('a*'b)list->boolvalfactorize_left:'aeq->('a*'b)list->('a*'blist)listvalsplit:('a*'b)list->'alist*'blistvalcombine:'alist->'blist->('a*'b)listvalsplit3:('a*'b*'c)list->'alist*'blist*'clistvalsplit4:('a*'b*'c*'d)list->'alist*'blist*'clist*'dlistvalcombine3:'alist->'blist->'clist->('a*'b*'c)listvaladd_set:'aeq->'a->'alist->'alistvaleq_set:'aeq->'alist->'alist->boolvalsubset:'alist->'alist->boolvalmerge_set:'acmp->'alist->'alist->'alistvalintersect:'aeq->'alist->'alist->'alistvalunion:'aeq->'alist->'alist->'alistvalunionq:'alist->'alist->'alistvalsubtract:'aeq->'alist->'alist->'alistvalsubtractq:'alist->'alist->'alistvaldistinct:'alist->boolvaldistinct_f:'acmp->'alist->boolvalduplicates:'aeq->'alist->'alistvaluniquize_key:('a->'b)->'alist->'alistvaluniquize:'alist->'alistvalsort_uniquize:'acmp->'alist->'alistvalmin:'acmp->'alist->'avalcartesian:('a->'b->'c)->'alist->'blist->'clistvalcartesians:('a->'b->'b)->'b->'alistlist->'blistvalcombinations:'alistlist->'alistlistvalcartesians_filter:('a->'b->'boption)->'b->'alistlist->'blistmoduleSmart:sigvalmap:('a->'a)->'alist->'alistendmoduletypeMonoS=sigtypeeltvalequal:eltlist->eltlist->boolvalmem:elt->eltlist->boolvalassoc:elt->(elt*'a)list->'avalmem_assoc:elt->(elt*'a)list->boolvalremove_assoc:elt->(elt*'a)list->(elt*'a)listvalmem_assoc_sym:elt->('a*elt)list->boolendendincludeList(** Tail-rec implementation of usual functions. This is a well-known trick used
in, for instance, ExtLib and Batteries. *)type'acell={head:'a;mutabletail:'alist;}externalcast:'acell->'alist="%identity"(** Extensions and redefinitions of OCaml Stdlib *)(** {6 Equality, testing} *)letrecsame_lengthl1l2=matchl1,l2with|[],[]->true|_::l1,_::l2->same_lengthl1l2|([],_::_)|(_::_,[])->falseletreccomparecmpl1l2=ifl1==l2then0elsematchl1,l2with|[],[]->0|_::_,[]->1|[],_::_->-1|x1::l1,x2::l2->matchcmpx1x2with|0->comparecmpl1l2|c->cletrecequalcmpl1l2=l1==l2||matchl1,l2with|[],[]->true|x1::l1,x2::l2->cmpx1x2&&equalcmpl1l2|_->falseletis_empty=function|[]->true|_->falseletmem_fcmpxl=List.exists(cmpx)lletfor_all_ip=letrecfor_all_pi=function|[]->true|a::l->pia&&for_all_p(i+1)linfor_all_pletfor_all2eqfl1l2=tryList.for_all2fl1l2withInvalid_argument_->falseletexists_ip=letrecexists_pi=function|[]->false|a::l->pia||exists_p(i+1)linexists_pletprefix_ofcmpprefll=letrecprefrec=function|(h1::t1,h2::t2)->cmph1h2&&prefrec(t1,t2)|([],_)->true|_->falseinprefrec(prefl,l)(** {6 Creating lists} *)letintervalnm=letrecinterval_n(l,m)=ifn>mthenlelseinterval_n(m::l,predm)ininterval_n([],m)letaddnnv=letrecauxnl=ifInt.equaln0thenlelseaux(predn)(v::l)inifn<0theninvalid_arg"List.addn"elseauxnletmakenv=addnnv[]letrecinit_looplenfpi=ifInt.equalilenthen()elseletc={head=fi;tail=[]}inp.tail<-castc;init_looplenfc(succi)letinitlenf=iflen<0theninvalid_arg"List.init"elseifInt.equallen0then[]elseletc={head=f0;tail=[]}ininit_looplenfc1;castcletrecappend_loopptl=function|[]->p.tail<-tl|x::l->letc={head=x;tail=[]}inp.tail<-castc;append_loopctllletappendl1l2=matchl1with|[]->l2|x::l->letc={head=x;tail=[]}inappend_loopcl2l;castcletreccopyp=function|[]->p|x::l->letc={head=x;tail=[]}inp.tail<-castc;copyclletrecconcat_loopp=function|[]->()|x::l->concat_loop(copypx)lletconcatl=letdummy={head=Obj.magic0;tail=[]}inconcat_loopdummyl;dummy.tailletflatten=concat(** {6 Lists as arrays} *)letassignlne=letrecassrecstkli=matchl,iwith|(h::t,0)->List.rev_appendstk(e::t)|(h::t,n)->assrec(h::stk)t(predn)|([],_)->failwith"List.assign"inassrec[]ln(** {6 Filtering} *)letrecfilter_loopfp=function|[]->()|x::l'asl->letb=fxinfilter_loopfpl';ifbthenifp.tail==l'thenp.tail<-lelsep.tail<-x::p.tailletrecfilterf=function|[]->[]|x::l'asl->iffxthenletc={head=x;tail=[]}infilter_loopfcl';ifc.tail==l'thenlelsecastcelsefilterfl'letrecfilter2_loopfpql1l2=matchl1,l2with|[],[]->()|x::l1',y::l2'->letb=fxyinfilter2_loopfpql1'l2';ifbthenifp.tail==l1'thenbeginp.tail<-l1;q.tail<-l2endelsebeginp.tail<-x::p.tail;q.tail<-y::q.tailend|_->invalid_arg"List.filter2"letrecfilter2fl1l2=matchl1,l2with|[],[]->([],[])|x1::l1',x2::l2'->letb=fx1x2inifbthenletc1={head=x1;tail=[]}inletc2={head=x2;tail=[]}infilter2_loopfc1c2l1'l2';ifc1.tail==l1'then(l1,l2)else(castc1,castc2)elsefilter2fl1'l2'|_->invalid_arg"List.filter2"letfilterip=letrecfilter_i_reci=function|[]->[]|x::l->letl'=filter_i_rec(succi)linifpixthenx::l'elsel'infilter_i_rec0letrecfilter_with_loopfilterpl=matchfilter,lwith|[],[]->()|b::filter,x::l'->filter_with_loopfilterpl';ifbthenifp.tail==l'thenp.tail<-lelsep.tail<-x::p.tail|_->invalid_arg"List.filter_with"letrecfilter_withfilterl=matchfilter,lwith|[],[]->[]|b::filter,x::l'->ifbthenletc={head=x;tail=[]}infilter_with_loopfiltercl';ifc.tail==l'thenlelsecastcelsefilter_withfilterl'|_->invalid_arg"List.filter_with"letrecmap_filter_loopfp=function|[]->()|x::l->matchfxwith|None->map_filter_loopfpl|Somey->letc={head=y;tail=[]}inp.tail<-castc;map_filter_loopfclletrecmap_filterf=function|[]->[]|x::l'->matchfxwith|None->map_filterfl'|Somey->letc={head=y;tail=[]}inmap_filter_loopfcl';castcletrecmap_filter_i_loopfip=function|[]->()|x::l->matchfixwith|None->map_filter_i_loopf(succi)pl|Somey->letc={head=y;tail=[]}inp.tail<-castc;map_filter_i_loopf(succi)clletrecmap_filter_i_loop'fi=function|[]->[]|x::l'->matchfixwith|None->map_filter_i_loop'f(succi)l'|Somey->letc={head=y;tail=[]}inmap_filter_i_loopf(succi)cl';castcletmap_filter_ifl=map_filter_i_loop'f0lletpartitionip=letrecauxi=function|[]->[],[]|x::l->let(l1,l2)=aux(succi)linifpixthen(x::l1,l2)else(l1,x::l2)inaux0(** {6 Applying functorially} *)letrecmap_loopfp=function|[]->()|x::l->letc={head=fx;tail=[]}inp.tail<-castc;map_loopfclletmapf=function|[]->[]|x::l->letc={head=fx;tail=[]}inmap_loopfcl;castcletrecmap2_loopfpl1l2=matchl1,l2with|[],[]->()|x::l1,y::l2->letc={head=fxy;tail=[]}inp.tail<-castc;map2_loopfcl1l2|_->invalid_arg"List.map2"letmap2fl1l2=matchl1,l2with|[],[]->[]|x::l1,y::l2->letc={head=fxy;tail=[]}inmap2_loopfcl1l2;castc|_->invalid_arg"List.map2"(** Like OCaml [List.mapi] but tail-recursive *)letrecmap_i_loopfip=function|[]->()|x::l->letc={head=fix;tail=[]}inp.tail<-castc;map_i_loopf(succi)clletmap_ifi=function|[]->[]|x::l->letc={head=fix;tail=[]}inmap_i_loopf(succi)cl;castcletmap_left=mapletmap2_ifil1l2=letrecmap_ii=function|([],[])->[]|(h1::t1,h2::t2)->letv=fih1h2inv::map_i(succi)(t1,t2)|(_,_)->invalid_arg"map2_i"inmap_ii(l1,l2)letrecmap3_loopfpl1l2l3=matchl1,l2,l3with|[],[],[]->()|x::l1,y::l2,z::l3->letc={head=fxyz;tail=[]}inp.tail<-castc;map3_loopfcl1l2l3|_->invalid_arg"List.map3"letmap3fl1l2l3=matchl1,l2,l3with|[],[],[]->[]|x::l1,y::l2,z::l3->letc={head=fxyz;tail=[]}inmap3_loopfcl1l2l3;castc|_->invalid_arg"List.map3"letrecmap4_loopfpl1l2l3l4=matchl1,l2,l3,l4with|[],[],[],[]->()|x::l1,y::l2,z::l3,t::l4->letc={head=fxyzt;tail=[]}inp.tail<-castc;map4_loopfcl1l2l3l4|_->invalid_arg"List.map4"letmap4fl1l2l3l4=matchl1,l2,l3,l4with|[],[],[],[]->[]|x::l1,y::l2,z::l3,t::l4->letc={head=fxyzt;tail=[]}inmap4_loopfcl1l2l3l4;castc|_->invalid_arg"List.map4"letrecmap_of_array_loopfpail=ifInt.equalilthen()elseletc={head=f(Array.unsafe_getai);tail=[]}inp.tail<-castc;map_of_array_loopfca(i+1)lletmap_of_arrayfa=letl=Array.lengthainifInt.equall0then[]elseletc={head=f(Array.unsafe_geta0);tail=[]}inmap_of_array_loopfca1l;castcletmap_appendfl=flatten(mapfl)letmap_append2fl1l2=flatten(map2fl1l2)letrecextendlal'=matchl,l'with|true::l,b::l'->b::extendlal'|false::l,l'->a::extendlal'|[],[]->[]|_->invalid_arg"extend"letcountfl=letrecauxacc=function|[]->acc|h::t->iffhthenaux(acc+1)telseauxacctinaux0l(** {6 Finding position} *)letrecindex_ffxln=matchlwith|[]->raiseNot_found|y::l->iffxythennelseindex_ffxl(succn)letindexfxl=index_ffxl1letsafe_indexfxl=trySome(indexfxl)withNot_found->Noneletindex0fxl=index_ffxl0(** {6 Folding} *)letfold_left_untilfaccus=letrecauxaccu=function|[]->accu|x::xs->matchfaccuxwithCSig.Stopx->x|CSig.Conti->auxixsinauxaccusletfold_right_ifil=letrecit_fila=matchlwith|[]->a|b::l->f(i-1)b(it_f(i-1)la)init_f(List.lengthl+i)lletfold_left_if=letrecit_list_fia=function|[]->a|b::l->it_list_f(i+1)(fiab)linit_list_fletrecfold_left3faccul1l2l3=match(l1,l2,l3)with|([],[],[])->accu|(a1::l1,a2::l2,a3::l3)->fold_left3f(faccua1a2a3)l1l2l3|(_,_,_)->invalid_arg"List.fold_left3"letrecfold_left4faccul1l2l3l4=match(l1,l2,l3,l4)with|([],[],[],[])->accu|(a1::l1,a2::l2,a3::l3,a4::l4)->fold_left4f(faccua1a2a3a4)l1l2l3l4|(_,_,_,_)->invalid_arg"List.fold_left4"(* [fold_right_and_left f [a1;...;an] hd =
f (f (... (f (f hd
an
[an-1;...;a1])
an-1
[an-2;...;a1])
...)
a2
[a1])
a1
[]] *)letfold_right_and_leftflhd=letrecauxtl=function|[]->hd|a::l->lethd=aux(a::tl)linfhdatlinaux[]l(* Match sets as lists according to a matching function, also folding a side effect *)letrecfold_left2_setefxl1l2=matchl1with|a1::l1->letrecfindseen=function|[]->raisee|a2::l2->tryfold_left2_setef(fxa1a2l1l2)l1(List.rev_appendseenl2)withe'whene'=e->find(a2::seen)l2infind[]l2|[]->ifl2=[]thenxelseraisee(* Poor man's monadic map *)letrecfold_left_mapfe=function|[]->(e,[])|h::t->lete',h'=fehinlete'',t'=fold_left_mapfe'tine'',h'::t'(* (* tail-recursive version of the above function *)
let fold_left_map f e l =
let g (e,b') h =
let (e',h') = f e h in
(e',h'::b')
in
let (e',lrev) = List.fold_left g (e,[]) l in
(e',List.rev lrev)
*)(* The same, based on fold_right, with the effect accumulated on the right *)letfold_right_mapfle=List.fold_right(funx(l,e)->let(y,e)=fxein(y::l,e))l([],e)leton_sndf(x,y)=(x,fy)letfold_left2_mapfell'=on_sndList.rev@@List.fold_left2(fun(e,l)xx'->let(e,y)=fexx'in(e,y::l))(e,[])ll'letfold_right2_mapfll'e=List.fold_right2(funxx'(l,e)->let(y,e)=fxx'ein(y::l,e))ll'([],e)letfold_left3_mapfell'l''=on_sndList.rev@@fold_left3(fun(e,l)xx'x''->let(e,y)=fexx'x''in(e,y::l))(e,[])ll'l''letfold_left4_mapfel1l2l3l4=on_sndList.rev@@fold_left4(fun(e,l)x1x2x3x4->let(e,y)=fex1x2x3x4in(e,y::l))(e,[])l1l2l3l4(** {6 Splitting} *)letexceptcmpxl=List.filter(funy->not(cmpxy))lletremove=except(* Alias *)letrecremove_firstp=function|b::lwhenpb->l|b::l->b::remove_firstpl|[]->raiseNot_foundletextract_firstpli=letreclooprev_left=function|[]->raiseNot_found|x::right->ifpxthenList.rev_appendrev_leftright,xelseloop(x::rev_left)rightinloop[]liletinsertpvl=letrecinsrec=function|[]->[v]|h::tl->ifpvhthenv::h::tlelseh::insrectlininsreclletrecfind_mapf=function|[]->raiseNot_found|x::l->matchfxwith|None->find_mapfl|Somey->y(* FIXME: again, generic hash function *)letsubsetl1l2=lett2=Hashtbl.create151inList.iter(funx->Hashtbl.addt2x())l2;letreclook=function|[]->true|x::ll->tryHashtbl.findt2x;lookllwithNot_found->falseinlookl1(** [goto i l] splits [l] into two lists [(l1,l2)] such that
[(List.rev l1)++l2=l] and [l1] has length [i]. It raises
[IndexOutOfRange] when [i] is negative or greater than the
length of [l]. *)exceptionIndexOutOfRangeletgotonl=letrecgotoiacc=function|tlwhenInt.equali0->(acc,tl)|h::t->goto(predi)(h::acc)t|[]->raiseIndexOutOfRangeingoton[]l(* [chop i l] splits [l] into two lists [(l1,l2)] such that
[l1++l2=l] and [l1] has length [i].
It raises [Failure] when [i] is negative or greater than the length of [l] *)letchopnl=trylet(h,t)=gotonlin(List.revh,t)withIndexOutOfRange->failwith"List.chop"(* spiwack: should raise [IndexOutOfRange] but I'm afraid of missing
a try/with when replacing the exception. *)(* [split_when p l] splits [l] into two lists [(l1,a::l2)] such that
[l1++(a::l2)=l], [p a=true] and [p b = false] for every element [b] of [l1].
If there is no such [a], then it returns [(l,[])] instead *)letsplit_whenp=letrecsplit_when_loopxy=matchywith|[]->(List.revx,[])|(a::l)->if(pa)then(List.revx,y)elsesplit_when_loop(a::x)linsplit_when_loop[]letfirstnnl=letrecauxaccnl=matchn,lwith|0,_->List.revacc|n,h::t->aux(h::acc)(predn)t|_->failwith"firstn"inaux[]nlletrecsep_last=function|[]->failwith"sep_last"|hd::[]->(hd,[])|hd::tl->let(l,tl)=sep_lasttlin(l,hd::tl)(* Drop the last element of a list *)letrecdrop_last=function|[]->failwith"drop_last"|hd::[]->[]|hd::tl->hd::drop_lasttlletreclast=function|[]->failwith"List.last"|hd::[]->hd|_::tl->lasttlletlastnnl=letlen=List.lengthlinletrecauxml=ifInt.equalmnthenlelseaux(m-1)(List.tll)iniflen<nthenfailwith"lastn"elseauxlenlletrecskipnnl=matchn,lwith|0,_->l|_,[]->failwith"List.skipn"|n,_::l->skipn(predn)lletskipn_at_leastnl=tryskipnnlwithFailure_whenn>=0->[](** if [l=p++t] then [drop_prefix p l] is [t] else [l] *)letdrop_prefixcmppl=letrecdrop_prefix_rec=function|(h1::tp,h2::tl)whencmph1h2->drop_prefix_rec(tp,tl)|([],tl)->tl|_->lindrop_prefix_rec(p,l)letshare_tailsl1l2=letrecshr_revacc=function|(x1::l1,x2::l2)whenx1==x2->shr_rev(x1::acc)(l1,l2)|(l1,l2)->(List.revl1,List.revl2,acc)inshr_rev[](List.revl1,List.revl2)(** {6 Association lists} *)letmap_assocf=map(fun(x,a)->(x,fa))letrecassoc_ffa=function|(x,e)::xs->iffaxtheneelseassoc_ffaxs|[]->raiseNot_foundletremove_assoc_ffal=tryremove_first(fun(x,_)->fax)lwithNot_found->lletmem_assoc_ffal=List.exists(fun(x,_)->fax)l(** {6 Operations on lists of tuples} *)letrecsplit_looppq=function|[]->()|(x,y)::l->letcl={head=x;tail=[]}inletcr={head=y;tail=[]}inp.tail<-castcl;q.tail<-castcr;split_loopclcrlletsplit=function|[]->[],[]|(x,y)::l->letcl={head=x;tail=[]}inletcr={head=y;tail=[]}insplit_loopclcrl;(castcl,castcr)letreccombine_looppl1l2=matchl1,l2with|[],[]->()|x::l1,y::l2->letc={head=(x,y);tail=[]}inp.tail<-castc;combine_loopcl1l2|_->invalid_arg"List.combine"letcombinel1l2=matchl1,l2with|[],[]->[]|x::l1,y::l2->letc={head=(x,y);tail=[]}incombine_loopcl1l2;castc|_->invalid_arg"List.combine"letrecsplit3_looppqr=function|[]->()|(x,y,z)::l->letcp={head=x;tail=[]}inletcq={head=y;tail=[]}inletcr={head=z;tail=[]}inp.tail<-castcp;q.tail<-castcq;r.tail<-castcr;split3_loopcpcqcrlletsplit3=function|[]->[],[],[]|(x,y,z)::l->letcp={head=x;tail=[]}inletcq={head=y;tail=[]}inletcr={head=z;tail=[]}insplit3_loopcpcqcrl;(castcp,castcq,castcr)(** XXX TODO tailrec *)letrecsplit4=function|[]->([],[],[],[])|(a,b,c,d)::l->let(ra,rb,rc,rd)=split4lin(a::ra,b::rb,c::rc,d::rd)letreccombine3_looppl1l2l3=matchl1,l2,l3with|[],[],[]->()|x::l1,y::l2,z::l3->letc={head=(x,y,z);tail=[]}inp.tail<-castc;combine3_loopcl1l2l3|_->invalid_arg"List.combine3"letcombine3l1l2l3=matchl1,l2,l3with|[],[],[]->[]|x::l1,y::l2,z::l3->letc={head=(x,y,z);tail=[]}incombine3_loopcl1l2l3;castc|_->invalid_arg"List.combine3"(** {6 Operations on lists seen as sets, preserving uniqueness of elements} *)(** Add an element, preserving uniqueness of elements *)letadd_setcmpxl=ifmem_fcmpxlthenlelsex::l(** List equality up to permutation (but considering multiple occurrences) *)leteq_setcmpl1l2=letrecauxl1=function|[]->is_emptyl1|a::l2->aux(remove_first(cmpa)l1)l2intryauxl1l2withNot_found->falseletrecmerge_setcmpl1l2=matchl1,l2with|[],l2->l2|l1,[]->l1|h1::t1,h2::t2->letc=cmph1h2inifInt.equalc0thenh1::merge_setcmpt1t2elseifc<=0thenh1::merge_setcmpt1l2elseh2::merge_setcmpl1t2letintersectcmpl1l2=filter(funx->mem_fcmpxl2)l1letunioncmpl1l2=letrecurec=function|[]->l2|a::l->ifmem_fcmpal2thenureclelsea::ureclinurecl1letsubtractcmpl1l2=ifis_emptyl2thenl1elseList.filter(funx->not(mem_fcmpxl2))l1letunionql1l2=union(==)l1l2letsubtractql1l2=subtract(==)l1l2(** {6 Uniqueness and duplication} *)(* FIXME: we should avoid relying on the generic hash function,
just as we'd better avoid Pervasives.compare *)letdistinctl=letvisited=Hashtbl.create23inletrecloop=function|h::t->ifHashtbl.memvisitedhthenfalseelsebeginHashtbl.addvisitedhh;looptend|[]->trueinlooplletdistinct_fcmpl=letrecloop=function|a::b::_whenInt.equal(cmpab)0->false|a::l->loopl|[]->trueinloop(List.sortcmpl)(* FIXME: again, generic hash function *)letuniquize_keyfl=letvisited=Hashtbl.create23inletrecauxaccchanged=function|h::t->letx=fhinifHashtbl.memvisitedxthenauxacctruetelsebeginHashtbl.addvisitedxx;aux(h::acc)changedtend|[]->ifchangedthenList.revaccelselinaux[]falselletuniquizel=uniquize_key(funx->x)l(** [sort_uniquize] might be an alternative to the hashtbl-based
[uniquize], when the order of the elements is irrelevant *)letrecuniquize_sortedcmp=function|a::b::lwhenInt.equal(cmpab)0->uniquize_sortedcmp(a::l)|a::l->a::uniquize_sortedcmpl|[]->[]letsort_uniquizecmpl=uniquize_sortedcmp(List.sortcmpl)letmincmpl=letrecauxcur=function|[]->cur|x::l->ifcmpxcur<0thenauxxlelseauxcurlinmatchlwith|x::l->auxxl|[]->raiseNot_foundletrecduplicatescmp=function|[]->[]|x::l->letl'=duplicatescmplinifmem_fcmpxlthenadd_setcmpxl'elsel'(** {6 Cartesian product} *)(* A generic cartesian product: for any operator (**),
[cartesian (**) [x1;x2] [y1;y2] = [x1**y1; x1**y2; x2**y1; x2**y1]],
and so on if there are more elements in the lists. *)letcartesianopl1l2=map_append(funx->map(opx)l2)l1(* [cartesians] is an n-ary cartesian product: it iterates
[cartesian] over a list of lists. *)letcartesiansopinitll=List.fold_right(cartesianop)ll[init](* combinations [[a;b];[c;d]] gives [[a;c];[a;d];[b;c];[b;d]] *)letcombinationsl=cartesians(funxl->x::l)[]l(* Keep only those products that do not return None *)letcartesian_filteropl1l2=map_append(funx->map_filter(opx)l2)l1(* Keep only those products that do not return None *)letcartesians_filteropinitll=List.fold_right(cartesian_filterop)ll[init](* Factorize lists of pairs according to the left argument *)letrecfactorize_leftcmp=function|(a,b)::l->letal,l'=partition(fun(a',_)->cmpaa')lin(a,(b::mapsndal))::factorize_leftcmpl'|[]->[]moduleSmart=structletrecmapfl=matchlwith|[]->l|h::tl->leth'=fhinlettl'=mapftlinifh'==h&&tl'==tlthenlelseh'::tl'endmoduletypeMonoS=sigtypeeltvalequal:eltlist->eltlist->boolvalmem:elt->eltlist->boolvalassoc:elt->(elt*'a)list->'avalmem_assoc:elt->(elt*'a)list->boolvalremove_assoc:elt->(elt*'a)list->(elt*'a)listvalmem_assoc_sym:elt->('a*elt)list->boolend