123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605(*
* BatEnum - Enumeration over abstract collection of elements.
* Copyright (C) 2003 Nicolas Cannasse
* 2009 David Rajchenbach-Teller, LIFO, Universite d'Orleans
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version,
* with the special exception on linking described in file LICENSE.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*)##V>=5##modulePervasives=Stdlib(*$inject
##V>=5##module Pervasives = Stdlib
*)(** {6 Representation} *)type'at={mutablecount:unit->int;(** Return the number of remaining elements in the enumeration. *)mutablenext:unit->'a;(** Return the next element of the enumeration or raise [No_more_elements].*)mutableclone:unit->'at;(** Return a copy of the enumeration. *)mutablefast:bool;(** [true] if [count] can be done without reading all elements, [false] otherwise.*)}type'aenumerable='attype'amappable='atexternalenum:'at->'at="%identity"externalof_enum:'at->'at="%identity"(* raised by 'next' functions, should NOT go outside the API *)exceptionNo_more_elementsletmake~next~count~clone={count=count;next=next;clone=clone;fast=true;}(** {6 Internal utilities}*)let_dummy ()=assertfalse(* raised by 'count' functions, may go outside the API *)exceptionInfinite_enumletreturn_no_more_elements ()=raiseNo_more_elementsletreturn_no_more_count ()=0letreturn_infinite_count ()=raiseInfinite_enum(* Inlined from ExtList to avoid circular dependencies. *)type'a_mut_list={hd:'a;mutabletl:'a_mut_list;}letrecempty()={count=return_no_more_count;next=return_no_more_elements;clone=empty;fast=true;}letclosee=e.next<-return_no_more_elements;e.count<-return_no_more_count;e.clone<-emptyletforcet=(* Transform [t] into a list *)letreccloneenumcount=letenum=ref!enumandcount=ref!countin{count=(fun()->!count);next=(fun()->match!enumwith|[]->raiseNo_more_elements|h::t->decrcount;enum:=t;h);clone=(fun()->letenum=ref!enumandcount=ref!countincloneenumcount);fast=true;}inletcount=ref0inlet_empty =Obj.magic[]inletrecloopdst=letx={hd=t.next();tl=_empty }inincrcount;dst.tl<-x;loopxinletenum=ref_empty in(tryenum:={hd=t.next();tl=_empty };incrcount;loop!enum;withNo_more_elements->());let tc=clone(Obj.magicenum)countint.clone<-tc.clone;t.next<-tc.next;t.count<-tc.count;t.fast<-true(* Inlined from {!LazyList}.
This lazy list permits cloning enumerations constructed with {!from}
without having to actually force them.*)moduleMicroLazyList=structtype'all_t=('anode_t)Lazy.tand'anode_t=|Nil|Consof'a*'all_tlet nil=lazyNilletenuml=letrec aux(l:'all_t):'at=letreference =reflinlete=make~next:(fun()->matchLazy.force!referencewith|Cons(x,t)->reference:=t;x|Nil->raiseNo_more_elements)~count:_dummy~clone:(fun()->aux!reference)ine.count<-(fun()->forcee;e.count());e.fast<-false;einauxlletfromf=letrec aux()=lazy(letitem=trySome(f())withNo_more_elements->Noneinmatchitemwith|Somex->Cons(x,aux())|_->Nil)inaux()endletfromf=lete={next=_dummy;count=_dummy;clone=_dummy;fast=false;}ine.next<-(fun()->tryf()withNo_more_elements->closee;raiseNo_more_elements);e.count<-(fun()->forcee;e.count());e.clone<-(fun()->let e'=MicroLazyList.enum(MicroLazyList.fromf)ine.next<-e'.next;e.clone<-e'.clone;e.count<-(fun()->forcee;e.count());(* we can't use [e'.count] because that would force [e'],
which doesn't update [e]. That would for example,
cause e.fast to not be updated to true. A simple test
to see the problem with [e'.count] is to do the
following: (1) create a enum using this [from]
function, (2) clone that enum, (3) grab the count of
the original enum and then iterate over it. A
discrepancy between the count and the elements will
result. *)e.fast<-e'.fast;e.clone());eletfrom2nextclone=lete={next=next;count=_dummy;clone=clone;fast=false;}ine.count<-(fun()->forcee;e.count());eletinitnf=(*Experimental fix for init*)ifn<0theninvalid_arg "Enum.init";letcount=refninlet f'()=match!countwith|0->raiseNo_more_elements|_->decrcount;f(n-1-!count)inlete=fromf'ine.fast<-true;e.count<-(fun()->!count);eletgett=trySome(t.next())withNo_more_elements->Noneletget_exnt=t.next()letpushte=letrecmaket=letfnext=t.nextinletfcount =t.countinletfclone =t.cloneinletnext_called =reffalseint.next<-(fun()->next_called :=true;t.next<-fnext;t.count<-fcount;t.clone<-fclone;e);t.count<-(fun()->letn=fcount()inif!next_called thennelsen+1);t.clone<-(fun()->let tc=fclone()inifnot!next_called thenmaketc;tc);inmaketletpeekt=matchgettwith|None->None|Somex->pushtx;SomexmoduleMicroList=(*Inlined from ExtList to avoid circular dependencies*)structletenuml=letrecaux lrcount=make~next:(fun()->match!lrwith|[]->raiseNo_more_elements|h::t->decrcount;lr:=t;h)~count:(fun()->if!count<0thencount:=List.length!lr;!count)~clone:(fun()->aux(ref!lr)(ref!count))inaux(refl)(ref(-1))endlettakene=letr=ref[]inbegintryfor_i=1tondor:=e.next()::!rdonewithNo_more_elements->()end;MicroList.enum(List.rev!r)(*let take n e = (*Er... that looks quite weird.*)
let remaining = ref n in
let f () =
if !remaining >= 0 then
let result = e.next () in
decr remaining;
result
else raise No_more_elements
in let e = make
~next: f
~count:(fun () -> !remaining)
~clone:_dummy
in e.clone <- (fun () -> force e; e.clone ());
e*)letjunkt=tryignore(t.next())withNo_more_elements->()letis_empty t=ift.fastthent.count()=0elsepeekt=Noneletcountt=t.count()letfast_count t=t.fastletclonet=t.clone()letiterft=letrecloop()=f(t.next());loop();intryloop();withNo_more_elements->()letiterift=letrecloopidx=fidx(t.next());loop(idx+1);intryloop0;withNo_more_elements->()letiter2ftu=letpush_t =refNoneinletrecloop()=push_t :=None;lete=t.next()inpush_t :=Somee;fe(u.next());loop()intryloop()withNo_more_elements->match!push_twith|None->()|Somee->pushteletiter2iftu=letpush_t =refNoneinletrecloopidx=push_t :=None;lete=t.next()inpush_t :=Somee;fidxe(u.next());loop(idx+1)intryloop0withNo_more_elements->match!push_twith|None->()|Somee->pushteletfoldfinitt=let acc=refinitinletrecloop()=acc:=f!acc(t.next());loop()intryloop()withNo_more_elements->!accletreduce ft=matchgettwithNone->raiseNot_found|Someinit->foldfinittletsumt=matchgettwith|None->0|Somei->fold(+)it(* Kahan summing. [Enum.reduce (+.)] is 20% faster, but has
cumulative error O(n) instead of O(1) *)letfsumt=matchgettwith|None->0.|Somei->let sum=refiinletc=ref0.initer(funx->lety=x-.!cinlett=!sum+.yinc:=(t-.!sum)-.y;sum:=t)t;!sumletkahan_sum =fsum(* NEED A PROPER TEST OF ROUNDING ERROR *)(*$T fsum
let arr = Array.make 10001 1e-10 in arr.(0) <- 1e10; \
Float.approx_equal (fsum (Array.enum arr)) (1e10 +. 1e-5)
*)(*$T kahan_sum
kahan_sum (Array.enum [| |]) = 0.
kahan_sum (Array.enum [| 1.; 2. |]) = 3.
let n, x = 1_000, 1.1 in \
Float.approx_equal (float n *. x) \
(kahan_sum (Array.enum (Array.make n x)))
*)letexists ft=tryletrec aux()=f(t.next())|| aux()in aux()withNo_more_elements->falseletfor_all ft=tryletrec aux()=f(t.next())&& aux()in aux()withNo_more_elements->true(* test paired elements, ignore any extra elements from one enum *)letfor_all2ft1t2=tryletrec aux()=f(t1.next())(t2.next())&& aux()inaux()withNo_more_elements->trueletscanlfinitt=let acc=refinitinlet gen()=acc:=f!acc(t.next());!accinlete=fromgeninpusheinit;eletscanft=matchgettwith|Somex->scanlfxt|None->empty()letfoldifinitt=let acc=refinitinletrecloopidx=acc:=fidx(t.next())!acc;loop(idx+1)intryloop0withNo_more_elements->!accletfold2finittu=let acc=refinitinletpush_t =refNoneinletrecloop()=push_t :=None;lete=t.next()inpush_t :=Somee;acc:=fe(u.next())!acc;loop()intryloop()withNo_more_elements->match!push_twith|None->!acc|Somee->pushte;!accletfold2i finittu=let acc=refinitinletpush_t =refNoneinletrecloopidx=push_t :=None;lete=t.next()inpush_t :=Somee;acc:=fidxe(u.next())!acc;loop(idx+1)intryloop0withNo_more_elements->match!push_twith|None->!acc|Somee->pushte;!accletfindft=letrecloop()=letx=t.next()iniffxthenxelseloop()intryloop()withNo_more_elements->raiseNot_found(*$T
find ((=) 5) (1 -- 10) = 5
try ignore (find ((=) 11) (1 -- 10) = 5); false with Not_found -> true
*)letfind_map ft=letrecloop()=matchf(t.next())with|Somex->x|None->loop()intryloop()withNo_more_elements->raiseNot_found(*$T find_map
try let _ = empty () |> find_map (const (Some 1)) in false with Not_found -> true
singleton 0 |> find_map (const (Some 1)) = 1
1 -- 5 |> find_map (function 2 -> Some 0 | _ -> None) = 0
1 -- 5 |> find_map (function 5 -> Some 0 | _ -> None) = 0
try let _ = 1 -- 5 |> find_map (function 6 -> Some 0 | _ -> None) in \
false with Not_found -> true
*)(*qtest TODO: migrate try into an exception test *)letrecmapft={count=t.count;next=(fun()->f(t.next()));clone=(fun()->mapf(t.clone()));fast=t.fast;}letrecmapift=let idx=ref(-1)in{count=t.count;next=(fun()->incridx;f!idx(t.next()));clone=(fun()->mapif(t.clone()));fast=t.fast;}letrecfilter ft=letrecnext()=letx=t.next()iniffxthenxelsenext()infrom2next(fun()->filterf(t.clone()))letrecfilter_map ft=letrecnext()=matchf(t.next())with|None->next()|Somex->xinfrom2next(fun()->filter_mapf(t.clone()))letrecappend tatb=lett={count=(fun()->ta.count()+tb.count());next=_dummy;clone=(fun()->append(ta.clone())(tb.clone()));fast=ta.fast&&tb.fast;}int.next<-(fun()->tryta.next()withNo_more_elements->(* add one indirection because tb can mute *)t.next<-(fun()->tb.next());t.count<-(fun()->tb.count());t.clone<-(fun()->tb.clone());t.fast<-tb.fast;t.next());t(*$T
append (List.enum [1;2;3]) (List.enum [4;5]) |> List.of_enum = [1;2;3;4;5]
append (List.enum [1;2;3]) (List.enum [4;5]) |> \
mapi (Tuple2.curry identity) |> List.of_enum = [0,1;1,2;2,3;3,4;4,5]
*)letprefix_action ft=letfull_action e=e.count<-(fun()->t.count());e.next<-(fun()->t.next());e.clone<-(fun()->t.clone());f()inletrec t'={count=(fun()->full_actiont';t.count());next=(fun()->full_actiont';t.next());clone=(fun()->full_actiont';t.clone());fast=t.fast}int'letsuffix_action_without_raise (f:unit->'a)(t:'at)={count=t.count;next=(fun()->tryt.next()withNo_more_elements->f());clone=(fun()->t.clone());(* needs to be delayed because [t] may
mutate and we want the newest clone
function *)fast=t.fast}letsuffix_action ft=let f'()=f();closet;raiseNo_more_elementsinsuffix_action_without_raisef'tletrecconcat t=let tn=ref(empty())inletrecnext()=try(!tn).next()withNo_more_elements->tn:=t.next();next()inletclone()=append((!tn).clone())(concat(t.clone()))infrom2nextclone(*$T concat
let e = List.enum [ [| 1; 2; 3; 4|]; [| 5; 6 |] ] |> map Array.enum \
|> concat in drop 1 e; (count e) = (count (clone e))
*)letsingleton x=init1(fun_->x)letswitchnnfe=letqueues =ArrayLabels.initn~f:(fun_->Queue.create ())inletgeni()=(*Generate the next value for the i^th enum*)letmy_queue =queues.(i)inifQueue.is_empty my_queue then(*Need to fetch next*)letrec aux()=(*Keep fetching until an appropriate
item has been found*)letnext_item =e.next()inletposition=fnext_item inifi=position thennext_itemelse(Queue.pushnext_item queues.(position);aux())in aux()elseQueue.takemy_queueinArrayLabels.init~f:(funi->from(geni))nletswitch fe=leta=switchn2(funx->iffxthen0else1)ein(a.(0),a.(1))(*$T
List.enum [1;2;3;4] |> switch (fun x -> x mod 2 = 0) |> \
Tuple2.mapn List.of_enum = ([2;4], [1;3])
*)letpartition =switch(*$T partition
let a,b = partition (fun x -> x > 3) (List.enum [1;2;3;4;5;1;5;0]) in \
List.of_enum a = [4;5;5] && List.of_enum b = [1;2;3;1;0]
*)(*$Q partition
(Q.list Q.small_int) (fun l -> let f x = x mod 2 = 1 in List.partition f l \
= (partition f (List.enum l) |> Tuple.Tuple2.mapn List.of_enum))
*)letseqinitfcond=let acc=refinitinlet aux()=ifcond!accthenbeginletresult =!accinacc:=f!acc;resultendelseraiseNo_more_elementsinfromauxletrepeat ?timesx=matchtimeswith|None->letrec aux={count=return_infinite_count;next=(fun()->x);clone=(fun()-> aux);fast=true;}in aux|Somen->initn(fun_->x)(*$T
repeat ~times:5 0 |> List.of_enum = [0;0;0;0;0]
repeat 1 |> take 3 |> List.of_enum = [1;1;1]
*)letcycle?timesx=letenum=matchtimeswith|None->from(fun()->clonex)|Somen->initn(fun_->clonex)inconcatenum(*$T
cycle ~times:5 (singleton 1) |> List.of_enum = [1;1;1;1;1]
cycle (List.enum [1;2]) |> take 5 |> List.of_enum = [1;2;1;2;1]
*)letrange?untilx=letcond=matchuntilwith|None->(fun_->true)|Somen->(funm->m<=n)inseqx((+)1)cond(*$T
range 1 ~until:5 |> List.of_enum = [1;2;3;4;5]
*)letdropne=for_i=1tondojunkedoneletskipne=dropne;eletdrop_while pe=letrec aux()=matchgetewith|Somexwhenpx-> aux()|Somex->pushex|None->()inprefix_actionauxe(*let drop_while p e =
let rec aux () =
let x = e.next () in
print_string "filtering\n";
if p x then (aux ())
else (push e x;
raise No_more_elements)
in
append (from aux) e*)lettake_while ft=letnext()=letx=t.next()iniffxthenxelse(pushtx;raiseNo_more_elements)infromnextletspanft=(*Two possibilities: either the tail has been read
already -- in which case all head data has been
copied onto the queue -- or the tail hasn't been
read -- in which case, stuff should be read from
[t] *)letqueue=Queue.create ()andread_from_queue =reffalseinlethead()=if!read_from_queue then(*Everything from the head has been copied *)tryQueue.takequeue(*to the queue already *)withQueue.Empty->raiseNo_more_elementselseletx=t.next()iniffxthenxelse(pushtx;raiseNo_more_elements)andtail()=ifnot!read_from_queue then(*Copy everything to the queue *)beginread_from_queue :=true;letrec aux()=matchgettwith|None->raiseNo_more_elements|Somexwhenfx->Queue.pushxqueue;aux()|Somex->xin aux()endelset.next()in(fromhead,fromtail)(*$T span
List.enum [1;2;3;4;5] |> span (fun x-> x<4) |> Tuple2.mapn List.of_enum = \
([1;2;3], [4;5])
*)(*$Q
(Q.list Q.small_int) (fun l -> \
let avg = List.fold_left (+) 0 l / (max 1 @@ List.length l) in \
let l' = List.sort Int.compare l in \
let f x = x < avg in \
Tuple2.mapn List.of_enum (span f @@ List.enum l' ) = \
(List.of_enum @@ take_while f @@ List.enum l', \
List.of_enum @@ drop_while f @@ List.enum l'))
*)letwhile_docontfe=let(head,tail)=spanconteinappend(fhead)tailletbreakteste=span(funx-> not(testx))eletuniq_bycmpe=matchpeekewithNone->empty()|Somefirst->letprev=reffirstinletnot_lastx=not(cmp(BatRef.postprev(fun_->x))x)inletresult =filternot_lasteinpushresult first;resultletuniqe=uniq_by(=)eletuniqqe=uniq_by(==)e(*$T
List.enum [1;1;2;3;3;2] |> uniq |> List.of_enum = [1;2;3;2]
List.enum [1;1;2;3;3;2] |> uniqq |> List.of_enum = [1;2;3;2]
List.enum ["a";"a";"b";"c";"c";"b"] |> uniq |> List.of_enum = ["a";"b";"c";"b"]
List.enum ["a";"A";"b";"c";"C";"b"] \
|> uniq_by (fun a b -> String.lowercase_ascii a = String.lowercase_ascii b) \
|> List.of_enum = ["a";"b";"c";"b"]
*)letdupt=(t,t.clone())(*$Q
(Q.list Q.small_int) (fun l -> \
List.enum l |> dup |> Tuple2.mapn List.of_enum |> Tuple2.uncurry (=))
*)letmin_count xy=letcountx=trySome(x.count())withInfinite_enum->Noneinmatchcountx,countywith|None,None->raiseInfinite_enum|Somec,None|None,Somec->c|Somec1,Somec2->min c1c2letcombine xy=ifx.fast&&y.fastthen(* Optimized case *)letrec aux(x,y)={count=(fun()->min_countxy);next=(fun()->(x.next(),y.next()));clone=(fun()-> aux(x.clone(),y.clone()));fast=true}in aux(x,y)elsefrom(fun()->(x.next(),y.next()))(*$T
combine (List.enum [1;2;3]) ( List.enum ["a";"b"]) \
|> List.of_enum = [1, "a"; 2, "b"]
combine (List.enum [1;2;3]) ( repeat "a") \
|> List.of_enum = [1,"a"; 2,"a"; 3,"a"]
combine (List.enum [1;2;3]) ( repeat "a") \
|> Enum.count = 3
*)letuncombine e=letadvance =ref`firstandqueue_snd =Queue.create ()andqueue_fst =Queue.create ()inletfirst()=match!advancewith|`first->let(x,y)=e.next()inQueue.pushyqueue_snd;x|`second->(*Second element has been read further*)tryQueue.popqueue_fstwithQueue.Empty->let(x,y)=e.next()inQueue.pushyqueue_snd;advance :=`first;xandsecond()=match!advancewith|`second->let(x,y)=e.next()inQueue.pushxqueue_fst;y|`first->(*Second element has been read further*)tryQueue.popqueue_sndwithQueue.Empty->let(x,y)=e.next()inQueue.pushxqueue_fst;advance :=`second;yin(fromfirst,fromsecond)(*$R uncombine
let pair_list = [1,2;3,4;5,6;7,8;9,0] in
let a,b = uncombine (BatList.enum pair_list) in
let a = BatArray.of_enum a in
let b = BatArray.of_enum b in
let c,d = uncombine (BatList.enum pair_list) in
let d = BatArray.of_enum d in
let c = BatArray.of_enum c in
let aeq = assert_equal ~printer:(BatIO.to_string (BatArray.print BatInt.print)) in
aeq a [|1;3;5;7;9|];
aeq b [|2;4;6;8;0|];
aeq a c;
aeq b d
*)letgroup_auxtesteqe=letprev_group =ref(empty())inletf()=(* Make sure elements belonging to prev group are consumed from e *)force!prev_group;let grp=letlast_test =refNoneinletcheck_test t=let ok=match!last_testwith|None->true|Somet'->eqt'tinifokthenlast_test :=Somet;okintake_while(funx->check_test (testx))einifis_emptygrpthenraiseNo_more_elements;prev_group := grp;grpinletclone()=failwith "Grouped enumerations cannot be cloned safely"infrom2fcloneletgroupteste=group_auxtest(=)eletgroup_byeqe=group_aux(funx->x)eqe(*$T group
empty () |> group (const ()) |> is_empty
List.enum [1;2;3;4] |> group identity |> map List.of_enum \
|> List.of_enum = [[1];[2];[3];[4]]
List.enum [1;2;3;4] |> group (const true) |> List.of_enum \
|> List.map List.of_enum = [[1;2;3;4]]
List.enum [1;2;3;5;6;7;9;10;4;5] |> group (fun x -> x mod 2) |> List.of_enum \
|> List.map List.of_enum = [[1];[2];[3;5];[6];[7;9];[10;4];[5]]
*)letclumpclump_sizeaddgete=(* convert a uchar enum into a ustring enum *)letnext()=matchpeekewith|None->raiseNo_more_elements|Somex->addx;junke;(* don't get [x] twice *)(tryfor_i=2toclump_size doadd(e.next())donewithNo_more_elements->());get()infromnext(*$T clump
let l = RefList.empty() in \
Char.range 'a' ~until:'k' |> \
clump 4 (RefList.push l) \
(fun()-> String.implode \
(RefList.to_list l |> tap (fun _ -> RefList.clear l) |> List.rev)) \
|> List.of_enum = ["abcd"; "efgh"; "ijk"]
*)(* mutable state used for {!cartesian_product}. Use a module to have a private namespace. *)moduleProductState=structtype('a,'b)current_state=|GetLeft|GetRight|GetRightOrStop|Stop|ProdLeftof'a*'blist|ProdRightof'b*'alisttype('a,'b)t={e1:'aenumerable;e2:'benumerable;mutableall1:'alist;mutableall2:'blist;mutablecur:('a,'b)current_state;}endletcartesian_product e1e2=letopenProductStatein(* sketch of the algo: state machine that alternates between taking a
new element from [e1] and yield its product with [state.all2], and
taking a new element from [e2] and make its product with [state.all1]
[state.cur]: current state of automaton, i.e., what we have to do next.
Can be `Stop,
`GetLeft/`GetRight (to obtain next element from first/second generator),
or `ProdLeft/`ProdRIght to compute the product of an element with a list
of already met elements *)letrecnextstate()=matchstate.curwith|Stop->raiseNo_more_elements|GetLeft->let x1=trySome(state.e1.next())withNo_more_elements->Noneinbeginmatchx1with|None->state.cur<-GetRightOrStop|Somex->state.all1<-x::state.all1;state.cur<-ProdLeft(x,state.all2)end;nextstate()|GetRight|GetRightOrStop->let x2=trySome(state.e2.next())withNo_more_elements->Noneinbeginmatchx2,state.curwith|None,GetRightOrStop->state.cur<-Stop;raiseNo_more_elements|None,GetRight->state.cur<-GetLeft|Somey,_->state.all2<-y::state.all2;state.cur<-ProdRight(y,state.all1)|None,_->assertfalseend;nextstate()|ProdLeft(_,[])->state.cur<-GetRight;nextstate()|ProdLeft(x,y::l)->state.cur<-ProdLeft(x,l);x,y|ProdRight(_,[])->state.cur<-GetLeft;nextstate()|ProdRight(y,x::l)->state.cur<-ProdRight(y,l);x,yandclonestate()=letstate' ={statewithe1=state.e1.clone();e2=state.e2.clone();}in_makestate'andcountstate()=let n1=state.e1.count()and n2=state.e2.count()in(* 3 products to make: e1 with e2, and ei with all{2-i} for i in {1,2} *)letn=n1*n2+n1*List.length state.all2+n2*List.length state.all1inmatchstate.curwith|ProdRight(_,l)->n+List.length l|ProdLeft(_,l)->n+List.length l|Stop->0|GetLeft|GetRight|GetRightOrStop->n(* build enum from the state *)and_makestate={next=nextstate;clone=clonestate;count=countstate;fast=state.e1.fast&&state.e2.fast;}inletstate={e1;e2;cur=GetLeft;all1=[];all2=[]}in_makestate(*$T cartesian_product
cartesian_product (List.enum [1;2;3]) (List.enum ["a";"b"]) \
|> List.of_enum |> List.sort Legacy.compare = \
[1,"a"; 1,"b"; 2,"a"; 2,"b"; 3,"a"; 3,"b"]
let e = cartesian_product (List.enum [1;2;3]) (List.enum [1]) in \
e |> List.of_enum |> List.sort Legacy.compare = [1,1; 2,1; 3,1]
let e = cartesian_product (List.enum [1]) (List.enum [1;2;3]) in \
e |> List.of_enum |> List.sort Legacy.compare = [1,1; 1,2; 1,3]
let e = cartesian_product (List.enum [1;2;3]) (List.enum [1;2;3]) in \
ignore (Enum.get e); Enum.count e = 8
let e = cartesian_product (List.enum [1;2]) (Enum.repeat 3) in\
e |> Enum.take 4 |> Enum.map fst |> List.of_enum \
|> List.sort Legacy.compare = [1; 1; 2; 2]
let e = cartesian_product (Enum.repeat 3) (List.enum [1;2]) in\
e |> Enum.take 4 |> Enum.map snd |> List.of_enum \
|> List.sort Legacy.compare = [1; 1; 2; 2]
let e = cartesian_product (Enum.repeat 3) (Enum.repeat "a") in\
e |> Enum.take 3 |> List.of_enum \
|> List.sort Legacy.compare = [3, "a"; 3, "a"; 3, "a"]
*)(*$Q cartesian_product
Q.(pair (list small_int) (list small_int)) \
(fun (l1,l2) -> \
let l1 = List.take 5 l1 in \
let l2 = List.take 4 l2 in \
cartesian_product (List.enum l1) (List.enum l2) |> count = \
List.length l1 * List.length l2)
Q.(pair (list small_int) (list small_int)) \
(fun (l1,l2) -> \
let l1 = List.take 5 l1 in \
let l2 = List.take 4 l2 in \
cartesian_product (List.enum l1) (List.enum l2) \
|> List.of_enum |> List.length = List.length l1 * List.length l2)
*)letfrom_while f=from(fun()->matchf()with|None->raiseNo_more_elements|Somex->x)letfrom_loopdatanext=letr=refdatainfrom(fun()->let(a,b)=next!rinr:=b;a)letunfolddatanext=from_loopdata(fundata->matchnextdatawith|None->raiseNo_more_elements|Somex->x)letarg_min fenum=matchgetenumwithNone->invalid_arg"Enum.arg_min: Empty enum"|Somev->letitem,eval=refv,ref(fv)initer(funv->let fv=fviniffv<!evalthen(item:=v;eval:=fv))enum;!itemletarg_max fenum=matchgetenumwithNone->invalid_arg"Enum.arg_max: Empty enum"|Somev->letitem,eval=refv,ref(fv)initer(funv->let fv=fviniffv>!evalthen(item:=v;eval:=fv))enum;!item(*$T arg_max
List.enum ["cat"; "canary"; "dog"; "dodo"; "ant"; "cow"] \
|> arg_max String.length = "canary"
*)(*$T arg_min
-5 -- 5 |> arg_min (fun x -> x * x + 6 * x - 5) = -3
*)moduleInfix=structlet(--)xy=rangex~until:ylet(--.)(a,step)b=letn=int_of_float((b-.a)/.step)+1inifn<0thenempty()elseinitn(funi->float_of_inti*.step+.a)let(--^)xy=rangex~until:(y-1)let(---)xy=ifx<=ythenx--yelseseqx((+)(-1))((<=)y)let(--~)ab=mapChar.chr(range(Char.codea)~until:(Char.codeb))let(//)ef=filterfelet(/@)ef=mapfelet(@/)=maplet(//@)ef=filter_mapfelet(@//)=filter_mapendincludeInfix(* -----------
Concurrency
*)letappend_from ab=lett=from(fun()->a.next())inletf()=letresult =b.next()int.next<-(fun()->b.next());resultinsuffix_action_without_raiseftletmergetestab=ifis_emptyathenbelseifis_emptybthenaelseletnext_a =ref(a.next())andnext_b =ref(b.next())inlet aux()=let(n,na,nb)=iftest!next_a!next_bthentry(!next_a,a.next(),!next_b)withNo_more_elements->(*a is exhausted, b probably not*)pushb!next_b;pushb!next_a;raiseNo_more_elementselsetry(!next_b,!next_a,b.next())withNo_more_elements->(*b is exhausted, a probably not*)pusha!next_a;pusha!next_b;raiseNo_more_elementsinnext_a :=na;next_b :=nb;ninappend_from(append_from(fromaux)a)b(*$T
let a=List.enum [1;3;5] and b = List.enum[2;4] in \
let test = let r = ref false in (fun _ _ -> r:= not !r; !r) in \
merge test a b |> List.of_enum = [1;2;3;4;5]
*)(*let mergen test a =
ArrayLabels.fold_left ~init:[]
~f:(fun x ->
let Array.of_list a
let next = Array.map
let rec aux =
if Array.length !next = 1 then (*we're done*)
if *)letinterleave enums=letenums_len =Array.length enumsinif not(enums_len >0)thenempty()elsebeginletavailable =Array.makeenums_len trueandnext_idx =Array.initenums_len((+)1)innext_idx.((Array.length next_idx)-1)<-0;letrecnext_elem idx=matchgetenums.(idx)with|Somex->x,next_idx.(idx)|None->beginavailable.(idx)<-false;letrecloopk=letl=next_idx.(k)inifl=idxthenraiseNo_more_elementselseifavailable.(l)then(next_idx.(idx)<-l;next_elem l)elselooplinloopidxendinfrom_loop0next_elemend(*$T interleave
let e1 = List.enum [ 8 ; 2 ; 5 ; 2 ] and e2 = List.enum [ -5 ; -7 ; -6 ; 2 ; 1 ; -9 ; 2 ] in \
let e = interleave [| e1 ; e2 |] in \
List.of_enum e = [ 8 ; -5 ; 2 ; -7 ; 5 ; -6 ; 2 ; 2 ; 1 ; -9 ; 2 ]
*)(*$R interleave
let e1 = Enum.empty ()
and e2 = List.enum [ 8 ; 2 ; 5 ; 2 ]
and e3 = List.enum [ -5 ; -7 ; -6 ; 2 ; 1 ; -9 ; 2 ] in
let e = interleave [| e1; e2 ; e3 |] in
assert_equal (List.of_enum e) [ 8 ; -5 ; 2 ; -7 ; 5 ; -6 ; 2 ; 2 ; 1 ; -9 ; 2 ]
*)(*$R interleave
let e1 = Enum.empty ()
and e2 = Enum.empty ()
and e3 = Enum.empty () in
let e = interleave [| e1; e2 ; e3 |] in
assert_equal (List.of_enum e) [ ]
*)letslazyf=letconstructor =lazy(f())inmake~next:(fun()->(Lazy.forceconstructor).next())~count:(fun()->(Lazy.forceconstructor).count())~clone:(fun()->(Lazy.forceconstructor).clone())letdelay=slazyletcombination ?(repeat=false)nk=letbinomial np=letbinomnp=ifp<0||n<0||p>nthen0else(leta=ref1infori=1topdoa:=!a*(n+1-i)/idone;!a)andcomp=n-pinif(comp<p)thenbinomncompelsebinomnpandadd_repetitions =letrecconvrangeacc=function|[]-> acc|h::tl->conv(range+1)((h-range):: acc)tlinconv0[]inletorder_to_combnprepeat ord=letrecget_combnpord acc=ifn<=0||p<=0||ord<0thenaccelse(letb=binomial(n-1)(p-1)iniford<bthenget_comb(n-1)(p-1)ord(n::acc)elseget_comb(n-1)p(ord-b)acc)inletresult =get_combnpord[]inifrepeatthenadd_repetitions resultelseresultandp=ifrepeat thenn+k-1elseninletlength =binomial pkinletrecmake_comb index=make~next:(fun()->if!index=lengththenraiseNo_more_elementselseletnext=order_to_combpkrepeat !indexinincrindex;next)~count:(fun()->length -!index)~clone:(fun()->make_comb(ref!index))inmake_comb(ref0)(*$T combination
(combination 3 3 |> count) = 1
(combination ~repeat:true 3 3 |> count) = 10
(combination ~repeat:true 29 3 |> count) = 4495
(combination ~repeat:true 3 3 |> List.of_enum ) = \
[ [3; 3; 3]; [3; 3; 2]; [3; 3; 1]; [3; 2; 2]; [3; 2; 1]; [3; 1; 1]; \
[2; 2; 2]; [2; 2; 1]; [2; 1; 1]; \
[1; 1; 1]; ]
*)letlsingf=init1(fun_->f())letlconsfe=append(lsingf)eletlappfe=append(slazyf)eletising=singletonleticonsfe=append(isingf)eletiapp=appendlethard_count t=ift.fastthenletresult =t.count()incloset;resultelse(*Counting would cache stuff, which we don't want here.*)letlength =ref0intrywhiletruedoignore(t.next());incrlength done;assertfalsewithNo_more_elements->!length(* common hidden function for print and print_at_most *)let_print_common ~first~last~sep~limitprint_aoute=BatInnerIO.nwriteoutfirst;matchgetewith|None->BatInnerIO.nwriteoutlast|Somex->print_aoutx;letrecauxlimit=matchgete,limitwith|None,_->BatInnerIO.nwriteoutlast|Some_,0->BatInnerIO.nwrite out"...";BatInnerIO.nwriteoutlast|Somex,_->BatInnerIO.nwriteout sep;print_aoutx;aux(limit-1)in aux(limit-1)letprint?(first="")?(last="")?(sep=" ")print_aoute=_print_common~first~last~sep~limit:max_intprint_aouteletprint_at_most ?(first="")?(last="")?(sep=" ")~limitprint_aoute=iflimit<=0theninvalid_arg "Enum.print_at_most";_print_common~first~last~sep~limitprint_aoute(*$T print_at_most
Printf.sprintf2 "yolo %a" (print_at_most ~limit:3 Int.print) \
(range 0 ~until:10) = "yolo 0 1 2..."
*)lett_printer a_printer _parenoute=print~first:"["~sep:"; "~last:"]"(a_printer false)outeletcomparecmptu=letrec aux()=match(gett,getu)with|(None,None)->0|(None,_)->-1|(_,None)->1|(Somex,Somey)->matchcmpxywith|0-> aux()|n->nin aux()letordord_val tu=letcmp_val =BatOrd.compord_val inBatOrd.ord0(comparecmp_valtu)letequaleqtu=letrec aux()=match(gett,getu)with|(None,None)->true|(Somex,Somey)->eqxy&& aux()|_->falsein aux()(*$Q
(Q.list Q.small_int) (fun l -> \
let e = List.enum l in equal Int.equal e (clone e))
*)letrecto_object t=objectmethodnext=t.next()methodcount=counttmethodclone=to_object(clonet)endletrecof_object o=make~next:(fun()->o#next)~count:(fun()->o#count)~clone:(fun()->of_object(o#clone))letflatten =concat(*$T
flatten (map singleton @@ List.enum [1;2;3]) |> List.of_enum = [1;2;3]
*)letrecconcat_map ft=let tn=ref(empty())inletrecnext()=try(!tn).next()withNo_more_elements->tn:=f(t.next());next()inletclone()=append((!tn).clone())(concat_mapf(t.clone()))infrom2nextclone(*$T concat_map
(1 -- 10 |> concat_map (fun x -> List.enum [x;-x]) |> sum) = 0
let e = (1 -- 10 |> concat_map (fun x -> List.enum [x;-x])) in \
let n = Enum.count e in \
n = (List.of_enum e |> List.length)
let e = (1 -- 10 |> concat_map (fun x -> List.enum [x;-x])) in \
Enum.count e = 20
*)(*$Q concat_map
Q.small_int (fun i -> \
let i = abs i in \
equal (=) (0 -- i) (concat_map singleton (0 -- i)))
*)moduleExceptionless=structletfindfe=trySome(findfe)withNot_found->NoneendmoduleLabels=structletiter~fx=iterfxletiter2~fxy=iter2fxyletiteri~fx=iterifxletiter2i~fxy=iter2ifxyletfor_all~ft=for_allftletexists~ft=existsftletfold~f~initx=foldfinitxletfold2~f~initxy=fold2finitxyletfoldi~f~initx=foldifinitxletfold2i~f~initxy=fold2ifinitxyletfind~fx=findfxlet map~fx=mapfxletmapi~fx=mapifxletfilter~fx=filterfxletfilter_map~fx=filter_mapfxletinitx~f=initxfletswitch ~f=switchflettake_while ~f=take_whilefletdrop_while ~f=drop_whilefletfrom~f=fromfletfrom_loop ~init~f=from_loopinitfletfrom_while ~f=from_whileflet seq~init~f~cnd=seqinitfcndletunfold ~init~f=unfoldinitfletcompare ?(cmp=Pervasives.compare)tu=comparecmptuletuniq?(cmp=(=))x=uniq_bycmpxmoduleLExceptionless=structincludeExceptionlessletfind~fe=findfeendendmoduletypeEnumerable=sigtype'aenumerablevalenum:'aenumerable->'atvalof_enum:'at->'aenumerableendmoduleWithMonad(Mon:BatInterfaces.Monad)=structtype'am='aMon.mletsequenceenum=let(>>=)=Mon.bindandreturn =Mon.returnin(* We use a list as an accumulator for the result sequence
computed under the monad. A previous version of this code used
a Queue instead, which was problematic for backtracking
monads. Due to the destructive nature of Enums, the current
version will still be problematic but at least the result will
be consistent. *)letof_acc acc=(* we don't use List functions to avoid creating a cyclic
dependency *)let li=ref(List.rev acc)infrom(fun()->match!liwith|[]->raiseNo_more_elements|hd::tl->li:=tl;hd)inletrecloopacc=matchgetenumwith|None->return(of_accacc)|Someelem->elem>>=(funx->loop(x:: acc))inloop[]letfold_monad finitenum=let(>>=)=Mon.bindandreturn =Mon.returninletrecfoldm=matchgetenumwith|None->m|Somex->m>>=fun acc->fold(faccx)infold(returninit)endmoduleMonad=structtype'am='atletreturn x=singletonxletbindmf=concat(mapfm)end(*$T
equal (=) (Monad.return 1) (singleton 1)
equal (=) (Monad.bind (List.enum [1;2]) (fun x-> List.enum [x+1;x])) \
(List.enum [2;1;3;2])
*)(*$Q
(Q.list Q.small_int) (fun l -> \
let id l = Monad.bind l Monad.return in \
List.enum l |> id |> List.of_enum = l)
*)