1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072(******************************************************************************)(* *)(* 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. *)(* *)(******************************************************************************)openPublicTypeAbbreviationsopenPublicSettingsopenPrivateSignaturesmodule[@inline]Make(SChunk:SCHUNK)(C:CAPACITY)=structopenCtype'aschunk='aSChunk.ttype'ameasure='aSChunk.measure=|MUnit:'ameasure|MSWeight:'aschunkmeasureletapply=SChunk.apply(* -------------------------------------------------------------------------- *)(* A shareable chunk sequence is organized in several levels. It can be
thought of as a list of levels, where the constructors [Zero] and [Level]
play the roles of nil and cons. *)(* The number of levels is logarithmic in the number of elements in the
sequence. *)(* The data constructor [Zero] represents an empty sequence. It carries a
[default] element, which is used when creating an empty chunk. *)(* A chunk at depth [depth] has capacity [capacity depth]. *)(* The [Level] constructor represents a nonempty sequence. It carries the
total [weight] of the elements of the sequence, followed with a [front]
schunk, which contains elements, a [middle] sequence, whose elements are
*schunks* of elements, and a [back] schunk, which contains elements. *)(* We impose the following "populated-sides invariant": if the front or back
schunk is empty, then the middle sequence must be empty as well. In the
contrapositive, if the middle sequence is nonempty, then the front and back
schunks must be nonempty. *)(* We impose the following "density invariant": in the middle sequence, the
sum of the lengths of two adjacent schunks must exceed [capacity depth]. We
require that this property also hold of the middle sequence in front of
which a fictitious full schunk has been prepended. As a result, the density
invariant implies that no schunk in the middle sequence is empty. *)(* The constructors [One] and [Short] play no role in this file. They are
added in anticipation of the code in [ShortPersistentSequence], which deals
with a more compact representation of short persistent sequences.
Therefore, many of the functions in this file do not handle these cases.
Some functions do handle these cases, because it is easy to do so here (and
this saves a test elsewhere). These functions are: [default], [dummy],
[weight], [is_empty], [iter_segments], [to_array], [print], [peek]. *)type'at=|Zeroof{default:'a;}|Oneof{default:'a;x:'a}|Shortof{default:'a;a:'aarray}|Levelof{weight:weight;front:'aschunk;middle:'aschunkt;back:'aschunk;}(* The structure of a [Level] is symmetric: front and back can be exchanged,
yielding the same structure. This allows us to share code by parameterizing
certain operations with a point of view. *)(* We use the words [this] and [that] to refer to this side -- the one closest
to us, from our point of view -- and that side -- the other side. *)(* -------------------------------------------------------------------------- *)(* Basic accessors. *)let[@inline]defaults=matchswith|Zero{default;_}|One{default;_}|Short{default;_}->default|Level{front;_}->SChunk.defaultfront(* [dummy s] extracts a dummy schunk out of [s], if possible; otherwise, it
creates a fresh one. *)let[@inline]dummys=matchswith|Zero{default;_}|One{default;_}|Short{default;_}->SChunk.dummydefault|Level{middle;_}->(* The default element of the middle sequence is a dummy schunk. *)defaultmiddle(* [weight_of] is so named to avoid a clash with the field name [weight].
It is ultimately exported under the name [weight]. *)let[@inline]weight_ofs=matchswith|Zero_->0|One_->1|Short{a;_}->Array.lengtha|Level{weight;_}->weightlet[@inline]is_emptys=matchswith|Zero_->true|One_|Short_|Level_->false(* -------------------------------------------------------------------------- *)(* Construction and destruction. *)letcreatedefault=Zero{default}let[@inline]create_middledefault=letdefault=SChunk.dummydefaultincreatedefault(* [nonempty_level] is a constructor, parameterized by a point of view. As we
do not allow the constructor [Level] to represent an empty sequence, this
constructor cannot be used in the case where the sequence is empty. *)let[@specialise]nonempty_levelpovweightthismiddlethat=assert(weight>0);letfront,back=exchangepovthisthatinLevel{weight;front;middle;back}(* [level] is also a constructor, parameterized by a point of view. It
does not require the sequence to be nonempty. It does not guarantee
that the populated-sides invariant holds. *)let[@inline]levelpovweightthismiddlethat=ifweight=0thenletdefault=SChunk.defaultthisincreatedefaultelsenonempty_levelpovweightthismiddlethat(* [promote p o] converts the schunk [p] to a sequence. *)letpromotepo=letdefault=SChunk.defaultpinletweight=SChunk.weightpin(* An empty front schunk. *)letfront=SChunk.createdefault(SChunk.capacityp)oin(* An empty middle sequence. *)letmiddle=create_middledefaultin(* The schunk [p] becomes the back schunk. *)letback=p(* or: [SChunk.copy p o], but that would be more expensive *)inlevelFrontweightfrontmiddleback(* -------------------------------------------------------------------------- *)(* Iteration. *)let[@specialise]reciter_segments:'a.pov->'at->'asegments=funpovsyield->matchswith|Zero_->()|One{x;_}->yield([|x|],0,1)|Short{a;_}->yield(a,0,Array.lengtha)|Level{front;middle;back;_}->letthis,that=exchangepovfrontbackinSChunk.iter_segmentspovthisyield;iterpov(funp->SChunk.iter_segmentspovpyield)middle;SChunk.iter_segmentspovthatyieldand[@specialise]iter:'a.pov->('a->unit)->'at->unit=funpovfs->ArrayExtra.iteriter_segmentspovfsletiter_leftgs=iterFrontgsletiter_rightgs=iterBackgsletfold_leftfseeds=Adapters.fold_leftiter_leftfseedsletto_lists=Adapters.to_listiter_rights(* -------------------------------------------------------------------------- *)(* Conversion to an array. *)(* We assume zero depth and unit weight. Thus, the weight of the sequence
is also its length. *)(* The code in the last case, [Level], would in fact work in all cases,
but we can save a little time by dealing explicitly with each case. *)letto_arrays=matchswith|Zero_->[||]|One{x;_}->[|x|]|Short{a;_}->Array.copya|Level_->ArrayExtra.concat_segmentsFront(defaults)(weight_ofs)(iter_segmentsFronts)(* -------------------------------------------------------------------------- *)(* Printing. *)(* The [model] pseudo-field is printed only when [m] is [MUnit],
otherwise this creates too much noise. *)modulePrinting=structopenPPrintopenPPrint.OCamlletrecprint:'a.'ameasure->('a->document)->'at->document=funmelements->matchswith|Zero_->!^"Zero"|One{x;_}->!^"One"^^record"pwseq"["model",flowing_listelement[x]]|Short{a;_}->!^"Short"^^record"pwseq"["model",flowing_listelement(Array.to_lista)]|Level{front;middle;back;weight}->letschunk=SChunk.printmelementin!^"Level "^^record"pwseq"(["weight",intweight;"front",schunkfront;"middle",printMSWeightschunkmiddle;"back",schunkback;]@(ifm=MUnitthen["model",flowing_listelement(to_lists);]else[]))endincludePrinting(* -------------------------------------------------------------------------- *)(* Validation. *)letreccheck:'a.'at->'ameasure->owner->depth->unit=funsmodepth->matchswith|Zero_->()|One_|Short_->(* The data constructors [One] and [Short] are not allowed to appear
under [Level]. Furthermore, [check] must not be applied to a
sequence that has of these data constructors at the root. *)assertfalse|Level{weight;front;middle;back}->(* Since the sequence is nonempty, its weight must be positive. *)assert(weight>0);(* Check the populated-sides invariant. *)ifSChunk.is_emptyfront||SChunk.is_emptybackthenassert(is_emptymiddle);(* Check that the front and back schunks are well-formed. *)SChunk.checkmofront;SChunk.checkmoback;(* Check the middle sequence (before iterating on it). *)check_middlemiddlemodepth;(* Check that the weight is correct. *)assert(weight=SChunk.weightfront+fold_left(funsump->sum+SChunk.weightp)0middle+SChunk.weightback)andcheck_middle:'a.'aschunkt->'ameasure->owner->depth->unit=funmiddlemodepth->(* Check that the sequence is well-formed (before iterating on it). *)checkmiddleMSWeighto(depth+1);(* Check that every element in the middle sequence is a well-formed
schunk. Note that this is *not* a consequence of the fact that
the middle sequence is well-formed. *)iterFront(SChunk.checkmo)middle;(* Check the density invariant: in the middle sequence, the sum of the
lengths of any two adjacent schunks must exceed [capacity depth]. *)letn=capacitydepthinletprevious_length=refnin(* a fictitious full schunk *)iterFront(funp->assert(!previous_length+SChunk.lengthp>n);previous_length:=SChunk.lengthp)middle(* Ensure [check] has zero cost in release mode. *)let[@inline]checksmodepth=assert(checksmodepth;true)(* -------------------------------------------------------------------------- *)(* Peek. *)let[@specialise]peekpovs=matchswith|Zero_->raiseEmpty|One{x;_}->x|Short{a;_}->assert(2<=Array.lengtha);beginmatchpovwith|Front->Array.geta0|Back->letn=Array.lengthainArray.geta(n-1)end|Level{front;back;_}->(* Rename [front] and [back] to [this] and [that],
with a possible exchange, depending on [pov]. *)letthis,that=exchangepovfrontback(* Ensure [front] and [back] are not used below. *)andfront,back=(),()infront;back;ifnot(SChunk.is_emptythis)thenSChunk.peekpovthiselseSChunk.peekpovthat(* -------------------------------------------------------------------------- *)(* Push. *)let[@specialise]recpush:'a.pov->'at->'a->'ameasure->owner->depth->'at=funpovsxmodepth->matchswith|Zero{default}->(* Create an empty schunk. *)letp=SChunk.createdefault(capacitydepth)oin(* Push [x] into it. *)letp=SChunk.pushpovpxmoin(* Promote it to a sequence. *)promotepo|One_|Short_->(* Not handled here. *)assertfalse|Level{weight;front;middle;back}->(* Rename [front] and [back] to [this] and [that],
with a possible exchange, depending on [pov]. *)letthis,that=exchangepovfrontback(* Ensure [front] and [back] are not used below. *)andfront,back=(),()infront;back;letweight=weight+applymxinifnot(SChunk.is_fullthis)thenbegin(* There is room in the front schunk. Push [x] into it. *)letthis=SChunk.pushpovthisxmoinnonempty_levelpovweightthismiddlethatendelseifSChunk.is_emptythatthenbegin(* The front schunk is full. *)(* The back schunk is empty. Per the populated-sides invariant,
the middle sequence must be empty as well. *)assert(is_emptymiddle);(* Push [x] into the back schunk and exchange the front and back
schunks, so [x] ends up in front, as desired. *)letthis,that=SChunk.pushpovthatxmo,thisinnonempty_levelpovweightthismiddlethatendelsebegin(* The front schunk is full. The middle sequence and back schunk
are nonempty. *)(* Create an empty schunk, push [x] into it, and make it the new
front chunk, while pushing the previous front chunk into the
[middle] sequence. *)letp=SChunk.create(SChunk.defaultthis)(capacitydepth)oinletthis,middle=SChunk.pushpovpxmo,pushpovmiddlethisMSWeighto(depth+1)innonempty_levelpovweightthismiddlethatend(* -------------------------------------------------------------------------- *)(* Pop. *)let[@specialise]recpop:'a.pov->'at->'ameasure->owner->'a*'at=funpovsmo->matchswith|Zero_->raiseEmpty|One_|Short_->(* Not handled here. *)assertfalse|Level{weight;front;middle;back}->(* Rename [front] and [back] to [this] and [that],
with a possible exchange, depending on [pov]. *)letthis,that=exchangepovfrontback(* Ensure [front] and [back] are not used below. *)andfront,back=(),()infront;back;ifSChunk.is_emptythisthenbegin(* The front schunk is empty. Per the populated-sides invariant,
the middle sequence must be empty as well. Thus, the back
schunk cannot be empty. *)assert(is_emptymiddle);assert(not(SChunk.is_emptythat));(* Pop an element [x] off the back schunk. *)letx,that=SChunk.poppovthatmoinletweight=weight-applymxinx,levelpovweightthismiddlethatendelsebegin(* Pop an element [x] off the front schunk. *)letx,this=SChunk.poppovthismoinletweight=weight-applymxin(* This test is identical to the one in [level]. *)ifweight=0thenletdefault=SChunk.defaultthisinx,createdefaultelse(* Restore the populated-sides invariant. *)(* This is a specialized version of [populate]. *)letthis,middle=ifSChunk.is_emptythis&¬(is_emptymiddle)thenpoppovmiddleMSWeightoelsethis,middleinx,nonempty_levelpovweightthismiddlethatend(* -------------------------------------------------------------------------- *)(* Restoring the populated-sides invariant. *)(* These functions use [pop], which is why they are placed here. *)(* [populate pov s o] restores one side of the populated-sides invariant. *)let[@inline]populatepovso=matchswith|Zero_->s|One_|Short_->(* Not handled here. *)assertfalse|Level{weight;front;middle;back}->(* Rename [front] and [back] to [this] and [that],
with a possible exchange, depending on [pov]. *)letthis,that=exchangepovfrontback(* Ensure [front] and [back] are not used below. *)andfront,back=(),()infront;back;ifSChunk.is_emptythis&¬(is_emptymiddle)thenbegin(* The invariant is broken on this side: the front chunk is
empty, yet the middle sequence is nonempty. Restore the
invariant by popping one element off the middle sequence
and making it the front chunk. *)letthis,middle=poppovmiddleMSWeightoinassert(not(SChunk.is_emptythis));nonempty_levelpovweightthismiddlethatendelses(* The smart constructor [populate_both_nonempty_level] requires the sequence
to be nonempty and restores the populated-sides invariant if necessary. It
is not parameterized by a point of view. *)letpopulate_both_nonempty_levelweightfrontmiddlebacko=assert(weight>0);lets=Level{weight;front;middle;back}inlets=populateFrontsoinlets=populateBacksoins(* The smart constructor [populate_nonempty_level] requires the sequence to be
nonempty and restores one side of the populated-sides invariant. *)let[@specialise]populate_nonempty_levelpovweightfrontmiddlebacko=assert(weight>0);lets=Level{weight;front;middle;back}inlets=populatepovsoins(* The smart constructor [populate_level] supports the case where the sequence
is empty and restores one side of the populated-sides invariant. *)let[@inline]populate_levelpovweightfrontmiddlebacko=ifweight=0thenletdefault=SChunk.defaultfrontincreatedefaultelsepopulate_nonempty_levelpovweightfrontmiddlebacko(* -------------------------------------------------------------------------- *)(* Constructors for sequences of a known size [size], operating only at depth
zero, with unit weights. *)(* [create_by_segments default size o create_schunk] builds a sequence
of length [size]. It expects a function [create_schunk] such that
[create_schunk n i k] produces a schunk of capacity [n] for the
subsequence of length [k] that begins at index [i]. *)letcreate_by_segmentsdefaultsizeocreate_schunk=(* We assume the depth is zero. *)letdepth=0in(* A fast path for empty sequences. *)ifsize=0thencreatedefaultelsebeginletn=capacitydepthin(* We assume unit weight. *)letweight=sizein(* This calling convention is more convenient here. *)let[@inline]create_schunk(i,k)=create_schunknikin(* Cut the range into a nonempty front segment, a possibly empty
series of segments of size [n], and a possibly empty back segment.
[cut] guarantees that if the back segment is empty then the middle
sequence is empty as well. *)letfront,foreach_middle_segment,back=ArrayExtra.cutnnsizein(* The front, middle, and back must be created in this order. Indeed,
[create_schunk] may have side effects, and the order of these side
effects matters. *)(* Construct the front. *)letfront=create_schunkfrontin(* Construct the middle. *)letmiddle=ref(create_middledefault)inforeach_middle_segment(funik->middle:=pushBack!middle(create_schunk(i,k))MSWeighto(depth+1));letmiddle=!middlein(* Construct the back. *)letback=create_schunkbackin(* Build a level. Because [cut] is designed to enforce the populated-sides
invariant by construction, there is no need to call [populate]. *)Level{weight;front;middle;back}end(* Conversion from an array. *)letof_array_segmentdefaultaheadsizeo=assert(Segment.is_valid(a,head,size));create_by_segmentsdefaultsizeo(funnik->(* Construct the segment [i, i + k) of the sequence using the elements in
the segment [head + i, head + i + k) of the array [a]. *)SChunk.of_array_segmentdefaultna(head+i)ko)letmakedefaultsizevo=assert(0<=size);(* Because this is a persistent sequence, we can create a single schunk of
capacity [n] and size [n], filled with the value [v], and re-use it over
and over. *)(* This optimization relies on the assumption that [o] is [Owner.none]. *)assert(o=Owner.none);(* We apply this optimization only at depth 0, not at greater depths (even
though it is possible in principle), because it is easy. *)letdepth=0inletn=capacitydepthinletp=SChunk.makedefaultnnvoincreate_by_segmentsdefaultsizeo(funn_ik->assert(n=SChunk.capacityp);(* We need a schunk of capacity [n] and size [k], filled with [k] copies
of the value [v]. *)(* If [k] is [n], we can use our ready-made schunk. If [k] is less than
[n], we create a new schunk with a smaller view, based on the same
support. *)ifk=nthenpelseletp1,_x=SChunk.takepkMUnitoinp1)letinitdefaultsizefo=assert(0<=size);create_by_segmentsdefaultsizeo(funnik->(* Allocate a schunk of capacity [n] and size [k],
filled with the values [f i], [f (i+1)], ..., [f (i+k-1)]. *)SChunk.initdefaultnkifo)(* -------------------------------------------------------------------------- *)(* [fuse_back middle p o depth] pushes the schunk [p] onto the back of
[middle], which is a sequence of schunks. If possible, the last schunk of
the sequence and the schunk [p] are fused into a single schunk. This allows
preserving the density invariant. *)(* By convention, pass [depth], not [depth + 1] -- [fuse_back] takes care of
adding one. *)let[@inline]fuse_backmiddlepodepth=ifSChunk.is_emptypthen(* [p] is empty. There is nothing to do. *)middleelseifis_emptymiddlethen(* [middle] is empty. Just push [p] into [middle]. *)pushBackmiddlepMSWeighto(depth+1)else(* The capacity at the current depth is given by [SChunk.capacity p]. *)letn=SChunk.capacitypinifSChunk.length(peekBackmiddle)+SChunk.lengthp>nthen(* Concatenating the last element of [middle] with [p] is forbidden.
Just push [p] into [middle]. *)pushBackmiddlepMSWeighto(depth+1)elsebegin(* [middle] is nonempty and its last element [last] can be concatenated
with [p]. Then, pop [last] off [middle], concatenate [last] and [p],
and push the result back into [middle]. *)letlast,middle=popBackmiddleMSWeightoinletlast=SChunk.concatlastpoinpushBackmiddlelastMSWeighto(depth+1)end(* -------------------------------------------------------------------------- *)(* [eject this middle that] returns either [this, that] or [that, this].
It allows itself to exchange [this] and [that] only when both [this]
and [middle] are empty, so the model of the sequence is not affected.
Provided the sequence [this/middle/that] is nonempty, we are assured
that the first component of the result is a nonempty schunk. *)let[@inline]ejectthismiddlethat=ifSChunk.is_emptythisthenbeginassert(is_emptymiddle);that,thisendelsethis,that(* -------------------------------------------------------------------------- *)(* Concatenation. *)letrecconcat:'a.'at->'at->owner->depth->'at=funs1s2odepth->matchs1,s2with|Zero_,s|s,Zero_->s|(One_|Short_),_|_,(One_|Short_)->(* Not handled here. *)assertfalse|Level{weight=weight1;front=front1;middle=middle1;back=back1},Level{weight=weight2;front=front2;middle=middle2;back=back2}->(* Weight. *)letweight=weight1+weight2in(* Exchange the front and back schunks of [s1], if necessary, to ensure
that its front schunk is nonempty. *)letfront1,back1=ejectfront1middle1back1inassert(not(SChunk.is_emptyfront1));(* Similarly, ensure that the back schunk of [s2] is nonempty. *)letback2,front2=ejectback2middle2front2inassert(not(SChunk.is_emptyback2));(* Get rid of [back1] and [front2] by pushing them into [middle1]. *)letmiddle1=fuse_backmiddle1back1odepthinletmiddle1=fuse_backmiddle1front2odepthin(* There remains to concatenate the two middles, *)letmiddle=fusemiddle1middle2odepthin(* and build a new level. *)populate_both_nonempty_levelweightfront1middleback2o(* By convention, pass [depth], not [depth + 1] -- [fuse] takes care of adding
one. *)andfuse:'a.'aschunkt->'aschunkt->owner->depth->'aschunkt=funs1s2odepth->ifis_emptys1thens2elseifis_emptys2thens1elseletlast1=peekBacks1andfirst2=peekFronts2inletn=SChunk.capacitylast1inassert(n=SChunk.capacityfirst2);ifSChunk.lengthlast1+SChunk.lengthfirst2>nthenconcats1s2o(depth+1)elseletfirst2,s2=popFronts2MSWeightoinlets1=fuse_backs1first2odepthinconcats1s2o(depth+1)(* -------------------------------------------------------------------------- *)(* Split. *)letrecthree_way_split:'a.'at->weight->'ameasure->owner->'at*'a*'at=funsimo->matchswith|Zero_->assertfalse|One_|Short_->(* Not handled here. *)assertfalse|Level{weight;front;middle;back}->assert(0<=i&&i<weight);letweight_front=SChunk.weightfrontinifi<weight_frontthenbegin(* The desired element lies in the front schunk. *)letfront1,x,front2=SChunk.three_way_splitfrontimoinletweight1=SChunk.weightfront1inlets1=promotefront1oinletweight2=weight-weight1-applymxinlets2=populate_levelFrontweight2front2middlebackoins1,x,s2endelseleti=i-weight_frontinletweight_middle=weight_ofmiddleinifweight_middle<=ithenbeginleti=i-weight_middlein(* The desired element lies in the back schunk. *)letback1,x,back2=SChunk.three_way_splitbackimoinletweight2=SChunk.weightback2inlets2=promoteback2oinletweight1=weight-weight2-applymxinlets1=populate_levelBackweight1frontmiddleback1oins1,x,s2endelsebegin(* The desired element lies in the middle. *)(* Split the middle sequence. *)letmiddle1,p,middle2=three_way_splitmiddleiMSWeightoin(* We now know that the desired element lies in the schunk [p]. *)letweight_middle1=weight_ofmiddle1inleti=i-weight_middle1in(* Split this schunk! *)letp1,x,p2=SChunk.three_way_splitpimoin(* To the left of [x], we have [front], [middle1], and [p1]. *)letweight1=SChunk.weightfront+weight_middle1+SChunk.weightp1inlets1=populate_nonempty_levelBackweight1frontmiddle1p1oin(* To the right of [x], we have [p2], [middle2], and [back]. *)letweight2=weight-weight1-applymxin(* or: SChunk.weight p2 + weight_of middle2 + SChunk.weight back *)lets2=populate_nonempty_levelFrontweight2p2middle2backoins1,x,s2end(* Check that [three_way_split] satisfies its postcondition. *)let[@inline]three_way_splitsimo=letresult=three_way_splitsimoinassert(lets1,x,_s2=resultinweight_ofs1<=i&&i<weight_ofs1+applymx);result(* -------------------------------------------------------------------------- *)(* Take. *)(* [take] is a specialized version of [three_way_split]. Instead of returning
a triple [s1, x, s2], it returns only the pair [s1, x]. *)letrectake:'a.'at->weight->'ameasure->owner->'at*'a=funsimo->matchswith|Zero_->assertfalse|One_|Short_->(* Not handled here. *)assertfalse|Level{weight;front;middle;back}->assert(0<=i&&i<weight);letweight_front=SChunk.weightfrontinifi<weight_frontthenbegin(* The desired element lies in the front schunk. *)letfront1,x=SChunk.takefrontimoinlets1=promotefront1oins1,xendelseleti=i-weight_frontinletweight_middle=weight_ofmiddleinifweight_middle<=ithenbeginleti=i-weight_middlein(* The desired element lies in the back schunk. *)letback1,x=SChunk.takebackimoinletweight1=SChunk.weightfront+weight_ofmiddle+SChunk.weightback1inlets1=populate_levelBackweight1frontmiddleback1oins1,xendelsebegin(* The desired element lies in the middle. *)(* Split the middle sequence. *)letmiddle1,p=takemiddleiMSWeightoin(* We now know that the desired element lies in the schunk [p]. *)letweight_middle1=weight_ofmiddle1inleti=i-weight_middle1in(* Split this schunk! *)letp1,x=SChunk.takepimoin(* To the left of [x], we have [front], [middle1], and [p1]. *)letweight1=SChunk.weightfront+weight_middle1+SChunk.weightp1inlets1=populate_nonempty_levelBackweight1frontmiddle1p1oins1,xend(* -------------------------------------------------------------------------- *)(* Drop. *)(* [drop] is a specialized version of [three_way_split]. Instead of returning
a triple [s1, x, s2], it returns only the pair [x, s2]. *)letrecdrop:'a.'at->weight->'ameasure->owner->'a*'at=funsimo->matchswith|Zero_->assertfalse|One_|Short_->(* Not handled here. *)assertfalse|Level{weight;front;middle;back}->assert(0<=i&&i<weight);letweight_front=SChunk.weightfrontinifi<weight_frontthenbegin(* The desired element lies in the front schunk. *)letx,front2=SChunk.dropfrontimoinletweight2=SChunk.weightfront2+weight_ofmiddle+SChunk.weightbackinlets2=populate_levelFrontweight2front2middlebackoinx,s2endelseleti=i-weight_frontinletweight_middle=weight_ofmiddleinifweight_middle<=ithenbeginleti=i-weight_middlein(* The desired element lies in the back schunk. *)letx,back2=SChunk.dropbackimoinlets2=promoteback2oinx,s2endelsebegin(* The desired element lies in the middle. *)(* Split the middle sequence. *)letp,middle2=dropmiddleiMSWeightoin(* We now know that the desired element lies in the schunk [p]. *)letweight_of_middle1=weight_ofmiddle-SChunk.weightp-weight_ofmiddle2inleti=i-weight_of_middle1in(* Split this schunk! *)letx,p2=SChunk.droppimoin(* To the right of [x], we have [p2], [middle2], and [back]. *)letweight2=SChunk.weightp2+weight_ofmiddle2+SChunk.weightbackinlets2=populate_nonempty_levelFrontweight2p2middle2backoinx,s2end(* -------------------------------------------------------------------------- *)(* Get. *)(* [get s i m] can be viewed as a specialized version of [three_way_split s i
m o]. Instead of returning a triple [s1, x, s2], it returns only the pair
[i - weight_of s1, x]. *)letrecget:'a.'at->weight->'ameasure->weight*'a=funsim->matchswith|Zero_|One_|Short_->assertfalse|Level{weight;front;middle;back}->assert(0<=i&&i<weight);letweight_front=SChunk.weightfrontinifi<weight_frontthen(* The desired element lies in the front schunk. *)SChunk.get_by_weightmfrontielseleti=i-weight_frontinletweight_middle=weight_ofmiddleinifweight_middle<=ithenleti=i-weight_middlein(* The desired element lies in the back schunk. *)SChunk.get_by_weightmbackielse(* The desired element lies in the middle. *)leti,p=getmiddleiMSWeightinSChunk.get_by_weightmpi(* -------------------------------------------------------------------------- *)(* Update. *)(* Whereas [get] returns a pair of an adjusted weight-index [i] and element
[x], [update] passes [i] and [x] to the update function [f] that it
receives as an argument. Thus, there is a CPS-style intuition. *)(* The code is written so as to preserve sharing: if possible, the resulting
sequence is physically equal to [s]. This can be the case either when the
sequence uniquely owns the schunk that is updated or when this schunk is
shared but the update has no effect. *)(* For simplicity, we assume that the update is weight-invariant, that is, it
replaces an element with a new element of equal weight. This allows us to
not update the [weight] field. The code could be generalized to allow
changes in the weight: it should be sufficient to take an additional
parameter [delta] and increase the weight by [delta] at every level. In
that case, as soon as [delta] is nonzero, sharing becomes impossible. *)letrecupdate:'a.'ameasure->owner->'aupdate->'atupdate=funmofsi->matchswith|Zero_->assertfalse|One_|Short_->(* Not handled here. *)assertfalse|Level{weight;front;middle;back}->assert(0<=i&&i<weight);letweight_front=SChunk.weightfrontinifi<weight_frontthen(* The desired element lies in the front schunk. *)letfront'=SChunk.update_by_weightmoffrontiiniffront==front'thenselseletfront=front'inLevel{weight;front;middle;back}elseleti=i-weight_frontinletweight_middle=weight_ofmiddleinifweight_middle<=ithenleti=i-weight_middlein(* The desired element lies in the back schunk. *)letback'=SChunk.update_by_weightmofbackiinifback==back'thenselseletback=back'inLevel{weight;front;middle;back}else(* The desired element lies in the middle sequence. *)letmiddle'=updateMSWeighto(SChunk.update_by_weightmof)middleiinifmiddle==middle'thenselseletmiddle=middle'inLevel{weight;front;middle;back}(* Set. *)let[@inline]setsimox'=letfxi=(* [x] and [x'] must have the same weight. *)assert(applymx=applymx');(* The weight index [i] must fall inside [x],
and inside [x'], which is the same thing. *)assert(0<=i&&i<applymx);x'inupdatemofsi(* For the record, a naive implementation of [set]:
let set s i m o x =
let s1, _, s2 = three_way_split s i m o in
concat s1 (push Front s2 x m o) o
*)(* -------------------------------------------------------------------------- *)(* Public bindings. *)letweight=weight_ofend(* Make *)