1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095(* This file is free software, part of gen. See file "license" for more details. *)(** {1 Restartable generators} *)(** {2 Global type declarations} *)type'at=unit->'aoptiontype'agen='atmoduletypeS=Gen_intf.S(*$inject
[@@@ocaml.warning "-26"]
let pint i = string_of_int i
let pilist l =
let b = Buffer.create 15 in
let fmt = Format.formatter_of_buffer b in
Format.fprintf fmt "%a@?"
(Gen.pp Format.pp_print_int) (Gen.of_list l);
Buffer.contents b
let pi2list l =
let b = Buffer.create 15 in
let fmt = Format.formatter_of_buffer b in
Format.fprintf fmt "%a@?"
(Gen.pp (fun fmt (a,b) -> Format.fprintf fmt "%d,%d" a b))
(Gen.of_list l);
Buffer.contents b
let pstrlist l =
let b = Buffer.create 15 in
let fmt = Format.formatter_of_buffer b in
Format.fprintf fmt "%a@?"
(Gen.pp Format.pp_print_string) (Gen.of_list l);
Buffer.contents b
*)(** {2 Transient generators} *)letempty()=None(*$T empty
empty |> to_list = []
*)letsingletonx=letfirst=reftrueinfun()->if!firstthen(first:=false;Somex)elseNone(*$T singleton
singleton 1 |> to_list = [1]
singleton "foo" |> to_list = ["foo"]
*)(*$R
let gen = Gen.singleton 42 in
OUnit.assert_equal (Some 42) (Gen.get gen);
OUnit.assert_equal None (Gen.get gen);
let gen = Gen.singleton 42 in
OUnit.assert_equal 1 (Gen.length gen);
*)letreturn=singletonletrepeatx()=Somex(*$T repeat
repeat 42 |> take 3 |> to_list = [42; 42; 42]
*)letrepeatedlyf()=Some(f())(*$T repeatedly
repeatedly (let r = ref 0 in fun () -> incr r; !r) \
|> take 5 |> to_list = [1;2;3;4;5]
*)letiteratexf=letcur=refxinfun()->letx=!curincur:=f!cur;Somex(*$T iterate
iterate 0 ((+)1) |> take 5 |> to_list = [0;1;2;3;4]
*)letnextgen=gen()letgetgen=gen()letget_exngen=matchgen()with|Somex->x|None->raise(Invalid_argument"Gen.get_exn")(*$R get_exn
let g = of_list [1;2;3] in
assert_equal 1 (get_exn g);
assert_equal 2 (get_exn g);
assert_equal 3 (get_exn g);
assert_raises (Invalid_argument "Gen.get_exn") (fun () -> get_exn g)
*)letjunkgen=ignore(gen())letrecfoldfaccgen=matchgen()with|None->acc|Somex->foldf(faccx)gen(*$Q
(Q.list Q.small_int) (fun l -> \
of_list l |> fold (fun l x->x::l) [] = List.rev l)
*)letreducefg=letacc=matchg()with|None->raise(Invalid_argument"reduce")|Somex->xinfoldfaccg(* Dual of {!fold}, with a deconstructing operation *)letunfoldfacc=letacc=refaccinfun()->matchf!accwith|None->None|Some(x,acc')->acc:=acc';Somex(*$T unfold
unfold (fun (prev,cur) -> Some (prev, (cur,prev+cur))) (0,1) \
|> take 7 |> to_list = [0; 1; 1; 2; 3; 5; 8]
*)letinit?(limit=max_int)f=letr=ref0infun()->if!r>=limitthenNoneelseletx=f!rinlet_=incrrinSomex(*$T init
init ~limit:5 (fun i->i) |> to_list = [0;1;2;3;4]
*)letreciterfgen=matchgen()with|None->()|Somex->fx;iterfgen(*$R iter
let e = Restart.(1 -- 10) in
OUnit.assert_equal ~printer:pint 10 (Restart.length e);
OUnit.assert_equal [1;2] Restart.(to_list (1 -- 2));
OUnit.assert_equal [1;2;3;4;5] (Restart.to_list (Restart.take 5 e));
*)letiterifgen=letreciterii=matchgen()with|None->()|Somex->fix;iteri(i+1)initeri0letis_emptygen=matchgen()with|None->true|Some_->false(*$T
is_empty empty
not (is_empty (singleton 2))
*)letlengthgen=fold(funacc_->acc+1)0gen(*$Q
(Q.list Q.small_int) (fun l -> \
of_list l |> length = List.length l)
*)(* useful state *)moduleRunState=structtype'at=|Init|Runof'a|Stopendletscanfaccg=letopenRunStateinletstate=refInitinfun()->match!statewith|Init->state:=Runacc;Someacc|Stop->None|Runacc->matchg()with|None->state:=Stop;None|Somex->letacc'=faccxinstate:=Runacc';Someacc'(*$T scan
scan (fun acc x -> x+1::acc) [] (1--5) |> to_list \
= [[]; [2]; [3;2]; [4;3;2]; [5;4;3;2]; [6;5;4;3;2]]
*)letunfold_scanfaccg=letopenRunStateinletstate=ref(Runacc)infun()->match!statewith|Init->assertfalse|Stop->None|Runacc->matchg()with|None->state:=Stop;None|Somex->letacc',y=faccxinstate:=Runacc';Somey(*$T unfold_scan
unfold_scan (fun acc x -> x+acc,acc) 0 (1--5) |> to_list \
= [0; 1; 3; 6; 10]
*)(** {3 Lazy} *)letmapfgen=letstop=reffalseinfun()->if!stopthenNoneelsematchgen()with|None->stop:=true;None|Somex->Some(fx)(*$Q map
(Q.list Q.small_int) (fun l -> \
let f x = x*2 in \
of_list l |> map f |> to_list = List.map f l)
*)(*$R
let e = 1 -- 10 in
let e' = e >>| string_of_int in
OUnit.assert_equal ~printer:pstrlist ["9"; "10"] (Gen.to_list (Gen.drop 8 e'));
*)letmapif=letcnt=ref0inletcnt_mapx=leti=!cntincnt:=i+1;fixinmapcnt_map(*$Q mapi
(Q.list Q.small_int) (fun l -> \
let len = List.length l in \
let f i x = i+x+1 in \
of_list l |> mapi f |> to_list |> fun l' -> List.fold_left (+) 0 l'= \
len*(len+1)/2 + List.fold_left (+) 0 l)
*)letfold_mapfsgen=map(letstate=refsinfunx->state:=f(!state)x;!state)gen(*$T
fold_map (+) 0 (1--3) |> to_list = [1;3;6]
*)letappendgen1gen2=letfirst=reftrueinfun()->if!firstthenmatchgen1()with|(Some_)asx->x|None->first:=false;gen2()elsegen2()(*$Q
(Q.pair (Q.list Q.small_int)(Q.list Q.small_int)) (fun (l1,l2) -> \
append (of_list l1) (of_list l2) |> to_list = l1 @ l2)
*)(*$R
let e = Gen.append (1 -- 5) (6 -- 10) in
OUnit.assert_equal [10;9;8;7;6;5;4;3;2;1] (Gen.to_rev_list e);
*)letflattennext_gen=letopenRunStateinletstate=refInitin(* get next element *)letrecnext()=match!statewith|Init->get_next_gen()|Rungen->beginmatchgen()with|None->get_next_gen()|(Some_)asx->xend|Stop->Noneandget_next_gen()=matchnext_gen()with|None->state:=Stop;None|Somegen->state:=Rungen;next()innextletflat_mapfnext_elem=letopenRunStateinletstate=refInitinletrecnext()=match!statewith|Init->get_next_gen()|Rungen->beginmatchgen()with|None->get_next_gen()|(Some_)asx->xend|Stop->Noneandget_next_gen()=matchnext_elem()with|None->state:=Stop;None|Somex->state:=Run(fx);next()|exceptione->state:=Stop;raiseeinnext(*$Q flat_map
(Q.list Q.small_int) (fun l -> \
let f x = of_list [x;x*2] in \
eq (map f (of_list l) |> flatten) (flat_map f (of_list l)))
*)(*$T
flat_map (fun x -> if x mod 1_500_000=0 then singleton x else empty) (1 -- 6_000_000) \
|> to_list = [1_500_000; 3_000_000; 4_500_000; 6_000_000]
*)(*$R
let e = 1 -- 3 in
let e' = e >>= (fun x -> x -- (x+1)) in
OUnit.assert_equal [1;2;2;3;3;4] (Gen.to_list e');
*)letmem?(eq=(=))xgen=letrecmemeqxgen=matchgen()with|Somey->eqxy||memeqxgen|None->falseinmemeqxgenlettakengen=assert(n>=0);letcount=ref0in(* how many yielded elements *)fun()->if!count=n||!count=~-1thenNoneelsematchgen()with|None->count:=~-1;None(* indicate stop *)|(Some_)asx->incrcount;x(*$Q
(Q.pair Q.small_int (Q.list Q.small_int)) (fun (n,l) -> \
of_list l |> take n |> length = Pervasives.min n (List.length l))
*)(* call [gen] at most [n] times, and stop *)letrec__dropngen=ifn=0then()elsematchgen()with|Some_->__drop(n-1)gen|None->()letdropngen=assert(n>=0);letdropped=reffalseinfun()->if!droppedthengen()elsebegin(* drop [n] elements and yield the next element *)dropped:=true;__dropngen;gen()end(*$Q
(Q.pair Q.small_int (Q.list Q.small_int)) (fun (n,l) -> \
let g1,g2 = take n (of_list l), drop n (of_list l) in \
append g1 g2 |> to_list = l)
*)letnthngen=assert(n>=0);__dropngen;matchgen()with|None->raiseNot_found|Somex->x(*$= nth & ~printer:string_of_int
4 (nth 4 (0--10))
8 (nth 8 (0--10))
*)(*$T
(try ignore (nth 11 (1--10)); false with Not_found -> true)
*)lettake_nthngen=assert(n>=1);leti=refninletrecnext()=matchgen()with|None->None|(Some_)asreswhen!i=n->i:=1;res|Some_->incri;next()innextletfilterpgen=letrecnext()=(* wrap exception into option, for next to be tailrec *)matchgen()with|None->None|(Somex)asres->ifpxthenres(* yield element *)elsenext()(* discard element *)innext(*$T
filter (fun x ->x mod 2 = 0) (1--10) |> to_list = [2;4;6;8;10]
*)lettake_whilepgen=letstop=reffalseinfun()->if!stopthenNoneelsematchgen()with|(Somex)asres->ifpxthenreselse(stop:=true;None)|None->stop:=true;None(*$T
take_while (fun x ->x<10) (1--1000) |> eq (1--9)
*)letfold_whilefsgen=letstate=refsinletrecconsumegen=matchgen()with|None->()|Somex->letacc,cont=f!statexinstate:=acc;matchcontwith|`Stop->()|`Continue->consumegeninconsumegen;!state(*$T
fold_while (fun acc b -> if b then acc+1, `Continue else acc, `Stop) 0 \
(of_list [true;true;false;true]) = 2
*)moduleDropWhileState=structtypet=|Stop|Drop|Yieldend(* state machine starts at Drop:
Drop:
- If next element doesn't satisfy predicate, goto yield
- if no more elements, goto stop
Yield:
- if there is a next element, yield it
- if no more elements, goto stop
Stop: just return None
*)letdrop_whilepgen=letopenDropWhileStateinletstate=refDropinletrecnext()=match!statewith|Stop->None|Drop->beginmatchgen()with|None->state:=Stop;None|(Somex)asres->ifpxthennext()else(state:=Yield;res)end|Yield->beginmatchgen()with|None->state:=Stop;None|Some_asres->resendinnext(*$T
drop_while (fun x-> x<10) (1--20) |> eq (10--20)
*)letfilter_mapfgen=(* tailrec *)letrecnext()=matchgen()with|None->None|Somex->matchfxwith|None->next()|(Some_)asres->resinnext(*$T
filter_map (fun x-> if x mod 2 = 0 then Some (string_of_int x) else None) (1--10) \
|> to_list = List.map string_of_int [2;4;6;8;10]
*)(*$R
let f x = if x mod 2 = 0 then Some (string_of_int x) else None in
let e = Gen.filter_map f (1 -- 10) in
OUnit.assert_equal ["2"; "4"; "6"; "8"; "10"] (Gen.to_list e);
*)letzip_indexgen=letr=ref~-1infun()->matchgen()with|None->None|Somex->incrr;Some(!r,x)(*$T
zip_index (1--5) |> to_list = [0,1; 1,2; 2,3; 3,4; 4,5]
*)letunzipgen=letstop=reffalseinletq1=Queue.create()inletq2=Queue.create()inletnext_left()=ifQueue.is_emptyq1thenif!stopthenNoneelsematchgen()with|Some(x,y)->Queue.pushyq2;Somex|None->stop:=true;NoneelseSome(Queue.popq1)inletnext_right()=ifQueue.is_emptyq2thenif!stopthenNoneelsematchgen()with|Some(x,y)->Queue.pushxq1;Somey|None->stop:=true;NoneelseSome(Queue.popq2)innext_left,next_right(*$T
unzip (of_list [1,2;3,4]) |> (fun (x,y)-> to_list x, to_list y) \
= ([1;3], [2;4])
*)(*$Q
(Q.list (Q.pair Q.small_int Q.small_int)) (fun l -> \
of_list l |> unzip |> (fun (x,y) -> to_list x,to_list y) = \
List.split l)
*)(* [partition p l] returns the elements that satisfy [p],
and the elements that do not satisfy [p] *)letpartitionpgen=letqtrue=Queue.create()inletqfalse=Queue.create()inletstop=reffalseinletrecnexttrue()=ifQueue.is_emptyqtruethenif!stopthenNoneelsematchgen()with|(Somex)asres->ifpxthenreselse(Queue.pushxqfalse;nexttrue())|None->stop:=true;NoneelseSome(Queue.popqtrue)andnextfalse()=ifQueue.is_emptyqfalsethenif!stopthenNoneelsematchgen()with|(Somex)asres->ifpxthen(Queue.pushxqtrue;nextfalse())elseres|None->stop:=true;NoneelseSome(Queue.popqfalse)innexttrue,nextfalse(*$T
partition (fun x -> x mod 2 = 0) (1--10) |> \
(fun (x,y)->to_list x, to_list y) = ([2;4;6;8;10], [1;3;5;7;9])
*)letrecfor_allpgen=matchgen()with|None->true|Somex->px&&for_allpgenletrecexistspgen=matchgen()with|None->false|Somex->px||existspgenletmin?(lt=funxy->x<y)gen=letfirst=matchgen()with|Somex->x|None->raise(Invalid_argument"min")infold(funminx->ifltxminthenxelsemin)firstgen(*$T
min (of_list [1;4;6;0;11; -2]) = ~-2
(try ignore (min empty); false with Invalid_argument _ -> true)
*)letmax?(lt=funxy->x<y)gen=letfirst=matchgen()with|Somex->x|None->raise(Invalid_argument"max")infold(funmaxx->ifltmaxxthenxelsemax)firstgen(*$T
max (of_list [1;4;6;0;11; -2]) = 11
(try ignore (max empty); false with Invalid_argument _ -> true)
*)leteq?(eq=(=))gen1gen2=letreccheck()=matchgen1(),gen2()with|None,None->true|Somex1,Somex2wheneqx1x2->check()|_->falseincheck()(*$Q
(Q.pair (Q.list Q.small_int)(Q.list Q.small_int)) (fun (l1,l2) -> \
eq (of_list l1)(of_list l2) = (l1 = l2))
*)letlexico?(cmp=Pervasives.compare)gen1gen2=letreclexico()=matchgen1(),gen2()with|None,None->0|Somex1,Somex2->letc=cmpx1x2inifc<>0thencelselexico()|Some_,None->1|None,Some_->-1inlexico()letcompare?cmpgen1gen2=lexico?cmpgen1gen2(*$Q
(Q.pair (Q.list Q.small_int)(Q.list Q.small_int)) (fun (l1,l2) -> \
let sign x = if x < 0 then -1 else if x=0 then 0 else 1 in \
sign (compare (of_list l1)(of_list l2)) = sign (Pervasives.compare l1 l2))
*)letrecfindpe=matche()with|None->None|Somexwhenpx->Somex|Some_->findpe(*$T
find (fun x -> x>=5) (1--10) = Some 5
find (fun x -> x>5) (1--4) = None
*)letsume=letrecsumacc=matche()with|None->acc|Somex->sum(x+acc)insum0(*$T
sum (1--10) = 55
*)(** {2 Multiple Iterators} *)letmap2fe1e2=fun()->matche1(),e2()with|Somex,Somey->Some(fxy)|_->None(*$T
map2 (+) (1--5) (1--4) |> eq (of_list [2;4;6;8])
map2 (+) (1--5) (repeat 0) |> eq (1--5)
*)letreciter2fe1e2=matche1(),e2()with|Somex,Somey->fxy;iter2fe1e2|_->()(*$T iter2
let r = ref 0 in iter2 (fun _ _ -> incr r) (1--10) (4--6); !r = 3
*)letrecfold2facce1e2=matche1(),e2()with|Somex,Somey->fold2f(faccxy)e1e2|_->accletrecfor_all2pe1e2=matche1(),e2()with|Somex,Somey->pxy&&for_all2pe1e2|_->trueletrecexists2pe1e2=matche1(),e2()with|Somex,Somey->pxy||exists2pe1e2|_->falseletzip_withfab=letstop=reffalseinfun()->if!stopthenNoneelsematcha(),b()with|Somexa,Somexb->Some(fxaxb)|_->stop:=true;Noneletzipab=zip_with(funxy->x,y)ab(*$Q
(Q.list Q.small_int) (fun l -> \
zip_with (fun x y->x,y) (of_list l) (of_list l) \
|> unzip |> fst |> to_list = l)
*)(*$R
let e = Gen.zip_with (+) (Gen.repeat 1) (4--7) in
OUnit.assert_equal [5;6;7;8] (Gen.to_list e);
*)(** {3 Complex combinators} *)moduleMergeState=structtype'at={gens:'agenQueue.t;mutablestate:my_state;}andmy_state=|NewGen(* obtain a new generator and push it in queue *)|YieldAndNew(* yield element from queue, then behave like NewGen *)|Yield(* just yield elements from queue *)|Stop(* no more elements *)end(* state machine starts at NewGen:
NewGen: use next_gen to push a new gen into the queue
Yield:
while the queue is not empty:
pop gen g from it
if g is empty continue
else:
pop element x from g
push g at back of queue
yield x
YieldAndNew: mix of Yield and NewGen.
if next_gen is exhausted, goto Yield;
if queue is empty, goto NewGen
Stop: do nothing
*)letmergenext_gen=letopenMergeStateinletstate={gens=Queue.create();state=NewGen;}in(* recursive function to get next element *)letrecnext()=matchstate.statewith|Stop->None|Yield->(* only yield from generators in state.gens *)ifQueue.is_emptystate.gensthen(state.state<-Stop;None)elseletgen=Queue.popstate.gensinbeginmatchgen()with|None->next()|(Some_)asres->Queue.pushgenstate.gens;(* put gen back in queue *)resend|NewGen->beginmatchnext_gen()with|None->state.state<-Yield;(* exhausted *)next()|Somegen->Queue.pushgenstate.gens;state.state<-YieldAndNew;next()end|YieldAndNew->(* yield element from queue, then get a new generator *)ifQueue.is_emptystate.gensthen(state.state<-NewGen;next())elseletgen=Queue.popstate.gensinbeginmatchgen()with|None->state.state<-NewGen;next()|(Some_)asres->Queue.pushgenstate.gens;state.state<-NewGen;resendinnext(*$T
merge (of_list [of_list [1;3;5]; of_list [2;4;6]; of_list [7;8;9]]) \
|> to_list |> List.sort Pervasives.compare = [1;2;3;4;5;6;7;8;9]
*)(*$R
let e = of_list [1--3; 4--6; 7--9] in
let e' = merge e in
OUnit.assert_equal [1;2;3;4;5;6;7;8;9]
(to_list e' |> List.sort Pervasives.compare);
*)letintersection?(cmp=Pervasives.compare)gen1gen2=letx1=ref(gen1())inletx2=ref(gen2())inletrecnext()=match!x1,!x2with|Somey1,Somey2->letc=cmpy1y2inifc=0(* equal elements, yield! *)then(x1:=gen1();x2:=gen2();Somey1)elseifc<0(* drop y1 *)then(x1:=gen1();next())else(* drop y2 *)(x2:=gen2();next())|_->Noneinnext(*$T
intersection (of_list [1;1;2;3;4;8]) (of_list [1;2;4;5;6;7;8;9]) \
|> to_list = [1;2;4;8]
*)letsorted_merge?(cmp=Pervasives.compare)gen1gen2=letx1=ref(gen1())inletx2=ref(gen2())infun()->match!x1,!x2with|None,None->None|(Somey1)asr1,((Somey2)asr2)->ifcmpy1y2<=0then(x1:=gen1();r1)else(x2:=gen2();r2)|(Some_)asr,None->x1:=gen1();r|None,((Some_)asr)->x2:=gen2();r(*$T
sorted_merge (of_list [1;2;2;3;5;10;100]) (of_list [2;4;5;6;11]) \
|> to_list = [1;2;2;2;3;4;5;5;6;10;11;100]
*)(*$R
[Gen.of_list [1;3;5]; Gen.of_list [0;1;1;3;4;6;10]; Gen.of_list [2;2;11]]
|> Gen.sorted_merge_n ?cmp:None
|> Gen.to_list
|> OUnit.assert_equal ~printer:pilist [0;1;1;1;2;2;3;3;4;5;6;10;11]
*)(** {4 Mutable heap (taken from heap.ml to avoid dependencies)} *)moduleHeap=structtype'at={mutabletree:'atree;cmp:'a->'a->int;}(** A pairing tree heap with the given comparison function *)and'atree=|Empty|Nodeof'a*'atree*'atreeletempty~cmp={tree=Empty;cmp;}letis_emptyh=matchh.treewith|Empty->true|Node_->falseletrecunion~cmpt1t2=matcht1,t2with|Empty,_->t2|_,Empty->t1|Node(x1,l1,r1),Node(x2,l2,r2)->ifcmpx1x2<=0thenNode(x1,union~cmpt2r1,l1)elseNode(x2,union~cmpt1r2,l2)letinserthx=h.tree<-union~cmp:h.cmp(Node(x,Empty,Empty))h.treeletpoph=matchh.treewith|Empty->raiseNot_found|Node(x,l,r)->h.tree<-union~cmp:h.cmplr;xendletsorted_merge_n?(cmp=Pervasives.compare)l=(* make a heap of (value, generator) *)letcmp(v1,_)(v2,_)=cmpv1v2inletheap=Heap.empty~cmpin(* add initial values *)List.iter(fungen'->matchgen'()with|Somex->Heap.insertheap(x,gen')|None->())l;fun()->ifHeap.is_emptyheapthenNoneelsebeginletx,gen=Heap.popheapinmatchgen()with|Somey->Heap.insertheap(y,gen);(* insert next value *)Somex|None->Somex(* gen empty, drop it *)end(*$T
sorted_merge_n [of_list [1;2;2;3;5;10;100]; of_list [2;4;5;6;11]; (6--10)] \
|> to_list = [1;2;2;2;3;4;5;5;6;6;7;8;9;10;10;11;100]
*)letround_robin?(n=2)gen=(* array of queues, together with their index *)letqs=Array.initn(fun_->Queue.create())inletcur=ref0in(* get next element for the i-th queue *)letrecnexti=letq=qs.(i)inifQueue.is_emptyqthenupdate_to_ii(* consume generator *)elseSome(Queue.popq)(* consume [gen] until some element for [i]-th generator is
available. *)andupdate_to_ii=matchgen()with|None->None|Somex->letj=!curincur:=(j+1)modn;(* move cursor to next generator *)letq=qs.(j)inifj=ithenbeginassert(Queue.is_emptyq);Somex(* return the element *)endelsebeginQueue.pushxq;update_to_ii(* continue consuming [gen] *)endin(* generators *)letl=Array.mapi(funi_->(fun()->nexti))qsinArray.to_listl(*$T
round_robin ~n:3 (1--12) |> List.map to_list = \
[[1;4;7;10]; [2;5;8;11]; [3;6;9;12]]
*)(*$R
let e = Restart.round_robin ~n:2 Restart.(1--10) in
match e with
| [a;b] ->
OUnit.assert_equal [1;3;5;7;9] (Gen.to_list a);
OUnit.assert_equal [2;4;6;8;10] (Gen.to_list b)
| _ -> OUnit.assert_failure "wrong list lenght"
*)(*$R
let e = Restart.round_robin ~n:3 Restart.(1 -- 999) in
let l = List.map Gen.length e in
OUnit.assert_equal [333;333;333] l;
*)(* Duplicate the enum into [n] generators (default 2). The generators
share the same underlying instance of the enum, so the optimal case is
when they are consumed evenly *)lettee?(n=2)gen=(* array of queues, together with their index *)letqs=Array.initn(fun_->Queue.create())inletfinished=reffalsein(* is [gen] exhausted? *)(* get next element for the i-th queue *)letrecnexti=ifQueue.is_emptyqs.(i)thenif!finishedthenNoneelseget_nexti(* consume generator *)elseQueue.popqs.(i)(* consume one more element *)andget_nexti=matchgen()with|Some_asres->forj=0ton-1doifj<>ithenQueue.pushresqs.(j)done;res|None->finished:=true;Nonein(* generators *)letl=Array.mapi(funi_->(fun()->nexti))qsinArray.to_listl(*$T
tee ~n:3 (1--12) |> List.map to_list = \
[to_list (1--12); to_list (1--12); to_list (1--12)]
*)moduleInterleaveState=structtype'at=|Onlyof'agen|Bothof'agen*'agen*boolref|Stopend(* Yield elements from a and b alternatively *)letinterleavegen_agen_b=letopenInterleaveStateinletstate=ref(Both(gen_a,gen_b,reftrue))inletrecnext()=match!statewith|Stop->None|Onlyg->beginmatchg()with|None->state:=Stop;None|(Some_)asres->resend|Both(g1,g2,r)->match(if!rtheng1()elseg2())with|None->state:=if!rthenOnlyg2elseOnlyg1;next()|(Some_)asres->r:=not!r;(* swap *)resinnext(*$T
interleave (repeat 0) (1--5) |> take 10 |> to_list = \
[0;1;0;2;0;3;0;4;0;5]
*)(*$R
let e1 = Gen.of_list [1;3;5;7;9] in
let e2 = Gen.of_list [2;4;6;8;10] in
let e = Gen.interleave e1 e2 in
OUnit.assert_equal [1;2;3;4;5;6;7;8;9;10] (Gen.to_list e);
*)moduleIntersperseState=structtype'at=|Start|YieldElemof'aoption|YieldSepof'aoption(* next val *)|Stopend(* Put [x] between elements of [enum] *)letinterspersexgen=letopenIntersperseStateinletstate=refStartinletrecnext()=match!statewith|Stop->None|YieldElemres->beginmatchgen()with|None->state:=Stop|Some_asres'->state:=YieldSepres'end;res|YieldSepres->state:=YieldElemres;Somex|Start->matchgen()with|None->state:=Stop;None|Some_asres->state:=YieldElemres;next()innext(*$T
intersperse 0 (1--5) |> to_list = [1;0;2;0;3;0;4;0;5]
*)(*$R
let e = 1 -- 5 in
let e' = Gen.intersperse 0 e in
OUnit.assert_equal [1;0;2;0;3;0;4;0;5] (Gen.to_list e');
*)(* Cartesian product *)letproductgenagenb=letall_a=ref[]inletall_b=ref[]in(* cur: current state, 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 *)letcur=ref`GetLeftinletrecnext()=match!curwith|`Stop->None|`GetLeft->beginmatchgena()with|None->cur:=`GetRightOrStop|Somea->all_a:=a::!all_a;cur:=`ProdLeft(a,!all_b)end;next()|`GetRight|`GetRightOrStop->(* TODO: test *)beginmatchgenb()with|Nonewhen!cur=`GetRightOrStop->cur:=`Stop|None->cur:=`GetLeft|Someb->all_b:=b::!all_b;cur:=`ProdRight(b,!all_a)end;next()|`ProdLeft(_,[])->cur:=`GetRight;next()|`ProdLeft(x,y::l)->cur:=`ProdLeft(x,l);Some(x,y)|`ProdRight(_,[])->cur:=`GetLeft;next()|`ProdRight(y,x::l)->cur:=`ProdRight(y,l);Some(x,y)innext(*$T
product (1--3) (of_list ["a"; "b"]) |> to_list \
|> List.sort Pervasives.compare = \
[1, "a"; 1, "b"; 2, "a"; 2, "b"; 3, "a"; 3, "b"]
*)(*$R
let printer = pi2list in
let e = Gen.product (1--3) (4--5) in
OUnit.assert_equal ~printer [1,4; 1,5; 2,4; 2,5; 3,4; 3,5]
(List.sort Pervasives.compare (Gen.to_list e));
*)(* Group equal consecutive elements together. *)letgroup?(eq=(=))gen=matchgen()with|None->fun()->None|Somex->letcur=ref[x]inletrecnext()=(* try to get an element *)letnext_x=if!cur=[]thenNoneelsegen()inmatchnext_x,!curwith|None,[]->None|None,l->cur:=[];(* stop *)Somel|Somex,y::_wheneqxy->cur:=x::!cur;next()(* same group *)|Somex,l->cur:=[x];Somelinnext(*$T
group (of_list [0;0;0;1;0;2;2;3;4;5;5;5;5;10]) |> to_list = \
[[0;0;0];[1];[0];[2;2];[3];[4];[5;5;5;5];[10]]
*)letuniq?(eq=(=))gen=letopenRunStateinletstate=refInitinletrecnext()=match!statewith|Stop->None|Init->beginmatchgen()with|None->state:=Stop;None|(Somex)asres->state:=Runx;resend|Runx->beginmatchgen()with|None->state:=Stop;None|(Somey)asres->ifeqxythennext()(* ignore duplicate *)else(state:=Runy;res)endinnext(*$T
uniq (of_list [0;0;0;1;0;2;2;3;4;5;5;5;5;10]) |> to_list = \
[0;1;0;2;3;4;5;10]
*)letsort?(cmp=Pervasives.compare)gen=(* build heap *)leth=Heap.empty~cmpiniter(Heap.inserth)gen;fun()->ifHeap.is_emptyhthenNoneelseSome(Heap.poph)(*$T
sort (of_list [0;0;0;1;0;2;2;3;4;5;5;5;-42;5;10]) |> to_list = \
[-42;0;0;0;0;1;2;2;3;4;5;5;5;5;10]
*)(* NOTE: using a set is not really possible, because once we have built the
set there is no simple way to iterate on it *)letsort_uniq?(cmp=Pervasives.compare)gen=uniq~eq:(funxy->cmpxy=0)(sort~cmpgen)(*$T
sort_uniq (of_list [0;0;0;1;0;2;2;3;4;5;42;5;5;42;5;10]) |> to_list = \
[0;1;2;3;4;5;10;42]
*)letchunksne=letrecnext()=matche()with|None->None|Somex->leta=Array.makenxinfilla1andfillai=(* fill the array. [i]: current index to fill *)ifi=nthenSomeaelsematche()with|None->Some(Array.suba0i)(* last array is not full *)|Somex->a.(i)<-x;filla(i+1)innext(*$T
chunks 25 (0--100) |> map Array.to_list |> to_list = \
List.map to_list [(0--24); (25--49);(50--74);(75--99);(100--100)]
*)(*$Q
Q.(list int) (fun l -> \
of_list l |> chunks 25 |> flat_map of_array |> to_list = l)
*)(* state of the permutation machine. One machine manages one element [x],
and depends on a deeper machine [g] that generates permutations of the
list minus this element (down to the empty list).
The machine can do two things:
- insert the element in the current list of [g], at any position
- obtain the next list of [g]
*)(* TODO: check https://en.wikipedia.org/wiki/Heap's_algorithm , might be better *)modulePermState=structtype'astate=|Done|Base(* bottom machine, yield [] *)|Insertof'ainsert_stateand'ainsert_state={x:'a;mutablel:'alist;mutablen:int;(* idx for insertion *)len:int;(* len of [l] *)sub:'at;}and'at={mutablest:'astate;}endletpermutationsg=letopenPermStatein(* make a machine for n elements. Invariant: n=len(l) *)letrecmake_machinenl=matchlwith|[]->assert(n=0);{st=Base}|x::tail->letsub=make_machine(n-1)tailinletst=matchnextsub()with|None->Done|Somel->Insert{x;n=0;l;len=n;sub}in{st;}(* next element of the machine *)andnextm()=matchm.stwith|Done->None|Base->m.st<-Done;Some[]|Insert({x;len;n;l;sub}asstate)->ifn=lenthenmatchnextsub()with|None->m.st<-Done;None|Somel->state.l<-l;state.n<-0;nextm()else(state.n<-state.n+1;Some(insertxnl))andinsertxnl=matchn,lwith|0,_->x::l|_,[]->assertfalse|_,y::tail->y::insertx(n-1)tailinletl=fold(funaccx->x::acc)[]ginnext(make_machine(List.lengthl)l)(*$T permutations
permutations (1--3) |> to_list |> List.sort Pervasives.compare = \
[[1;2;3]; [1;3;2]; [2;1;3]; [2;3;1]; [3;1;2]; [3;2;1]]
permutations empty |> to_list = [[]]
permutations (singleton 1) |> to_list = [[1]]
*)(*
Credits to Bernardo Freitas Paulo da Costa for [permutations_heap]!
B.R.Heap's algorithm for permutations,
cf http://en.wikipedia.org/wiki/Heap%27s_algorithm.
Continuation-based recursive formula, model for the state manipulations
below:
{[
let rec heap_perm k a n =
match n with
| 0 -> k a
| n ->
for i = 0 to n-1 do
heap_perm k a (n-1);
let j = (if n mod 2 = 1 then 0 else i) in
let t = a.(j) in
a.(j) <- a.(n-1);
a.(n-1) <- t
done
]}
*)(* The state of the permutation machine, containing
- the array [a] we're permuting, in the "current permutation";
- the level of recursion [n]: we can permute elements with index < [n]
- the stack of values of indices to permute [i] in the list [is]
The permutation stops when we have no more elements in the stack [is].
*)moduleHeapPermState=structtype'astate={elts:'aarray;mutablen:int;mutableis:intlist;}endletpermutations_heapg=letopenHeapPermStateinletl=fold(funaccx->x::acc)[]ginleta=Array.of_listlinletrecnextst()=matchst.nwith|0->beginmatchst.iswith|[]|_::[]->assertfalse|0::i::is'->(* "Pop state" before returning next element *)st.is<-(i+1)::is';st.n<-1;Some(Array.copya)|_::_::_->assertfalseend|n->matchst.iswith|[]->None|i::is'wheni=n->(* Pop state at end of loop *)st.is<-is';st.n<-n+1;beginmatchst.iswith|[]->None(* last loop *)|i::is'->letj=(ifst.nmod2=1then0elsei)inlettmp=st.elts.(j)inst.elts.(j)<-st.elts.(n);st.elts.(n)<-tmp;st.is<-(i+1)::is';nextst()end|_::_->(* Recurse down and start new loop *)st.n<-n-1;st.is<-0::st.is;nextst()inletn=Array.lengthainifn=0thenemptyelsenext{elts=a;n=n;is=[0]}(*$T permutations_heap
permutations_heap (1--3) |> to_list |> List.sort Pervasives.compare = \
[[|1;2;3|]; [|1;3;2|]; [|2;1;3|]; [|2;3;1|]; [|3;1;2|]; [|3;2;1|]]
permutations_heap empty |> to_list = []
permutations_heap (singleton 1) |> to_list = [[|1|]]
*)moduleCombState=structtype'astate=|Done|Base|Addof'a*'at*'at(* add x at beginning of first; then switch to second *)|Followof'at(* just forward *)and'at={mutablest:'astate}endletcombinationsng=letopenCombStateinassert(n>=0);letrecmake_statenl=matchn,lwith|0,_->{st=Base}|_,[]->{st=Done}|_,x::tail->letm1=make_state(n-1)tailinletm2=make_statentailin{st=Add(x,m1,m2)}andnextm()=matchm.stwith|Done->None|Base->m.st<-Done;Some[]|Followm->beginmatchnextm()with|None->m.st<-Done;None|Some_asres->resend|Add(x,m1,m2)->matchnextm1()with|None->m.st<-Followm2;nextm()|Somel->Some(x::l)inletl=fold(funaccx->x::acc)[]ginnext(make_statenl)(*$T
combinations 2 (1--4) |> map (List.sort Pervasives.compare) \
|> to_list |> List.sort Pervasives.compare = \
[[1;2]; [1;3]; [1;4]; [2;3]; [2;4]; [3;4]]
combinations 0 (1--4) |> to_list = [[]]
combinations 1 (singleton 1) |> to_list = [[1]]
*)modulePowerSetState=structtype'astate=|Done|Base|Addof'a*'at(* add x before any result of m *)|AddToof'alist*'a*'at(* yield x::list, then back to Add(x,m) *)and'at={mutablest:'astate}endletpower_setg=letopenPowerSetStateinletrecmake_statel=matchlwith|[]->{st=Base}|x::tail->letm=make_statetailin{st=Add(x,m)}andnextm()=matchm.stwith|Done->None|Base->m.st<-Done;Some[]|Add(x,m')->beginmatchnextm'()with|None->m.st<-Done;None|Somelasres->m.st<-AddTo(l,x,m');resend|AddTo(l,x,m')->m.st<-Add(x,m');Some(x::l)inletl=fold(funaccx->x::acc)[]ginnext(make_statel)(*$T
power_set (1--3) |> map (List.sort Pervasives.compare) \
|> to_list |> List.sort Pervasives.compare = \
[[]; [1]; [1;2]; [1;2;3]; [1;3]; [2]; [2;3]; [3]]
power_set empty |> to_list = [[]]
power_set (singleton 1) |> map (List.sort Pervasives.compare) \
|> to_list |> List.sort Pervasives.compare = [[]; [1]]
*)(** {3 Conversion} *)letof_listl=letl=reflinfun()->match!lwith|[]->None|x::l'->l:=l';Somexletto_rev_listgen=fold(funaccx->x::acc)[]gen(*$Q
(Q.list Q.small_int) (fun l -> \
to_rev_list (of_list l) = List.rev l)
*)letto_listgen=List.rev(to_rev_listgen)letto_arraygen=letl=to_rev_listgeninmatchlwith|[]->[||]|_->leta=Array.of_listlinletn=Array.lengthain(* reverse array *)fori=0to(n-1)/2dolettmp=a.(i)ina.(i)<-a.(n-i-1);a.(n-i-1)<-tmpdone;aletof_array?(start=0)?lena=letlen=matchlenwith|None->Array.lengtha-start|Somen->assert(n+start<Array.lengtha);ninleti=refstartinfun()->if!i>=start+lenthenNoneelse(letx=a.(!i)inincri;Somex)(*$Q
(Q.array Q.small_int) (fun a -> \
of_array a |> to_array = a)
*)letof_string?(start=0)?lens=letlen=matchlenwith|None->String.lengths-start|Somen->assert(n+start<String.lengths);ninleti=refstartinfun()->if!i>=start+lenthenNoneelse(letx=s.[!i]inincri;Somex)letto_bufferbufg=iter(Buffer.add_charbuf)gletto_strings=letbuf=Buffer.create16into_bufferbufs;Buffer.contentsbufletrand_inti=repeatedly(fun()->Random.inti)letint_range?(step=1)ij=ifstep=0thenraise(Invalid_argument"Gen.int_range");let(>)=ifstep>0then(>)else(<)inletr=refiinfun()->letx=!rinifx>jthenNoneelsebeginr:=!r+step;Somexend(*$= & ~printer:Q.Print.(list int)
[1;2;3;4] (int_range 1 4 |> to_list)
[4;3;2;1] (int_range ~step:~-1 4 1 |> to_list)
[6;4;2] (int_range 6 1 ~step:~-2 |> to_list)
[] (int_range 4 1 |> to_list)
*)letlinesg=letbuf=Buffer.create32inletstop=reffalseinletrecnext()=if!stopthenNoneelsematchg()with|None->stop:=true;(* only return a non-empty line *)ifBuffer.lengthbuf=0thenNoneelseSome(Buffer.contentsbuf)|Some'\n'->lets=Buffer.contentsbufinBuffer.clearbuf;Somes|Somec->Buffer.add_charbufc;next()innext(*$= & ~printer:Q.Print.(list string)
["abc"; "de"; ""] (lines (of_string "abc\nde\n\n") |> to_list)
*)letunlinesg=letst=ref`Nextinfun()->match!stwith|`Stop->None|`Next->beginmatchg()with|None->st:=`Stop;None|Some""->Some'\n'(* empty line *)|Somes->st:=`Consume(s,1);Somes.[0]end|`Consume(s,i)wheni=String.lengths->st:=`Next;Some'\n'|`Consume(s,i)->st:=`Consume(s,i+1);Somes.[i](*$Q
Q.printable_string (fun s -> \
of_string s |> lines |> unlines |> to_string |> String.trim = String.trim s)
*)letpp?(start="")?(stop="")?(sep=",")?(horizontal=false)pp_elemformattergen=(ifhorizontalthenFormat.pp_open_hboxformatter()elseFormat.pp_open_hvboxformatter0);Format.pp_print_stringformatterstart;letrecnextis_first=matchgen()with|Somex->ifnotis_firstthenbeginFormat.pp_print_stringformattersep;Format.pp_print_spaceformatter();pp_elemformatterxendelsepp_elemformatterx;nextfalse|None->()innexttrue;Format.pp_print_stringformatterstop;Format.pp_close_boxformatter()moduleInfix=structlet(--)=int_range~step:1let(>>=)xf=flat_mapfxlet(>>|)xf=mapfxlet(>|=)xf=mapfxendincludeInfixmoduleRestart=structtype'at=unit->'agentype'arestartable='atletliftfe=f(e())letlift2fe1e2=f(e1())(e2())letempty()=emptyletsingletonx()=singletonxletreturn=singletonletiteratexf()=iteratexfletrepeatx()=repeatxletunfoldfacc()=unfoldfaccletinit?limitf()=init?limitfletcycleenum=assert(not(is_empty(enum())));fun()->letgen=ref(enum())in(* start cycle *)letrecnext()=match(!gen)()with|(Some_)asres->res|None->gen:=enum();next()innextletis_emptye=is_empty(e())letfoldfacce=foldfacc(e())letreducefe=reducef(e())letscanfacce()=scanfacc(e())letunfold_scanfacce()=unfold_scanfacc(e())letiterfe=iterf(e())letiterife=iterif(e())letlengthe=length(e())letmapfe()=mapf(e())letmapife()=mapif(e())letfold_mapfse()=fold_mapfs(e())letappende1e2()=append(e1())(e2())letflattene()=flatten(e())letflat_mapfe()=flat_mapf(e())letmem?eqxe=mem?eqx(e())lettakene()=taken(e())letdropne()=dropn(e())letnthne=nthn(e())lettake_nthne()=take_nthn(e())letfilterpe()=filterp(e())lettake_whilepe()=take_whilep(e())letfold_whilefse=fold_whilefs(e())letdrop_whilepe()=drop_whilep(e())letfilter_mapfe()=filter_mapf(e())letzip_withfe1e2()=zip_withf(e1())(e2())letzipe1e2()=zip(e1())(e2())letzip_indexe()=zip_index(e())letunzipe=mapfste,mapsndeletpartitionpe=filterpe,filter(funx->not(px))eletfor_allpe=for_allp(e())letexistspe=existsp(e())letfor_all2pe1e2=for_all2p(e1())(e2())letexists2pe1e2=exists2p(e1())(e2())letmap2fe1e2()=map2f(e1())(e2())letiter2fe1e2=iter2f(e1())(e2())letfold2facce1e2=fold2facc(e1())(e2())letmin?lte=min?lt(e())letmax?lte=max?lt(e())let___eq=eqleteq?eqe1e2=___eq?eq(e1())(e2())letlexico?cmpe1e2=lexico?cmp(e1())(e2())letcompare?cmpe1e2=compare?cmp(e1())(e2())letsume=sum(e())letfindfe=findf(e())letmergee()=merge(e())letintersection?cmpe1e2()=intersection?cmp(e1())(e2())letsorted_merge?cmpe1e2()=sorted_merge?cmp(e1())(e2())letsorted_merge_n?cmpl()=sorted_merge_n?cmp(List.map(fung->g())l)lettee?ne=tee?n(e())letround_robin?ne=round_robin?n(e())letinterleavee1e2()=interleave(e1())(e2())letinterspersexe()=interspersex(e())letproducte1e2()=product(e1())(e2())letgroup?eqe()=group?eq(e())letuniq?eqe()=uniq?eq(e())letsort?(cmp=Pervasives.compare)enum=fun()->sort~cmp(enum())letsort_uniq?(cmp=Pervasives.compare)e=lete'=sort~cmpeinuniq~eq:(funxy->cmpxy=0)e'letchunksne()=chunksn(e())letpermutationsg()=permutations(g())letpermutations_heapg()=permutations_heap(g())letcombinationsng()=combinationsn(g())letpower_setg()=power_set(g())letof_listl()=of_listlletto_rev_liste=to_rev_list(e())letto_liste=to_list(e())letto_arraye=to_array(e())letof_array?start?lena()=of_array?start?lenaletof_string?start?lens()=of_string?start?lensletto_strings=to_string(s())letto_bufferbufs=to_bufferbuf(s())letrand_inti()=rand_intiletint_range?stepij()=int_range?stepijletlinesg()=lines(g())letunlinesg()=unlines(g())moduleInfix=structlet(--)=int_range~step:1let(>>=)xf=flat_mapfxlet(>>|)xf=mapfxlet(>|=)xf=mapfxendincludeInfixletpp?start?stop?sep?horizontalpp_elemfmte=pp?start?stop?sep?horizontalpp_elemfmt(e())letof_gen?caching?max_chunk_sizeg=letcached=refNoneinfun()->match!cachedwith|Somemlist->GenMList.to_genmlist|None->letmlist=GenMList.of_gen_lazy?caching?max_chunk_sizegincached:=Somemlist;GenMList.to_genmlistend(** {2 Generator functions} *)letstartg=g()(** Store content of the generator in an enum *)letpersistentgen=letl=GenMList.of_gengeninfun()->GenMList.to_genl(*$T
let g = 1--10 in let g' = persistent g in \
Restart.to_list g' = Restart.to_list g'
let g = 1--10 in let g' = persistent g in \
Restart.to_list g' = [1;2;3;4;5;6;7;8;9;10]
*)(*$R
let i = ref 0 in
let gen () =
let j = !i in
if j > 5 then None else (incr i; Some j)
in
let e = Gen.persistent gen in
OUnit.assert_equal [0;1;2;3;4;5] (Restart.to_list e);
OUnit.assert_equal [0;1;2;3;4;5] (Restart.to_list e);
OUnit.assert_equal [0;1;2;3;4;5] (Restart.to_list e);
*)letpersistent_lazy?caching?max_chunk_sizegen=letl=GenMList.of_gen_lazy?caching?max_chunk_sizegeninfun()->GenMList.to_genl(*$T
let g = 1--1_000_000_000 in let g' = persistent_lazy g in \
(g' () |> take 100 |> to_list = (1--100 |> to_list)) && \
(g' () |> take 200 |> to_list = (1--200 |> to_list))
*)letpeekg=letstate=ref`Startinletrecnext()=match!statewith|`Stop->None|`Atx->beginmatchg()with|None->state:=`Stop;Some(x,None)|Someyasres->state:=`Aty;Some(x,res)end|`Start->beginmatchg()with|None->state:=`Stop;None|Somex->state:=`Atx;next()endinnext(*$= & ~printer:Q.Print.(list (pair int (option int)))
[] (peek (of_list []) |> to_list)
[1, Some 2; 2, Some 3; 3, Some 4; 4, None] (peek (1 -- 4) |> to_list)
*)(*$Q
Q.(list int) (fun l -> \
l = [] || (of_list l |> peek |> filter_map snd |> to_list = List.tl l))
*)letqueue_to_array_q=ifQueue.is_emptyqthen[||]else(letx=Queue.peekqinleta=Array.make(Queue.lengthq)xinleti=ref0inQueue.iter(funx->a.(!i)<-x;incri)q;a)letpeek_nng=ifn<1theninvalid_arg"peek_n";letstate=ref`Startinletq=Queue.create()inletrecnext()=match!statewith|`Start->filln;state:=ifQueue.is_emptyqthen`Stopelse`Continue;next()|`Continue->assert(not(Queue.is_emptyq));letx=Queue.popqinfill1;state:=ifQueue.is_emptyqthen`Stopelse`Continue;Some(x,queue_to_array_q)|`Stop->None(* add [n] elements to [f] if possible *)andfilli=assert(i+Queue.lengthq<=n);ifi>0thenmatchg()with|None->()|Somex->Queue.pushxq;fill(i-1)innext(*$= & ~printer:Q.Print.(list (pair int (array int)))
[] (peek_n 1 (of_list []) |> to_list)
[1, [|2;3|]; 2, [|3;4|]; 3, [|4|]; 4, [||]] (peek_n 2 (1 -- 4) |> to_list)
[1, [|2;3;4|]; 2, [|3;4;5|]; 3, [|4;5|]; 4, [|5|]; 5,[||]] \
(peek_n 3 (1 -- 5) |> to_list)
*)(*$QR
Q.(list small_int)
(fun l ->
let l' =
of_list l
|> peek_n 10
|> filter_map (fun (_,a) -> if a=[||] then None else Some a.(0))
|> to_list
in
l = [] || l' = List.tl l)
*)(** {2 Basic IO} *)moduleIO=structletwith_file_in?(mode=0o644)?(flags=[])filenamef=letic=open_in_genflagsmodefilenameintryletx=ficinclose_in_noerric;xwithe->close_in_noerric;raiseeletwith_in?mode?flagsfilenamef=with_file_in?mode?flagsfilename(funic->letnext()=trySome(input_charic)withEnd_of_file->Noneinfnext)letwith_lines?mode?flagsfilenamef=with_file_in?mode?flagsfilename(funic->letnext()=trySome(input_lineic)withEnd_of_file->Noneinfnext)letwith_file_out?(mode=0o644)?(flags=[Open_creat;Open_wronly])filenamef=letoc=open_out_genflagsmodefilenameintryletx=focinclose_outoc;xwithe->close_out_noerroc;raiseeletwrite_str?mode?flags?(sep="")filenameg=with_file_out?mode?flagsfilename(funoc->iteri(funis->ifi>0thenoutput_stringocsep;output_stringocs)g)letwrite?mode?flagsfilenameg=with_file_out?mode?flagsfilename(funoc->iter(func->output_charocc)g)letwrite_lines?mode?flagsfilenameg=with_file_out?mode?flagsfilename(funoc->iter(funs->output_stringocs;output_charoc'\n')g)end