12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154open!ImportmoduleArray=Array0moduleEither=Either0includeList1(* This itself includes [List0]. *)letinvalid_argf=Printf.invalid_argfmoduleT=structtype'at='alist[@@deriving_inlinesexp,sexp_grammar]lett_of_sexp:'a.(Ppx_sexp_conv_lib.Sexp.t->'a)->Ppx_sexp_conv_lib.Sexp.t->'at=list_of_sexp;;letsexp_of_t:'a.('a->Ppx_sexp_conv_lib.Sexp.t)->'at->Ppx_sexp_conv_lib.Sexp.t=sexp_of_list;;let(t_sexp_grammar:Ppx_sexp_conv_lib.Sexp.Private.Raw_grammar.t)=let(_the_generic_group:Ppx_sexp_conv_lib.Sexp.Private.Raw_grammar.generic_group)={implicit_vars=["list"];ggid="j\132);\135qH\158\135\222H\001\007\004\158\218";types=["t",Explicit_bind(["a"],Apply(Implicit_var0,[Explicit_var0]))]}inlet(_the_group:Ppx_sexp_conv_lib.Sexp.Private.Raw_grammar.group)={gid=Ppx_sexp_conv_lib.Lazy_group_id.create();apply_implicit=[list_sexp_grammar];generic_group=_the_generic_group;origin="list.ml.T"}inlet(t_sexp_grammar:Ppx_sexp_conv_lib.Sexp.Private.Raw_grammar.t)=Ref("t",_the_group)int_sexp_grammar;;[@@@end]endmoduleOr_unequal_lengths=structtype'at=|Okof'a|Unequal_lengths[@@deriving_inlinecompare,sexp_of]letcompare:'a.('a->'a->int)->'at->'at->int=fun_cmp__aa__001_b__002_->ifPpx_compare_lib.phys_equala__001_b__002_then0else(matcha__001_,b__002_with|Ok_a__003_,Ok_b__004_->_cmp__a_a__003__b__004_|Ok_,_->-1|_,Ok_->1|Unequal_lengths,Unequal_lengths->0);;letsexp_of_t:typea.(a->Ppx_sexp_conv_lib.Sexp.t)->at->Ppx_sexp_conv_lib.Sexp.t=fun_of_a->function|Okv0->letv0=_of_av0inPpx_sexp_conv_lib.Sexp.List[Ppx_sexp_conv_lib.Sexp.Atom"Ok";v0]|Unequal_lengths->Ppx_sexp_conv_lib.Sexp.Atom"Unequal_lengths";;[@@@end]endincludeTletinvariantft=itert~fletof_listt=tletrange'~compare~stride?(start=`inclusive)?(stop=`exclusive)start_istop_i=letnext_i=stridestart_iinletorderxy=Ordering.of_int(comparexy)inletraise_stride_cannot_return_same_value()=invalid_arg"List.range': stride function cannot return the same value"inletinitial_stride_order=matchorderstart_inext_iwith|Equal->raise_stride_cannot_return_same_value()|Less->`Less|Greater->`Greaterinletrecloopiaccum=leti_to_stop_order=orderistop_iinmatchi_to_stop_order,initial_stride_orderwith|Less,`Less|Greater,`Greater->(* haven't yet reached [stop_i]. Continue. *)letnext_i=strideiin(matchorderinext_i,initial_stride_orderwith|Equal,_->raise_stride_cannot_return_same_value()|Less,`Greater|Greater,`Less->invalid_arg"List.range': stride function cannot change direction"|Less,`Less|Greater,`Greater->loopnext_i(i::accum))|Less,`Greater|Greater,`Less->(* stepped past [stop_i]. Finished. *)accum|Equal,_->(* reached [stop_i]. Finished. *)(matchstopwith|`inclusive->i::accum|`exclusive->accum)inletstart_i=matchstartwith|`inclusive->start_i|`exclusive->next_iinrev(loopstart_i[]);;letrange?(stride=1)?(start=`inclusive)?(stop=`exclusive)start_istop_i=ifstride=0theninvalid_arg"List.range: stride must be non-zero";range'~compare~stride:(funx->x+stride)~start~stopstart_istop_i;;lethdt=matchtwith|[]->None|x::_->Somex;;lettlt=matchtwith|[]->None|_::t'->Somet';;letnthtn=ifn<0thenNoneelse(letrecnth_auxtn=matchtwith|[]->None|a::t->ifn=0thenSomeaelsenth_auxt(n-1)innth_auxtn);;letnth_exntn=matchnthtnwith|None->invalid_argf"List.nth_exn %d called on list of length %d"n(lengtht)()|Somea->a;;letunordered_appendl1l2=matchl1,l2with|[],l|l,[]->l|_->rev_appendl1l2;;letcheck_length2_exnnamel1l2=letn1=lengthl1inletn2=lengthl2inifn1<>n2theninvalid_argf"length mismatch in %s: %d <> %d"namen1n2();;letcheck_length3_exnnamel1l2l3=letn1=lengthl1inletn2=lengthl2inletn3=lengthl3inifn1<>n2||n2<>n3theninvalid_argf"length mismatch in %s: %d <> %d || %d <> %d"namen1n2n2n3();;letcheck_length2l1l2~f=iflengthl1<>lengthl2thenOr_unequal_lengths.Unequal_lengthselseOk(fl1l2);;letcheck_length3l1l2l3~f=letn1=lengthl1inletn2=lengthl2inletn3=lengthl3inifn1<>n2||n2<>n3thenOr_unequal_lengths.Unequal_lengthselseOk(fl1l2l3);;letiter2l1l2~f=check_length2l1l2~f:(iter2_ok~f)letiter2_exnl1l2~f=check_length2_exn"iter2_exn"l1l2;iter2_okl1l2~f;;letrev_map2l1l2~f=check_length2l1l2~f:(rev_map2_ok~f)letrev_map2_exnl1l2~f=check_length2_exn"rev_map2_exn"l1l2;rev_map2_okl1l2~f;;letfold2l1l2~init~f=check_length2l1l2~f:(fold2_ok~init~f)letfold2_exnl1l2~init~f=check_length2_exn"fold2_exn"l1l2;fold2_okl1l2~init~f;;letfor_all2l1l2~f=check_length2l1l2~f:(for_all2_ok~f)letfor_all2_exnl1l2~f=check_length2_exn"for_all2_exn"l1l2;for_all2_okl1l2~f;;letexists2l1l2~f=check_length2l1l2~f:(exists2_ok~f)letexists2_exnl1l2~f=check_length2_exn"exists2_exn"l1l2;exists2_okl1l2~f;;letmemta~equal=letrecloopequala=function|[]->false|b::bs->equalab||loopequalabsinloopequalat;;(* This is a copy of the code from the standard library, with an extra eta-expansion to
avoid creating partial closures (showed up for [filter]) in profiling). *)letrev_filtert~f=letrecfind~faccu=function|[]->accu|x::l->iffxthenfind~f(x::accu)lelsefind~facculinfind~f[]t;;letfiltert~f=rev(rev_filtert~f)letfind_mapt~f=letrecloop=function|[]->None|x::l->(matchfxwith|None->loopl|Some_asr->r)inloopt;;letfind_map_exn=letnot_found=Not_found_s(Atom"List.find_map_exn: not found")inletfind_map_exnt~f=matchfind_mapt~fwith|None->raisenot_found|Somex->xin(* named to preserve symbol in compiled binary *)find_map_exn;;letfindt~f=letrecloop=function|[]->None|x::l->iffxthenSomexelselooplinloopt;;letfind_exn=letnot_found=Not_found_s(Atom"List.find_exn: not found")inletrecfind_exnt~f=matchtwith|[]->raisenot_found|x::t->iffxthenxelsefind_exnt~fin(* named to preserve symbol in compiled binary *)find_exn;;letfindit~f=letrecloopit=matchtwith|[]->None|x::l->iffixthenSome(i,x)elseloop(i+1)linloop0t;;letfind_mapit~f=letrecloopit=matchtwith|[]->None|x::l->(matchfixwith|Some_asresult->result|None->loop(i+1)l)inloop0t;;letfind_mapi_exn=letnot_found=Not_found_s(Atom"List.find_mapi_exn: not found")inletfind_mapi_exnt~f=matchfind_mapit~fwith|None->raisenot_found|Somex->xin(* named to preserve symbol in compiled binary *)find_mapi_exn;;letfor_allit~f=letrecloopit=matchtwith|[]->true|hd::tl->fihd&&loop(i+1)tlinloop0t;;letexistsit~f=letrecloopit=matchtwith|[]->false|hd::tl->fihd||loop(i+1)tlinloop0t;;(** For the container interface. *)letfold_left=foldletto_array=Array.of_listletto_listt=t(** Tail recursive versions of standard [List] module *)letslow_appendl1l2=rev_append(revl1)l2(* There are a few optimized list operations here, including append and map. There are
basically two optimizations in play: loop unrolling, and dynamic switching between
stack and heap allocation.
The loop-unrolling is straightforward, we just unroll 5 levels of the loop. This makes
each iteration faster, and also reduces the number of stack frames consumed per list
element.
The dynamic switching is done by counting the number of stack frames, and then
switching to the "slow" implementation when we exceed a given limit. This means that
short lists use the fast stack-allocation method, and long lists use a slower one that
doesn't require stack space. *)letreccount_appendl1l2count=matchl2with|[]->l1|_->(matchl1with|[]->l2|[x1]->x1::l2|[x1;x2]->x1::x2::l2|[x1;x2;x3]->x1::x2::x3::l2|[x1;x2;x3;x4]->x1::x2::x3::x4::l2|x1::x2::x3::x4::x5::tl->x1::x2::x3::x4::x5::(ifcount>1000thenslow_appendtll2elsecount_appendtll2(count+1)));;letappendl1l2=count_appendl1l20letslow_mapl~f=rev(rev_mapl~f)letreccount_map~flctr=matchlwith|[]->[]|[x1]->letf1=fx1in[f1]|[x1;x2]->letf1=fx1inletf2=fx2in[f1;f2]|[x1;x2;x3]->letf1=fx1inletf2=fx2inletf3=fx3in[f1;f2;f3]|[x1;x2;x3;x4]->letf1=fx1inletf2=fx2inletf3=fx3inletf4=fx4in[f1;f2;f3;f4]|x1::x2::x3::x4::x5::tl->letf1=fx1inletf2=fx2inletf3=fx3inletf4=fx4inletf5=fx5inf1::f2::f3::f4::f5::(ifctr>1000thenslow_map~ftlelsecount_map~ftl(ctr+1));;letmapl~f=count_map~fl0letfolding_mapt~init~f=letacc=refinitinmapt~f:(funx->letnew_acc,y=f!accxinacc:=new_acc;y);;letfold_mapt~init~f=letacc=refinitinletresult=mapt~f:(funx->letnew_acc,y=f!accxinacc:=new_acc;y)in!acc,result;;let(>>|)lf=mapl~fletmap2_okl1l2~f=rev(rev_map2_okl1l2~f)letmap2l1l2~f=check_length2l1l2~f:(map2_ok~f)letmap2_exnl1l2~f=check_length2_exn"map2_exn"l1l2;map2_okl1l2~f;;letrev_map3_okl1l2l3~f=letrecloopl1l2l3ac=matchl1,l2,l3with|[],[],[]->ac|x1::l1,x2::l2,x3::l3->loopl1l2l3(fx1x2x3::ac)|_->assertfalseinloopl1l2l3[];;letrev_map3l1l2l3~f=check_length3l1l2l3~f:(rev_map3_ok~f)letrev_map3_exnl1l2l3~f=check_length3_exn"rev_map3_exn"l1l2l3;rev_map3_okl1l2l3~f;;letmap3_okl1l2l3~f=rev(rev_map3_okl1l2l3~f)letmap3l1l2l3~f=check_length3l1l2l3~f:(map3_ok~f)letmap3_exnl1l2l3~f=check_length3_exn"map3_exn"l1l2l3;map3_okl1l2l3~f;;letrecrev_map_appendl1l2~f=matchl1with|[]->l2|h::t->rev_map_append~ft(fh::l2);;letfold_rightl~f~init=matchlwith|[]->init(* avoid the allocation of [~f] below *)|_->fold~f:(funab->fba)~init(revl);;letunziplist=letreclooplistl1l2=matchlistwith|[]->revl1,revl2|(x,y)::tl->looptl(x::l1)(y::l2)inlooplist[][];;letunzip3list=letreclooplistl1l2l3=matchlistwith|[]->revl1,revl2,revl3|(x,y,z)::tl->looptl(x::l1)(y::l2)(z::l3)inlooplist[][][];;letzip_exnl1l2=check_length2_exn"zip_exn"l1l2;map2_ok~f:(funab->a,b)l1l2;;letzipl1l2=map2~f:(funab->a,b)l1l2(** Additional list operations *)letrev_mapil~f=letrecloopiacc=function|[]->acc|h::t->loop(i+1)(fih::acc)tinloop0[]l;;letmapil~f=rev(rev_mapil~f)letfolding_mapit~init~f=letacc=refinitinmapit~f:(funix->letnew_acc,y=fi!accxinacc:=new_acc;y);;letfold_mapit~init~f=letacc=refinitinletresult=mapit~f:(funix->letnew_acc,y=fi!accxinacc:=new_acc;y)in!acc,result;;letiteril~f=ignore(foldl~init:0~f:(funix->fix;i+1):int);;letfoldit~init~f=snd(foldt~init:(0,init)~f:(fun(i,acc)v->i+1,fiaccv));;letfilteril~f=rev(foldil~f:(funposaccx->iffposxthenx::accelseacc)~init:[]);;letreducel~f=matchlwith|[]->None|hd::tl->Some(fold~init:hd~ftl);;letreduce_exnl~f=matchreducel~fwith|None->invalid_arg"List.reduce_exn"|Somev->v;;letreduce_balancedl~f=(* Call the "size" of a value the number of list elements that have been combined into
it via calls to [f]. We proceed by using [f] to combine elements in the accumulator
of the same size until we can't combine any more, then getting a new element from the
input list and repeating.
With this strategy, in the accumulator:
- we only ever have elements of sizes a power of two
- we never have more than one element of each size
- the sum of all the element sizes is equal to the number of elements consumed
These conditions enforce that list of elements of each size is precisely the binary
expansion of the number of elements consumed: if you've consumed 13 = 0b1101
elements, you have one element of size 8, one of size 4, and one of size 1. Hence
when a new element comes along, the number of combinings you need to do is the number
of trailing 1s in the binary expansion of [num], the number of elements that have
already gone into the accumulator. The accumulator is in ascending order of size, so
the next element to combine with is always the head of the list. *)letrecstep_accumnumaccx=ifnumland1=0thenx::accelse(matchaccwith|[]->assertfalse(* New elements from later in the input list go on the front of the accumulator, so
the accumulator is in reverse order wrt the original list order, hence [f y x]
instead of [f x y]. *)|y::ys->step_accum(numasr1)ys(fyx))in(* Experimentally, inlining [foldi] and unrolling this loop a few times can reduce
runtime down to a third and allocation to 1/16th or so in the microbenchmarks below.
However, in most use cases [f] is likely to be expensive (otherwise why do you care
about the order of reduction?) so the overhead of this function itself doesn't really
matter. If you come up with a use-case where it does, then that's something you might
want to try: see hg log -pr 49ef065f429d. *)matchfoldil~init:[]~f:step_accumwith|[]->None|x::xs->Some(foldxs~init:x~f:(funxy->fyx));;letreduce_balanced_exnl~f=matchreduce_balancedl~fwith|None->invalid_arg"List.reduce_balanced_exn"|Somev->v;;letgroupil~break=letgroups=foldil~init:[]~f:(funiaccx->matchaccwith|[]->[[x]]|current_group::tl->ifbreaki(hd_exncurrent_group)xthen[x]::current_group::tl(* start new group *)else(x::current_group)::tl)(* extend current group *)inmatchgroupswith|[]->[]|l->rev_mapl~f:rev;;letgroupl~break=groupil~break:(fun_xy->breakxy)letconcat_mapl~f=letrecauxacc=function|[]->revacc|hd::tl->aux(rev_append(fhd)acc)tlinaux[]l;;letconcat_mapil~f=letrecauxcontacc=function|[]->revacc|hd::tl->aux(cont+1)(rev_append(fconthd)acc)tlinaux0[]l;;letmergel1l2~compare=letrecloopaccl1l2=matchl1,l2with|[],l2->rev_appendaccl2|l1,[]->rev_appendaccl1|h1::t1,h2::t2->ifcompareh1h2<=0thenloop(h1::acc)t1l2elseloop(h2::acc)l1t2inloop[]l1l2;;includestruct(* We are explicit about what we import from the general Monad functor so that we don't
accidentally rebind more efficient list-specific functions. *)moduleMonad=Monad.Make(structtype'at='alistletbindx~f=concat_mapx~fletmap=`Custommapletreturnx=[x]end)openMonadmoduleMonad_infix=Monad_infixmoduleLet_syntax=Let_syntaxletignore_m=ignore_mletjoin=joinletbind=bindlet(>>=)tf=bindt~fletreturn=returnletall=allletall_unit=all_unitend(** returns final element of list *)letreclast_exnlist=matchlistwith|[x]->x|_::tl->last_exntl|[]->invalid_arg"List.last";;(** optionally returns final element of list *)letreclastlist=matchlistwith|[x]->Somex|_::tl->lasttl|[]->None;;letrecis_prefixlist~prefix~equal=matchprefixwith|[]->true|hd::tl->(matchlistwith|[]->false|hd'::tl'->equalhdhd'&&is_prefixtl'~prefix:tl~equal);;letfind_consecutive_duplicatet~equal=matchtwith|[]->None|a1::t->letrecloopa1t=matchtwith|[]->None|a2::t->ifequala1a2thenSome(a1,a2)elseloopa2tinloopa1t;;(* returns list without adjacent duplicates *)letremove_consecutive_duplicates?(which_to_keep=`Last)list~equal=letrecloopto_keepaccum=function|[]->to_keep::accum|hd::tl->ifequalhdto_keepthen(letto_keep=matchwhich_to_keepwith|`First->to_keep|`Last->hdinloopto_keepaccumtl)elseloophd(to_keep::accum)tlinmatchlistwith|[]->[]|hd::tl->rev(loophd[]tl);;(** returns sorted version of list with duplicates removed *)letdedup_and_sort~comparelist=matchlistwith|[]|[_]->list(* performance hack *)|_->letequalxx'=comparexx'=0inletsorted=sort~comparelistinremove_consecutive_duplicates~equalsorted;;letfind_a_dup~comparel=letsorted=sort~comparelinletrecloopl=matchlwith|[]|[_]->None|hd1::(hd2::_astl)->ifcomparehd1hd2=0thenSomehd1elselooptlinloopsorted;;letcontains_dup~comparelst=matchfind_a_dup~comparelstwith|Some_->true|None->false;;letfind_all_dups~comparel=(* We add this reversal, so we can skip a [rev] at the end. We could skip
[rev] anyway since we don not give any ordering guarantees, but it is
nice to get results in natural order. *)letcompareab=-1*compareabinletsorted=sort~comparelin(* Walk the list and record the first of each consecutive run of identical elements *)letrecloopsortedprev~already_recordedacc=matchsortedwith|[]->acc|hd::tl->ifcompareprevhd<>0thenlooptlhd~already_recorded:falseaccelseifalready_recordedthenlooptlhd~already_recorded:trueaccelselooptlhd~already_recorded:true(hd::acc)inmatchsortedwith|[]->[]|hd::tl->looptlhd~already_recorded:false[];;letcountt~f=Container.count~foldt~fletsummt~f=Container.sum~foldmt~fletmin_eltt~compare=Container.min_elt~foldt~compareletmax_eltt~compare=Container.max_elt~foldt~compareletcountit~f=foldit~init:0~f:(funidxcounta->iffidxathencount+1elsecount);;letinitn~f=ifn<0theninvalid_argf"List.init %d"n();letrecloopiaccum=assert(i>=0);ifi=0thenaccumelseloop(i-1)(f(i-1)::accum)inloopn[];;letrev_filter_mapl~f=letreclooplaccum=matchlwith|[]->accum|hd::tl->(matchfhdwith|Somex->looptl(x::accum)|None->looptlaccum)inloopl[];;letfilter_mapl~f=rev(rev_filter_mapl~f)letrev_filter_mapil~f=letrecloopilaccum=matchlwith|[]->accum|hd::tl->(matchfihdwith|Somex->loop(i+1)tl(x::accum)|None->loop(i+1)tlaccum)inloop0l[];;letfilter_mapil~f=rev(rev_filter_mapil~f)letfilter_optl=filter_mapl~f:Fn.idletpartition3_mapt~f=letreclooptfstsndtrd=matchtwith|[]->revfst,revsnd,revtrd|x::t->(matchfxwith|`Fsty->loopt(y::fst)sndtrd|`Sndy->looptfst(y::snd)trd|`Trdy->looptfstsnd(y::trd))inloopt[][][];;letpartition_tft~f=letfx:_Either.t=iffxthenFirstxelseSecondxinpartition_mapt~f;;letpartition_resultt=partition_mapt~f:Result.to_eithermoduleAssoc=structtype('a,'b)t=('a*'b)list[@@deriving_inlinesexp]lett_of_sexp:'a'b.(Ppx_sexp_conv_lib.Sexp.t->'a)->(Ppx_sexp_conv_lib.Sexp.t->'b)->Ppx_sexp_conv_lib.Sexp.t->('a,'b)t=let_tp_loc="list.ml.Assoc.t"infun_of_a_of_bt->list_of_sexp(function|Ppx_sexp_conv_lib.Sexp.List[v0;v1]->letv0=_of_av0andv1=_of_bv1inv0,v1|sexp->Ppx_sexp_conv_lib.Conv_error.tuple_of_size_n_expected_tp_loc2sexp)t;;letsexp_of_t:'a'b.('a->Ppx_sexp_conv_lib.Sexp.t)->('b->Ppx_sexp_conv_lib.Sexp.t)->('a,'b)t->Ppx_sexp_conv_lib.Sexp.t=fun_of_a_of_bv->sexp_of_list(function|v0,v1->letv0=_of_av0andv1=_of_bv1inPpx_sexp_conv_lib.Sexp.List[v0;v1])v;;[@@@end]letfindt~equalkey=matchfindt~f:(fun(key',_)->equalkeykey')with|None->None|Somex->Some(sndx);;letfind_exn=letnot_found=Not_found_s(Atom"List.Assoc.find_exn: not found")inletfind_exnt~equalkey=matchfindtkey~equalwith|None->raisenot_found|Somevalue->valuein(* named to preserve symbol in compiled binary *)find_exn;;letmemt~equalkey=matchfindt~equalkeywith|None->false|Some_->true;;letremovet~equalkey=filtert~f:(fun(key',_)->not(equalkeykey'))letaddt~equalkeyvalue=(* the remove doesn't change the map semantics, but keeps the list small *)(key,value)::removet~equalkey;;letinverset=mapt~f:(fun(x,y)->y,x)letmapt~f=mapt~f:(fun(key,value)->key,fvalue)endletsubl~pos~len=(* We use [pos > length l - len] rather than [pos + len > length l] to avoid the
possibility of overflow. *)ifpos<0||len<0||pos>lengthl-lentheninvalid_arg"List.sub";rev(foldil~init:[]~f:(funiaccel->ifi>=pos&&i<pos+lenthenel::accelseacc));;letsplit_nt_orign=ifn<=0then[],t_origelse(letrecloopntaccum=ifn=0thenrevaccum,telse(matchtwith|[]->t_orig,[](* in this case, t_orig = rev accum *)|hd::tl->loop(n-1)tl(hd::accum))inloopnt_orig[]);;(* copied from [split_n] to avoid allocating a tuple *)lettaket_orign=ifn<=0then[]else(letrecloopntaccum=ifn=0thenrevaccumelse(matchtwith|[]->t_orig|hd::tl->loop(n-1)tl(hd::accum))inloopnt_orig[]);;letrecdroptn=matchtwith|_::tlwhenn>0->droptl(n-1)|t->t;;letchunks_ofl~length=iflength<=0theninvalid_argf"List.chunks_of: Expected length > 0, got %d"length();letrecauxof_lengthaccl=matchlwith|[]->revacc|_::_->letsublist,l=split_nllengthinauxof_length(sublist::acc)linauxlength[]l;;letsplit_whilexs~f=letrecloopacc=function|hd::tlwhenfhd->loop(hd::acc)tl|t->revacc,tinloop[]xs;;(* copied from [split_while] to avoid allocating a tuple *)lettake_whilexs~f=letrecloopacc=function|hd::tlwhenfhd->loop(hd::acc)tl|_->revaccinloop[]xs;;letrecdrop_whilet~f=matchtwith|hd::tlwhenfhd->drop_whiletl~f|t->t;;letdrop_lastt=matchrevtwith|[]->None|_::lst->Some(revlst);;letdrop_last_exnt=matchdrop_lasttwith|None->failwith"List.drop_last_exn: empty list"|Somelst->lst;;letcartesian_productlist1list2=ifis_emptylist2then[]else(letrecloopl1l2accum=matchl1with|[]->accum|hd::tl->looptll2(rev_append(map~f:(funx->hd,x)l2)accum)inrev(looplist1list2[]));;letconcatl=fold_rightl~init:[]~f:appendletconcat_no_orderl=foldl~init:[]~f:(funaccl->rev_appendlacc)letconsxl=x::lletis_sortedl~compare=letrecloopl=matchlwith|[]|[_]->true|x1::(x2::_asrest)->comparex1x2<=0&&looprestinloopl;;letis_sorted_strictlyl~compare=letrecloopl=matchlwith|[]|[_]->true|x1::(x2::_asrest)->comparex1x2<0&&looprestinloopl;;moduleInfix=structlet(@)=appendendletpermute?(random_state=Random.State.default)list=matchlistwith(* special cases to speed things up in trivial cases *)|[]|[_]->list|[x;y]->ifRandom.State.boolrandom_statethen[y;x]elselist|_->letarr=Array.of_listlistinArray_permute.permutearr~random_state;Array.to_listarr;;letrandom_element_exn?(random_state=Random.State.default)list=ifis_emptylistthenfailwith"List.random_element_exn: empty list"elsenth_exnlist(Random.State.intrandom_state(lengthlist));;letrandom_element?(random_state=Random.State.default)list=trySome(random_element_exn~random_statelist)with|_->None;;letreccomparecmpab=matcha,bwith|[],[]->0|[],_->-1|_,[]->1|x::xs,y::ys->letn=cmpxyinifn=0thencomparecmpxsyselsen;;lethash_fold_t=hash_fold_listletequalequalt1t2=letrecloop~equalt1t2=matcht1,t2with|[],[]->true|x1::t1,x2::t2->equalx1x2&&loop~equalt1t2|_->falseinloop~equalt1t2;;lettranspose=letrectranspose_auxtrev_columns=matchpartition_mapt~f:(function|[]->Second()|x::xs->First(x,xs))with|_::_,_::_->None|[],_->Some(rev_appendrev_columns[])|heads_and_tails,[]->letcolumn,trimmed_rows=unzipheads_and_tailsintranspose_auxtrimmed_rows(column::rev_columns)infunt->transpose_auxt[];;exceptionTranspose_got_lists_of_different_lengthsofintlist[@@deriving_inlinesexp]let()=Ppx_sexp_conv_lib.Conv.Exn_converter.add[%extension_constructorTranspose_got_lists_of_different_lengths](function|Transpose_got_lists_of_different_lengthsv0->letv0=sexp_of_listsexp_of_intv0inPpx_sexp_conv_lib.Sexp.List[Ppx_sexp_conv_lib.Sexp.Atom"list.ml.Transpose_got_lists_of_different_lengths";v0]|_->assertfalse);;[@@@end]lettranspose_exnl=matchtransposelwith|Somel->l|None->raise(Transpose_got_lists_of_different_lengths(mapl~f:length));;letintersperset~sep=matchtwith|[]->[]|x::xs->x::fold_rightxs~init:[]~f:(funyacc->sep::y::acc);;letfold_resultt~init~f=Container.fold_result~fold~init~ftletfold_untilt~init~f=Container.fold_until~fold~init~ftletis_suffixlist~suffix~equal:equal_elt=letlist_len=lengthlistinletsuffix_len=lengthsuffixinlist_len>=suffix_len&&equalequal_elt(droplist(list_len-suffix_len))suffix;;