Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file Generic.ml
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682(******************************************************************************)(* *)(* Sek *)(* *)(* Arthur Charguéraud, Émilie Guermeur and François Pottier *)(* *)(* Copyright Inria. All rights reserved. This file is distributed under the *)(* terms of the GNU Lesser General Public License as published by the Free *)(* Software Foundation, either version 3 of the License, or (at your *)(* option) any later version, as described in the file LICENSE. *)(* *)(******************************************************************************)(* This file contains "generic" implementations of the operations that do not
need any internal knowledge of our data structures. *)openPrivateSignaturesincludePublicSignature(* -------------------------------------------------------------------------- *)(* Logging support. *)letdebug=falselet[@inline]logformat=ifdebugthenPrintf.fprintfstderrformatelsePrintf.ifprintfstderrformat(* -------------------------------------------------------------------------- *)(* Implementations of [find] and friends in terms of [iter]. *)module[@inline]Iter(S:sigtype'atvaliter:direction->('a->unit)->'at->unitend)=structopenSlet[@specialise]find(typea)direction(p:a->bool)(s:at):a=(* [let exception] requires OCaml 4.04. *)letmoduleE=structexceptionFoundofaendinmatchiterdirection(funx->ifpxthenraise(E.Foundx))swith|exceptionE.Foundx->x|()->raiseNot_foundlet[@specialise]find_optdirectionps=trySome(finddirectionps)withNot_found->Nonelet[@specialise]find_map(typeab)direction(p:a->boption)(s:at):boption=(* [let exception] requires OCaml 4.04. *)letmoduleE=structexceptionFoundofboptionendinmatchiterdirection(funx->matchpxwith|Some_aso->raise(E.Foundo)|None->())swith|exceptionE.Foundo->o|()->Noneletexistsps=tryignore(findforwardps);truewithNot_found->falselet[@inline]for_allps=not(exists(funx->not(px))s)let[@inline]memxs=exists(funy->x=y)slet[@inline]memqxs=exists(funy->x==y)send(* Iter *)(* -------------------------------------------------------------------------- *)(* Implementations of [filter], [partition], and friends in terms of [iter],
[create], [push], and [finalize]. When the result type ['a u] is the type
of ephemeral sequences, [finalize] is the identity function; when the
result type is the type of persistent sequences, [finalize] is
[snapshot_and_clear]. *)(* In [create n default], the parameter [n] is an upper bound on the size
of the ephemeral sequence that is being created. *)module[@inline]IterCreatePush(S:sigtype'atvaldefault:'at->'avallength:'at->lengthvaliter:direction->('a->unit)->'at->unittype'auvalcreate:length->'a->'auvalpush:direction->'au->'a->unitvalfinalize:'au->'atend)=structopenSletfilterps=lets'=create(lengths)(defaults)initerforward(funx->ifpxthenpushbacks'x)s;finalizes'letfilter_mapdfs=lets'=create(lengths)diniterforward(funx->matchfxwith|None->()|Somey->pushbacks'y)s;finalizes'letflatten_mapdfs=(* Here, we would like to first compute the length [n] of the sequence
[s'], then call [create n d]. However, because the function [f] might
be impure, we are not allowed to call it more than once. So we cannot
do that (or we would have to allocate a sequence of the results of
these calls, but that would be bad if [s] is a very long sequence). *)(* We work around this problem by passing [max_int] as an upper bound
and hoping that [create] ignores its argument [n] anyway. It is up
to our caller to ensure this. *)letn=max_intinlets'=createndiniterforward(funx->letys=fxiniterforward(funy->pushbacks'y)ys)s;finalizes'letpartitionps=letn=lengthsandd=defaultsinlets1,s2=creatend,createndiniterforward(funx->pushback(ifpxthens1elses2)x)s;finalizes1,finalizes2end(* IterCreatePush *)(* -------------------------------------------------------------------------- *)(* An implementation of [uniq] in terms of [filter] and a few more functions. *)(* To implement [uniq], we could use [filter is_new s], where the function
[is_new] maintains a reference of type ['a option ref]. We prefer to avoid
this option (which imposes a lot of memory allocation). To do so, we peek
at the first element of [s] and use it to initialize a reference of type
['a ref]. *)let[@inline]uniqis_emptycreatedefaultpeekfilterpushcmps=ifis_emptysthencreate(defaults)elseletx=peekfrontsinletprevious=refxinletis_newx=cmp!previousx<>0&&(previous:=x;true)inlets=filteris_newsinpushfrontsx(* -------------------------------------------------------------------------- *)(* Implementations of [to_seq], [map], [iter2_segments], [iter2] and [map2] in
terms of iterators and [init], plus implementations of several derived
functions. *)(* We use unchecked iterators when we are certain that we cannot possibly
attempt to use an invalid iterator. When we cannot be certain, we either
use checked iterators, or use unchecked iterators and perform an explicit
validity check when we are finished. *)(* In [to_seq], for instance, our iterator can be invalidated if the user
modifies the underlying ephemeral sequence. If, thereafter, the user
forces the sequence returned by [to_seq], then an invalid iterator is
used. To detect this, we use a checked iterator. *)(* In [iter2_segments], for instance, our iterators can be invalidated if the
user function [f] modifies the underlying sequences. To detect this, we
could use checked iterators. We prefer to delay the check to the end: we
use unchecked iterators and verify at the end that they are still valid. *)(* Checked iterators are more costly when [check_iterator_validity] is [true].
When it is [false], they are the same as unchecked iterators. *)module[@inline]IteratorsInit(S:sigtype'atvaldefault:'at->'avallength:'at->lengthvalunchecked_init:'a->length->(index->'a)->'atmoduleIter:sigincludeITERwithtype'at:='atandtypedirection:=directionendmoduleUncheckedIter:sigincludeITERwithtype'at:='atandtypedirection:=directionvalis_valid:'aiter->boolvalget_writable_segment_and_jump:direction->'aiter->'asegmentendend)=structopenS(* [of_seq_segment]. *)letof_seq_segmentdnxs=ifnot(0<=n)theninvalid_arg"of_seq_segment: invalid length"elseletit=Adapters.iterator_of_seqxsintryunchecked_initdn(fun_i->it())withAdapters.Exhausted->invalid_arg"of_seq_segment: sequence has fewer elements than advertised"(* [of_list_segment]. *)letof_list_segmentdnxs=ifnot(0<=n)theninvalid_arg"of_list_segment: invalid length"elseletit=Adapters.iterator_of_listxsintryunchecked_initdn(fun_i->it())withAdapters.Exhausted->invalid_arg"of_list_segment: list has fewer elements than advertised"(* [of_list]. *)let[@inline]of_listdxs=letn=List.lengthxsinof_list_segmentdnxs(* [to_seq]. *)letto_seqpovs=letit=S.Iter.createpovsin(* checked iterator *)letrecproduce()=tryletx=S.Iter.get_and_movepovitinSeq.Cons(x,produce)withEnd->Seq.Nilinproduce(* [to_seqi]. *)letto_seqipovs=letit=S.Iter.createpovsin(* checked iterator *)letrecproducei()=tryletx=S.Iter.get_and_movepovitinSeq.Cons((i,x),produce(i+1))withEnd->Seq.Nilinproduce0(* [mapi]. *)(* TODO speed up using [iter_segments] and [push_segment] *)(* We use a checked iterator because a call to [f] might modify the sequence
[s]. Re-implementing this code using [iter_segments] would eliminate this
inefficiency. *)let[@inline]mapidfs=letit=S.Iter.createforwardsin(* checked iterator *)unchecked_initd(lengths)(funi->letx=S.Iter.get_and_moveforwarditinfix)(* [map]. *)let[@inline]mapdfs=let[@inline]f_ix=fxinmapidfs(* [deep_copy s] is [map (default s) id s]. *)(* This is a "deep" copy, that is, all of the data is copied, and nothing is
shared. The new sequence does not have exactly the same shape as the
original sequence; its chunks are fully populated. *)let[@inline]deep_copys=let[@inline]idx=xinmap(defaults)ids(* [rev]. *)(* TODO speed up using [iter_segments] and [push_segment] *)letrevs=letit=S.UncheckedIter.createbackwardsin(* unchecked iterator *)unchecked_init(defaults)(lengths)(fun_i->S.UncheckedIter.get_and_movebackwardit)(* [unzip] can be implemented either by iterating on the source and
performing iterated pushes on two destinations; or by calling [map]
twice, at the cost of iterating twice over the source sequence.
We use the second approach. *)letunzips=let(d1,d2)=defaultsinmapd1fsts,mapd2snds(* To implement [iter2_segments], we use two iterators, which we query using
[get_segment_and_jump]. We use unchecked iterators and perform a check at
the end. *)includestructopenS.UncheckedIter(* We could also use [iter_segments] on one sequence
and [get_segment_and_jump] on the other sequence,
but that would be less symmetric,
and experiments seem to indicate that [get_segment_and_jump] is faster
than [iter_segments] anyway, although we cannot explain why that is. *)(* At the moment, we assume that the two iterators are based on the same
type of sequences, so we cannot mix (say) an iterator on an ephemeral
sequence and an iterator on a persistent sequence. This could be relaxed
if necessary. *)(* We implement "lax" versions of these functions, where the lengths of the
two sequences are allowed to differ. See NOTES.md for a discussion. *)let_segment_length((_,_,k):'asegment)=klet[@specialise]truncate_segmentpovsize((a,i,k)asseg)=assert(Segment.is_validseg);assert(size<k);matchpovwith|Front->a,i,size|Back->a,i+k-size,size(* [preserving it f] executes the function [f] and takes action to preserve
the iterator [it], which can be invalidated by [f]. *)(* TODO implement this in a more efficient way *)let[@inline]preservingitf=leti=indexitinlety=f()inresetforwardit;reachiti;y(* For some applications, such as [blit], it is convenient to have write
access to the segment produced by the iterator [it2]. The parameter
[writable2] controls this. *)(* A pitfall: if [it1] and [it2] are iterators on the same sequence, then
writing through [it2] invalidates [it1]. We must be careful to preserve
[it1]. This extra precaution is requested by the parameter [preserve1]. *)(* [even] is invoked when the two iterators are even, that is, when they
have the same index. *)let[@specialise]recevenpovpreserve1writable2it1it2f=(* Ask [it1] for a segment [seg1], and transition to [ahead1]. *)log"even: it1 = %d, it2 = %d\n%!"(indexit1)(indexit2);letseg1=get_segment_and_jumppovit1inahead1povpreserve1writable2it1seg1it2f(* [ahead1] is invoked when the iterator [it1] has produced one more
segment than the iterator [it2], namely the segment [seg1]. *)and[@specialise]ahead1povpreserve1writable2it1seg1it2f=log"ahead1 (%d): it1 = %d, it2 = %d\n%!"(_segment_lengthseg1)(indexit1)(indexit2);(* Ask [it2] for a segment [seg2]. *)letseg2=ifwritable2thenbegin(* A pitfall: if [it1] and [it2] are iterators on the same sequence,
then writing through [it2] invalidates [it1]. We must be careful
to preserve [it1]. *)ifpreserve1thenpreservingit1(fun()->get_writable_segment_and_jumppovit2)elseget_writable_segment_and_jumppovit2endelseget_segment_and_jumppovit2intestpovpreserve1writable2it1seg1it2seg2f(* [ahead2] is invoked when the iterator [it2] has produced one more
segment than the iterator [it1], namely the segment [seg2]. *)and[@specialise]ahead2povpreserve1writable2it1it2seg2f=log"ahead2 (%d): it1 = %d, it2 = %d\n%!"(_segment_lengthseg2)(indexit1)(indexit2);(* Ask [it1] for a segment [seg1]. *)letseg1=get_segment_and_jumppovit1intestpovpreserve1writable2it1seg1it2seg2f(* [test] is invoked when each of [it1] and [it2] have produced one
segment. We must test which of the two segments is longer. *)and[@specialise]testpovpreserve1writable2it1seg1it2seg2f=leta1,i1,k1=seg1anda2,i2,k2=seg2inassert(k1>0&&k2>0);log"test (%d/%d): it1 = %d, it2 = %d\n%!"k1k2(indexit1)(indexit2);ifk1=k2thenbegin(* The segments [seg1] and [seg2] have the same length. *)(* Pass them to the user, and transition to [even]. *)fseg1seg2;evenpovpreserve1writable2it1it2fendelseifk1<k2thenbegin(* [seg1] is shorter than [seg2]. Split [seg2]. *)matchpovwith|Front->fseg1(a2,i2,k1);ahead2povpreserve1writable2it1it2(a2,i2+k1,k2-k1)f|Back->fseg1(a2,i2+k2-k1,k1);ahead2povpreserve1writable2it1it2(a2,i2,k2-k1)fendelsebeginassert(k2<k1);(* [seg2] is shorter than [seg1]. Split [seg1]. *)matchpovwith|Front->f(a1,i1,k2)seg2;ahead1povpreserve1writable2it1(a1,i1+k2,k1-k2)it2f|Back->f(a1,i1+k1-k2,k2)seg2;ahead1povpreserve1writable2it1(a1,i1,k1-k2)it2fend(* [with_final_validity_check it1 it2 f] executes f()] and checks that the
iterators [it1] and [it2] are still valid at the end. *)let[@inline]with_final_validity_checkit1it2f=Adapters.try_finallyf(fun()->ifnot(is_validit1&&is_validit2)theninvalid_arg"ephemeral sequence was modified while iteration was ongoing")(* The main function: [iter2_segments]. *)(* By convention, iteration stops as soon as the end of the shorter
sequence is reached. This is more useful than the convention of
the OCaml standard library, which is to raise [Invalid_argument]
when the two sequences have distinct lengths. *)let[@inline]iter2_segmentspovs1s2f=letit1=createpovs1andit2=createpovs2inwith_final_validity_checkit1it2(fun()->tryevenpovfalsefalseit1it2f(* [even] cannot terminate normally. *)withEnd->())(* [bounded_iter2_segments] is a variant of [iter2_segments] that stops
after a certain total size has been reached. The final segments are
truncated if necessary so as to match [size] exactly. *)(* This function offers control over [preserve1] and [writable2], and takes
two iterators as arguments, instead of two sequences: this allows the
caller to choose the starting points. *)(* We assume that the function [f] cannot invalidate [it1] and [it2],
so we do not perform a final validity check. This assumption is
safe because [bounded_iter2_segments] is not exposed to the user. *)let[@specialise]bounded_iter2_segmentspovpreserve1writable2sizeit1it2f=tryletsize=refsizeinevenpovpreserve1writable2it1it2(funseg1seg2->(* If we are done, stop. *)lets=!sizeinifs=0thenraiseEnd;(* Otherwise, compare the length [k] of the segments [seg1]
and [seg2] with the requested size [s]. If [s] is less
than [k], then the segments must be truncated. *)assert(_segment_lengthseg1=_segment_lengthseg2);letk=_segment_lengthseg1inifs<kthenbegin(* size := 0; *)f(truncate_segmentpovsseg1)(truncate_segmentpovsseg2);raiseEndendelsebeginsize:=s-k;fseg1seg2end)(* [even] cannot terminate normally. *)withEnd->()end(* anonymous [struct] *)(* [iter2]. *)let[@specialise]iter2povfs1s2=ArrayExtra.iter2iter2_segmentspovfs1s2(* [map2] could be implemented in terms of [iter2], but that would require
building the result via iterated pushes. Because the length of the result
is known in advance, it is perhaps more efficient to use [init]. *)(* TODO speed up using [iter2_segments] and [push_segment] *)letmap2dfs1s2=letit1=S.Iter.createforwards1(* checked iterator *)andit2=S.Iter.createforwards2in(* checked iterator *)unchecked_initd(min(lengths1)(lengths2))(fun_i->letx1=S.Iter.get_and_moveforwardit1andx2=S.Iter.get_and_moveforwardit2infx1x2)(* [zip]. *)letzips1s2=map2(defaults1,defaults2)(funx1x2->(x1,x2))s1s2(* [fold_left2] and [fold_right2]. *)letfold_left2fseeds1s2=Adapters.fold_left2(iter2forward)fseeds1s2letfold_right2fs1s2seed=Adapters.fold_right2(iter2backward)fs1s2seed(* [find2]. *)let[@specialise]find2(typeab)pov(p:a->b->bool)(s1:at)(s2:bt):a*b=(* [let exception] requires OCaml 4.04. *)letmoduleE=structexceptionFoundof(a*b)endinmatchiter2pov(funx1x2->ifpx1x2thenraise(E.Found(x1,x2)))s1s2with|exceptionE.Foundxx->xx|()->raiseNot_found(* [exists2]. *)letexists2ps1s2=tryignore(find2forwardps1s2);truewithNot_found->false(* [for_all2]. *)let[@inline]for_all2ps1s2=not(exists2(funx1x2->not(px1x2))s1s2)(* [equal]. *)letequalps1s2=lengths1=lengths2&&for_all2ps1s2(* [compare]. *)exceptionReturnofintletcompare(typeab)(cmp:a->b->int)(s1:at)(s2:bt):int=try(* Compare the elements [x1] and [x2] drawn synchronously from the
two sequences. As soon as a comparison result [c] indicates that
[x1] and [x2] are distinct, stop and return [c]. *)iter2front(funx1x2->letc=cmpx1x2inifc<>0thenraise(Returnc))s1s2;(* If we are still here, then no mismatch was found. There remains to
check the lengths of the sequences. The outcome of the comparison
between sequences is the outcome of the comparison between their
lengths. *)compare(lengths1)(lengths2)withReturnc->c(* [merge] could be implemented by composing [to_seq], a merge
operation on streams of type ['a Seq.t], and [of_seq]. *)(* We give a direct implementation in terms of our iterators. *)(* In theory, there is a risk that the user-provided function [cmp] might
modify the collections [s1] or [s2], so we use checked iterators. *)letmergecmps1s2=letmoduleI=S.Iterinletit1=I.createforwards1(* checked iterator *)andit2=I.createforwards2in(* checked iterator *)unchecked_init(defaults1)(lengths1+lengths2)(fun_i->ifI.finishedit1thenI.get_and_moveforwardit2elseifI.finishedit2thenI.get_and_moveforwardit1elseletx1=I.getit1andx2=I.getit2inletc=cmpx1x2inifc<=0thenbegin(* [x1] is smaller or equal. Pick [x1]. *)(* When [x1] and [x2] are equal, OCaml's [List.merge] picks [x1];
we do the same. *)I.moveforwardit1;x1endelsebeginI.moveforwardit2;x2end)end(* IteratorsInit *)