Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file Iterator.ml
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406(******************************************************************************)(* *)(* 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. *)(* *)(******************************************************************************)openPublicTypeAbbreviationsopenPrivateSignatures(* This module provides an implementation of iterators for a sequence data
structure. *)(* We assume that each element has a nonzero weight. The weight of an element
is given by a measure [m]. *)(* We assume that a sequence can be decomposed into a front schunk,
a middle sequence, and a back schunk. *)(* This functor can be instantiated both for shareable sequences and for
ephemeral sequences. In the case of shareable sequences, one must be
careful to never create an iterator on an empty sequence, because it cannot
(efficiently) be decomposed into front-middle-back. In the case of
ephemeral sequences, the front and back chunks must be wrapped within
artificial schunks. *)(* The hooks [S.front] and [S.back] must be considered costly: indeed, when
working with ephemeral sequences, these functions must wrap the front or
back chunk into an artificial schunk. *)(* The hook [S.weight_front s] is equivalent to [SChunk.weight (S.front s)],
but may be cheaper. *)module[@inline]Make(SChunk:SCHUNK)(M:sigtype'atvalweight:'at->weightvalis_empty:'at->boolvalget:'at->weight->'aSChunk.measure->weight*'aend)(S:sigtype'atvalweight:'at->weightvaldummy:'at->'aSChunk.tvalfront:'at->'aSChunk.tvalmiddle:'at->'aSChunk.tM.tvalback:'at->'aSChunk.tvalweight_front:'at->weightvalschunk_uniquely_owned:'at->'aSChunk.t->boolvalensure_schunk_uniquely_owned:'at->weight->'aSChunk.t->unittypebirthvaliterator_is_born:'at->birthvalis_valid:'at->birth->boolvalinvalidate_iterators:'at->unitvalinvalidate_iterators_except:'at->birthend)(I:WITERwithtype'ameasure='aSChunk.measureandtype'at='aM.t)=structtype'aschunk='aSChunk.ttype'ameasure='aSChunk.measure=|MUnit:'ameasure|MSWeight:'aschunkmeasuretype'at='aS.tletapply=SChunk.apply(* -------------------------------------------------------------------------- *)(* In this file, the elements of a sequence have nonzero weights. One must
be careful to distinguish between ordinary indices and weight indices. As
a general rule of thumb, indices into schunks are ordinary indices, and
every index that appears as a parameter to an iterator operation is a
weight index. *)(* The logical model of an iterator is an integer index comprised between [-1]
and [weight], both included, where [weight] is the weight of the sequence.
The special indices [-1] and [weight] represent two sentinel elements, one
in the front and one in the back. The ordinary indices in the semi-open
interval [\[0, weight)] designate the actual elements of the sequence.
The constructors are as follows:
- [create_at_sentinel Front] creates an iterator at index [-1].
- [create_at_sentinel Back] creates an iterator at index [weight].
- [create Front] creates an iterator at index [0].
- [create Back] creates an iterator at index [weight-1].
An existing iterator can be brought back to its initial state:
- [reset_to_sentinel Front] moves an iterator to index [-1].
- [reset_to_sentinel Back] moves an iterator to index [weight].
- [reset Front] moves an iterator to index [0].
- [reset Back] moves an iterator to index [weight-1].
In the public API, the constants [Front] and [Back] are exposed under the
names [forward] and [backward], as we believe that these names are less
confusing.
[finished it] tests whether the iterator currently points to a
sentinel (which can be the front or back sentinel). It is *not*
parameterized with a [pov]: it does not test whether the iterator
lies at a specific sentinel; it tests whether the iterator lies at
any of the two sentinels. This semantics can be implemented more
efficiently. This API is good enough in practice and is syntactically
more lightweight.
[get it] reads the element pointed to by the iterator. It raises [End] if
the iterator points to a sentinel, that is, if [finished it] is [true].
[move pov it] moves the iterator to the next element in the sequence, in
the specified direction. Invoking [move] when the iterator already points
to the final sentinel element is illegal. We detect this situation and
raise [Invalid_argument _], but the user must not rely on this behavior. *)(* The implementation is designed so as to minimize the cost of [move] as
long as the iterator moves within a schunk. It is okay for [move] to
be more expensive when the iterator moves to another schunk. *)(* -------------------------------------------------------------------------- *)(* A path describes the position of the iterator inside the sequence. A path
is one of the following:
[PathSentinel pov] indicates that the iterator points to the front or
back sentinel.
[PathSide pov] indicates that the iterator points to an element inside
the front or back schunk. In that case, [schunk] is the front or back
schunk of the sequence.
[PathMiddle it], where [it] is an iterator on the middle sequence,
indicates that the iterator points into the middle sequence. In that
case, [schunk] is an element of the middle sequence (which is a sequence
of schunks). *)type'apath=|PathSentinelofpov|PathSideofpov|PathMiddleof'aschunkI.iter(* -------------------------------------------------------------------------- *)(* Naming conventions. *)(* Many fields in the [iterator] record represent an index of some kind. *)(* One must be careful not to confuse an ordinary *index*, typically an
index into an array or array segment, and a *weight index*, a notion
that exists when elements have a weight -- the weight index of an
element is the sum of the weights of the elements that precede it.
To distinguish indices and weight indices, we use the following
prefixes:
i_ indicates an index
w_ indicates a weight index
When the measure [m] is [MUnit], the two notions coincide. *)(* An index is always the index *of* something *with respect to* something.
For the the things whose index we manipulate, we use the following
conventional abbreviations:
cur = current element; the element the iterator points to
sup = support array; the data array of the current schunk
shd = segment head; the first element of the current segment
stl = segment tail; the last element of the current segment
sch = schunk; the current schunk, composed of at most two segments
seq = sequence; the complete sequence
For the index of foo with respect to bar, we write [i_foo_bar]; similarly,
for the weight index of foo with respect to bar, we write [w_foo_bar].
One can remember that [i_foo_bar] stands for [i_foo - i_bar].
A leading underscore indicates a computed field: e.g. [_i_cur_shd] is
not actually a field; it is an auxiliary function. *)(* -------------------------------------------------------------------------- *)(* An iterator contains the following fields:
[seq] is the sequence on which the iterator operates. This field allows
us, among other things, to navigate between the [front], [middle], and
[back] fields of the sequence.
[weight] is the weight of the sequence [seq]. It is cached here for
faster access.
[support], [i_shd_sup], and [i_stl_sup] represent an array segment that
contains the element currently pointed to by the iterator. The iterator
does not own this array; it is part of the sequence data structure.
[i_cur_sup] is a pointer into this array segment; it is the index of the
current element with respect to the [support] array.
More specifically:
- [birth] contains information about the birth date of the iterator,
that is, the moment when it was created or last reset. This allows
testing whether the iterator is valid. This field is useful only
when iterating over an ephemeral sequence, and only when dynamic
detection of invalid iterators is enabled; nevertheless, it is
always present.
- [support] is an array that contains the element currently pointed to by
the iterator. If the iterator points to a sentinel, [support] is
[dummy_support], an empty array.
- [i_shd_sup] is an index into the array [support]. It is the lower end
(inclusive) of the array segment of interest.
If the iterator points to a sentinel, [i_shd_sup] is zero.
- [i_stl_sup] is an index into the array [support]. It is the upper end
(exclusive) of the array segment of interest.
If the iterator points to a sentinel, [i_stl_sup] is zero.
- [i_cur_sup] is an index into the array [support]. It is the index of the
element currently pointed to by the iterator.
If the iterator points to a sentinel, [i_cur_sup] is zero.
[path] describes the position of the iterator in the sequence: the
iterator may points to the front or back sentinel, at the front or back
schunk, or into the middle sequence.
[schunk] is the schunk under focus, that is, the schunk that contains the
element under focus. This schunk is not owned by the iterator: it is part
of the sequence. It may be the front schunk, the back schunk, or one of
the schunks stored in the middle sequence. When the iterator points to a
sentinel (and only then), [schunk] is a dummy schunk. The array segment
described by [support], [i_shd_sup], [i_stl_sup] is a segment of this schunk.
[i_shd_sch] is the index with respect to the current schunk of the head
element of the current segment. When the iterator points to a sentinel,
[i_shd_sch] is zero.
(Only when [m] is not [MUnit].) [w_cur_seq] is the weight index with respect
to the complete sequence of the current element. That is, [w_cur_seq] is the
cumulated weight of the elements found strictly to the left of the current
element. When the iterator points to the front sentinel, [w_cur_seq] is [-1].
When the iterator points to the back sentinel, [w_cur_seq] is [weight], the
total weight of the sequence.
When [m] is [MUnit], the field [w_cur_seq] is not maintained. This saves
a write in the critical section of [move]. In that case, instead, [w_cur_seq]
is computed based on [w_shd_seq], which is described next.
In either case, the function [_w_cur_seq] computes the weight index with
respect to the complete sequence of the current element.
(Only when [m] is [MUnit].) [w_shd_seq] is the weight index with
respect to the complete sequence of the head element of the current
segment. When the iterator points to a sentinel, [w_shd_seq] is [-1]
or [length s].
LATER: we could save a word by storing [w_shd_seq] in the same field
as [w_cur_seq]. This appears too error-prone for the time being.
If the sequence is empty, then the iterator always points to a sentinel,
since there are no actual elements. *)type'aiter={mutablebirth:S.birth;mutablei_cur_sup:index;(* [_i_cur_sch] is a computed field *)mutablei_shd_sup:index;mutablei_stl_sup:index;mutablesupport:'aarray;mutablew_cur_seq:weight;(* only when [m <> MUnit] *)(* [_w_cur_seq] is a computed field *)mutablei_shd_sch:index;mutablew_shd_seq:weight;(* only when [m = MUnit] *)mutableschunk:'aschunk;mutablepath:'apath;mutableweight:weight;seq:'at;}(* [dummy_weight_index] is used to fill the fields [w_cur_seq] and [w_shd_seq]
when they are not in use. *)letdummy_weight_index=-2(* [dummy_support] is used to fill the field [support] when the iterator
points to a sentinel. *)letdummy_support=[||](* -------------------------------------------------------------------------- *)(* [is_valid it] tests whether the iterator [it] is valid. *)(* Depending on the parameter [check_iterator_validity], the function
[S.is_valid] either performs an actual runtime test, or returns [true]
unconditionally. We need not be concerned with this here. *)let[@inline]is_validit=S.is_validit.seqit.birth(* -------------------------------------------------------------------------- *)(* Computed fields. *)(* [_i_cur_shd it] computes the index of the current element with respect to
the current segment. *)(* When the iterator points at a sentinel, this is zero. *)let[@inline]_i_cur_shdit:index=it.i_cur_sup-it.i_shd_sup(* [_i_cur_sch it] computes the index of the current element with respect
to [schunk]. It is obtained by adding [i_shd_sch], the index of the
segment head with respect to the schunk, and [_i_cur_shd it], the
index of the current element with respect to the segment head. *)(* When the iterator points at a sentinel, this is zero. *)let[@inline]_i_cur_schit:index=it.i_shd_sch+_i_cur_shdit(* [_w_cur_seq it m] computes the weight index with respect to the complete
sequence of the current element. If [m] is [MUnit], it is computed as the
sum of the weight index of the segment head and the index of the current
element with respect to the segment head. Otherwise, it is read in the
[w_cur_seq] field. *)let[@inline]_w_cur_seq(typea)it(m:ameasure):weight=matchmwith|MUnit->it.w_shd_seq+_i_cur_shdit|MSWeight->it.w_cur_seq(* -------------------------------------------------------------------------- *)(* Validity of an iterator. *)(* [check it m] checks that the iterator [it] satisfies all of the invariants
that have been described above. *)letcheck(typea)(it:aiter)(m:ameasure)=(* Read all fields and computed fields. *)let{birth=_;i_cur_sup;i_shd_sup;i_stl_sup;support;w_cur_seq=stored_w_cur_seq;schunk;i_shd_sch;w_shd_seq;path;weight;seq}=itinleti_cur_sch=_i_cur_schitinletw_cur_seq=_w_cur_seqitmin(* Verify that the iterator is valid. This involves [birth]. *)assert(is_validit);(* Check [weight]. *)assert(weight=S.weightseq);(* Check [w_cur_seq] and [w_shd_seq]. *)beginmatchmwith|MUnit->assert(stored_w_cur_seq=dummy_weight_index);assert(-1<=w_shd_seq&&w_shd_seq<=weight);|MSWeight->assert(stored_w_cur_seq=w_cur_seq);assert(w_shd_seq=dummy_weight_index);end;assert(-1<=w_cur_seq&&w_cur_seq<=weight);(* Check [support], [i_shd_sup], [i_stl_sup], [i_cur_sup], [i_shd_sch]. *)ifnot(SChunk.is_dummyschunk)thenbegin(* These checks are redundant with what follows; keep them anyway. *)assert(0<=i_shd_sup&&i_shd_sup<Array.lengthsupport);assert(i_shd_sup<=i_stl_sup&&i_stl_sup<=Array.lengthsupport);assert(i_shd_sup<=i_cur_sup&&i_cur_sup<i_stl_sup);(* Check that [i_cur_sch] is a valid index into [schunk]. *)assert(0<=i_cur_sch&&i_cur_sch<SChunk.lengthschunk);(* Check that [(support, i_shd_sup, i_stl_sup - i_shd_sup)] is
a segment of [schunk], and is the segment that contains [i_cur_sch]. *)SChunk.iteri_segments_frontschunk(funhead((a,i,k)asseg)->assert(Segment.is_validseg);assert(k>0);ifhead<=i_cur_sch&&i_cur_sch<head+kthenbegin(* [(a, i, k)] is *the* segment of [schunk] that contains [i_cur_sch].
Check that it coincides with the segment
[(support, i_shd_sup, i_stl_sup - i_shd_sup)]. *)assert(a==support);assert(i=i_shd_sup);assert(k=i_stl_sup-i_shd_sup);(* Check [i_shd_sch]. *)assert(i_shd_sch=head);end);endelsebeginassert(support==dummy_support);assert(i_cur_sup=0);assert(i_shd_sup=0);assert(i_stl_sup=0);assert(i_shd_sch=0);end;(* Check [w_cur_seq], [i_cur_sch] and [path]. *)matchpathwith|PathSentinelpov->assert(SChunk.is_dummyschunk);assert(i_cur_sch=0);(* bonus *)beginmatchpovwith|Front->assert(w_cur_seq=-1)|Back->assert(w_cur_seq=weight)end|PathSidepov->assert(not(SChunk.is_dummyschunk));letfront,back=S.frontseq,S.backseqinletthis,_that=exchangepovfrontbackin(* Because [S.front] and [S.back] may construct an artificial
schunk, we cannot expect physical equality of the schunks,
but we can check the physical equality of the underlying chunks. *)assert(SChunk.supportschunk==SChunk.supportthis);(* Compute a weight index relative to this schunk. *)letw_cur_sch=matchpovwith|Front->w_cur_seq|Back->(* Subtract the weight index of the beginning of [back]. *)w_cur_seq-(weight-SChunk.weightback)in(* Check that [w_cur_sch] is a valid weight index into this schunk and
designates the element at index [i_cur_sch]. *)assert(0<=w_cur_sch&&w_cur_sch<SChunk.weightschunk);assert((w_cur_sch,i_cur_sch)=SChunk.reachmschunkw_cur_sch)|PathMiddleit_middle->assert(not(SChunk.is_dummyschunk));(* The middle iterator must be well-formed. *)I.checkit_middleMSWeight;(* A middle iterator never points to a sentinel. *)assert(not(I.finishedit_middleMSWeight));letfront=S.frontseqin(* Compute a weight index relative to the middle sequence. *)letw_cur_mid=w_cur_seq-SChunk.weightfrontin(* [it_middle] must be an iterator on our middle sequence. *)letmiddle=S.middleseqinassert(I.sequenceit_middle==middle);(* Fetch the schunk [p] designated by this weight index,
and obtain a weight index into this schunk. This new
[w_cur_sch] is the previous [w_cur_mid] minus the weight of
the elements that precede [p] in the sequence [middle]. *)letw_cur_sch,p=M.getmiddlew_cur_midMSWeightin(* Check that the iterator points to schunk [p]. *)assert(schunk==p);(* Check that [w_cur_sch] is a valid weight index into this schunk and
designates the element at index [i_cur_sch]. *)assert(0<=w_cur_sch&&w_cur_sch<SChunk.weightschunk);assert((w_cur_sch,i_cur_sch)=SChunk.reachmschunkw_cur_sch);(* Check that the middle iterator's weight index agrees with ours. *)assert(I.windexit_middleMSWeight+w_cur_sch=w_cur_mid);(* If [m] is [MUnit], then [w_cur_sch] must match exactly [i_cur_sch].
Furthermore, [w_shd_seq] must match the weight of [front] plus the
current weight index of the middle sequence, plus [i_shd_sch]. *)ifm=MUnitthenbeginassert(w_cur_sch=i_cur_sch);assert(w_shd_seq=SChunk.weightfront+I.windexit_middleMSWeight+i_shd_sch);end(* Ensure [check] has zero cost in release mode. *)let[@inline]checkitm=assert(checkitm;true)(* -------------------------------------------------------------------------- *)(* Printing an iterator. (For debugging purposes.) *)modulePrinting=structopenPPrintopenPPrint.OCamlletprint_pathelementpathm=matchpathwith|PathSentinelpov->!^("PathSentinel "^show_povpov)|PathSidepov->!^("PathSide "^show_povpov)|PathMiddleit_middle->!^"PathMiddle "^^I.print(SChunk.printmelement)it_middleMSWeightletprint_iterelement({path;schunk;i_cur_sup;i_shd_sup;i_stl_sup;i_shd_sch;w_shd_seq;_}asit)m=letw_cur_seq=_w_cur_seqitmandi_cur_sch=_i_cur_schitinrecord"piter"(["is_valid",bool(is_validit);"i_cur_sup",inti_cur_sup;"i_shd_sup",inti_shd_sup;"i_stl_sup",inti_stl_sup;(* [support] is not shown *)"w_cur_seq (computed)",intw_cur_seq;"i_cur_sch (computed)",inti_cur_sch;"schunk",SChunk.printmelementschunk;"i_shd_sch",inti_shd_sch;]@(ifm=MUnitthen["w_shd_seq",intw_shd_seq;]else[])@["path",print_pathelementpathm;])endletprint=Printing.print_iter(* -------------------------------------------------------------------------- *)(* Basic accessors. *)(* [sequence it] returns the sequence associated with the iterator. *)let[@inline]sequenceit=it.seq(* [dummy_schunk it] extracts a dummy schunk out of the iterator [it], if
possible; otherwise, it creates a fresh one. *)let[@inline]dummy_schunkit=ifSChunk.is_dummyit.schunkthenit.schunkelseS.dummyit.seq(* [finished it m] returns [true] if the iterator points to a sentinel.
We have three possible implementations of it, respectively based on
[path], on [w_cur_seq], and on [i_shd_sup] and [i_stl_sup]. We choose
the last one, which seems cheapest. *)let_finishedit_m=assert(is_validit);matchit.pathwith|PathSentinel_->true|PathSide_|PathMiddle_->falselet_finisheditm=assert(is_validit);letw_cur_seq=_w_cur_seqitminw_cur_seq=-1||w_cur_seq=it.weightlet[@inline]finishedit_m=assert(is_validit);(* The current segment, delimited by [i_shd_sup] and [i_stl_sup],
is empty if and only if the iterator points to a sentinel. *)it.i_shd_sup=it.i_stl_sup(* [unchecked_get it] requires the iterator to *not* point to a sentinel. *)let[@inline]unchecked_getitm=assert(not(finisheditm));Array.getit.supportit.i_cur_suplet[@inline]getitm=assert(is_validit);iffinisheditmthenraiseEndelseunchecked_getitm(* [unchecked_current_weight it m] returns the weight of the current element.
The iterator must not point to a sentinel. *)(* It so happens that this function is invoked only when [m] is [MSWeight].
We make this equality a precondition and simplify the code. It is not
clear that the compiler can be trusted to do this. *)let[@inline]unchecked_current_weightitm=assert(not(finisheditm));assert(m=MSWeight);letx=unchecked_getitminSChunk.weightx(* or: [apply m x] *)(* [current_weight it m] returns the weight of the current element. *)(* By convention, the weight of a sentinel is one. At depth zero, this
convention enables the weight index and the index of every element
(including sentinels) to coincide. *)(* It so happens that this function is invoked only when [m] is [MSWeight].
We make this equality a precondition and simplify the code. It is not
clear that the compiler can be trusted to do this. *)let[@inline]current_weightitm=assert(m=MSWeight);iffinisheditmthen1elseunchecked_current_weightitm(* [unchecked_is_at_weight it w m] is equivalent to [is_at_weight i w m]
(below), but assumes that the iterator is not currently at a sentinel. *)(* It so happens that this function is invoked only when [m] is [MSWeight].
We make this equality a precondition and simplify the code. It is not
clear that the compiler can be trusted to do this. *)let[@inline]unchecked_is_at_weightitmw=(* [w] must be a valid, non-sentinel weight index. *)assert(m=MSWeight);letw=w-it.w_cur_seq(* or: [w_cur_seq it m] *)in0<=w&&w<unchecked_current_weightitm(* [is_at_weight it w m] tests whether the iterator [it] points to the element
designated by the weight index [w], that is, whether [w - w_cur_seq it m]
lies between [0] included and the weight of the current element excluded. *)(* At the time of writing this comment, [is_at_weight] is used only inside
assertions. *)letis_at_weight(typea)(it:aiter)(m:ameasure)w:bool=(* [w] must be a valid weight index. *)assert(-1<=w&&w<=it.weight);matchmwith|MUnit->w=_w_cur_seqitm|MSWeight->letw=w-_w_cur_seqitmin0<=w&&w<current_weightitm(* -------------------------------------------------------------------------- *)(* Creating an iterator. *)(* [sentinel pov s] returns the weight index of the front or back sentinel. *)let[@inline]sentinelpovs=matchpovwith|Front->-1|Back->S.weightslet[@specialise]create_at_sentinel=fun(typea)pov(s:at)(m:ameasure)->letw_cur_seq,w_shd_seq=matchmwith|MUnit->dummy_weight_index,sentinelpovs|MSWeight->sentinelpovs,dummy_weight_indexin{birth=S.iterator_is_borns;i_cur_sup=0;i_shd_sup=0;i_stl_sup=0;support=dummy_support;w_cur_seq;schunk=S.dummys;i_shd_sch=0;w_shd_seq;path=PathSentinelpov;weight=S.weights;seq=s;}(* -------------------------------------------------------------------------- *)(* Copying an iterator. *)letcopy_pathpath=matchpathwith|PathSentinel_|PathSide_->path|PathMiddleit_middle->PathMiddle(I.copyit_middle)letcopyit=assert(is_validit);{itwithpath=copy_pathit.path}(* -------------------------------------------------------------------------- *)(* Moving an iterator. Auxiliary functions. *)(* [assign_dummy_schunk] is used when moving an iterator to a sentinel.
It assigns appropriate dummy values to the fields that describe the
current schunk and segment. *)let[@inline]assign_dummy_schunkit=(* Values exploited by [move]. *)it.i_cur_sup<-0;it.i_shd_sup<-0;it.i_stl_sup<-0;(* Assignments required to avoid memory leaks. *)it.schunk<-dummy_schunkit;(* Bonus assignments, required by [check]. *)it.i_shd_sch<-0;it.support<-dummy_support(* [assign_segment it i_cur_sch i_shd_sch seg] assigns the fields [i_shd_sch],
[i_shd_sup], [i_stl_sup], and [i_cur_sup]. *)let[@inline]assign_segmentiti_cur_schi_shd_schseg=let(support,i_shd_sup,size)=seginassert(0<=i_cur_sch&&i_cur_sch<SChunk.lengthit.schunk);assert(0<=i_shd_sch&&i_shd_sch<SChunk.weightit.schunk);assert(Segment.is_validseg);assert(it.support==support);it.i_shd_sch<-i_shd_sch;it.i_shd_sup<-i_shd_sup;it.i_stl_sup<-i_shd_sup+size;it.i_cur_sup<-i_shd_sup+i_cur_sch-i_shd_sch;assert(_i_cur_schit=i_cur_sch)(* [assign_w_cur_seq it w_cur_seq m] updates [w_cur_seq], either directly
or by updating [w_shd_seq] if [m = MUnit]. This operation assumes that
the segment-related fields are already correctly set up: indeed,
when [m = MUnit], the code evaluates [it.i_cur_sup - it.i_shd_sup]. *)let[@inline]assign_w_cur_seq(typea)itw_cur_seq(m:ameasure)=beginmatchmwith|MUnit->it.w_shd_seq<-w_cur_seq-_i_cur_shdit|MSWeight->it.w_cur_seq<-w_cur_seqend;assert(_w_cur_seqitm=w_cur_seq)(* [assign_in_current_schunk it i_cur_sch w_cur_seq m] updates the current
segment and the field [w_cur_seq] so that the iterator points to the
element at index [i_cur_sch] in the current schunk. The weight index of
that element in the sequence must be [w_cur_seq]. *)(* The function is implemented by finding out which of the segments of the
schunk covers [i_cur_sch]. *)letassign_in_current_schunkiti_cur_schw_cur_seqm=letn=SChunk.lengthit.schunkinassert(0<=i_cur_sch&&i_cur_sch<n);let(_,_,k1)asseg1=SChunk.segment_maxFront0it.schunkin(* Install the segment that covers [i_cur_sch]. There are two
possibilities. *)letsegment,i_shd_sch=ifi_cur_sch<k1then(* [i_cur_sch] falls in the first segment (possibly the only segment). *)seg1,0elsebeginassert(k1<=i_cur_sch);(* [i_cur_sch] falls in the second segment. *)let(_,_,k2)asseg2=SChunk.segment_maxFrontk1it.schunkin(* The union of [seg1] and [seg2] necessarily covers the whole schunk. *)assert(k1+k2=n);seg2,k1endin(* Update the current segment. *)assign_segmentiti_cur_schi_shd_schsegment;(* Update the weight index. (This operation must be performed after
the call to [assign_segment]). *)assign_w_cur_seqitw_cur_seqm(* [assign it path schunk i_cur_sch w_cur_seq m] updates the [path] and
[schunk] fields, then invokes [assign_in_current_schunk it i_cur_sch
w_cur_seq m] to update the current segment and the field [w_cur_seq]. *)let[@inline]assignitpathschunki_cur_schw_cur_seqm=it.path<-path;it.schunk<-schunk;it.support<-SChunk.dataschunk;assign_in_current_schunkiti_cur_schw_cur_seqm(* During a move operation, assuming that [schunk] and [i_cur_sch] represent
the new values of the [schunk] and [i_cur_sch] fields, and assuming that
the iterator is moving by exactly one element, [new_w_cur_seq pov it m
schunk i_cur_sch] is the new value of [w_cur_seq]. *)let[@specialise]new_w_cur_seq=fun(typea)pov(it:aiter)(m:ameasure)schunki_cur_sch->matchmwith|MUnit->_w_cur_seqitm+signpov|MSWeight->matchpovwith|Front->(* The iterator is moving forward. Increase [w_cur_seq] by the
weight of the element that we are leaving behind us. It is the
element under focus before moving. *)_w_cur_seqitm+current_weightitm|Back->(* The iterator is moving backward. Decrease [w_cur_seq] by the
weight of the element that we are leaving behind us. It is the
element under focus after moving. *)_w_cur_seqitm-applym(SChunk.getschunki_cur_sch)(* [reset_to_sentinel pov it m] moves the iterator [it] to a sentinel element.
Thus, it resets the iterator [it] to the same state as if it had just been
created by [create_at_sentinel pov (sequence it) m]. *)letreset_to_sentinelpovitm=lets=it.seqin(* Warn the sequence that it must be ready for iteration, and obtain a
possibly new birth date. *)it.birth<-S.iterator_is_borns;(* Because the sequence may have been modified after this iterator was
created, the [weight] field must be updated. *)it.weight<-S.weights;it.path<-PathSentinelpov;assign_dummy_schunkit;(* Update the weight index. (This operation must be performed after
the call to [assign_dummy_schunk].) *)assign_w_cur_seqit(sentinelpovs)m(* [get_middle_iterator it middle] retrieves an existing iterator on the
middle sequence [middle], or creates one. If a new one is created, it is
starts on the side that seems more likely to be accessed. *)letget_middle_iteratoritmiddle=matchit.pathwith|PathMiddleit_middle->(* A middle iterator exists. *)it_middle|PathSentinelpov|PathSidepov->(* A middle iterator must be created. *)I.create_at_sentinelpovmiddleMSWeight(* -------------------------------------------------------------------------- *)(* Moving an iterator. *)(* [move_finished pov it m] implements [move pov it m] in the case where
[move] brings the iterator to the final sentinel. *)let[@inline]move_finishedpovitm=reset_to_sentinel(dualpov)itm(* [move_enter_schunk pov it m path schunk] is invoked when the iterator
enters the schunk [schunk] sequentially, that is, either at the front
or at the back. *)let[@specialise]move_enter_schunkpovitmpathschunk=(* Focus on the first or last element of [schunk], depending on [pov]. *)leti_cur_sch=matchpovwith|Front->0|Back->SChunk.lengthschunk-1in(* Compute the new [w_cur_seq]. *)letw_cur_seq=new_w_cur_seqpovitmschunki_cur_schin(* Update the iterator. *)assignitpathschunki_cur_schw_cur_seqm(* [move_to_that pov it m that] implements [move pov it m] in the
special case where the iterator must move to the back schunk [that]. *)let[@specialise]move_to_thatpovitmthat=ifnot(SChunk.is_emptythat)then(* [that] is nonempty. Move to its first element. *)move_enter_schunkpovitm(PathSide(dualpov))thatelse(* [that] is empty. We have exhausted the entire sequence. *)move_finishedpovitm(* [move_next_schunk pov it m] implements [move pov it m] in the case where
the iterator is at the end of a schunk and must move to the next schunk. *)let[@specialise]move_next_schunkpovitm=(* Rename [S.front] and [S.back] to [this] and [that],
with a possible exchange, depending on [pov]. *)letthis,that=exchangepovS.frontS.backin(* We are careful to invoke [S.front] and [S.back]
only if needed, as these functions may be costly. *)matchit.pathwith|PathSentinelit_pov->ifpov<>it_povthen(* The iterator is already at the final sentinel. Moving beyond
the sentinel is not permitted by the specification of [move]. *)(* This dynamic check is performed here, as opposed to up front,
for efficiency reasons. *)invalid_arg"move: attempt to move beyond the sentinel"elselets=it.seqinletthis=thissinifnot(SChunk.is_emptythis)then(* The next nonempty schunk is [this]. Move to it. *)move_enter_schunkpovitm(PathSidepov)thiselsebegin(* [this] is empty. So is [middle]. Move to [that]. *)assert(M.is_empty(S.middles));move_to_thatpovitm(thats)end|PathSideit_pov->ifpov<>it_povthen(* The iterator is at the end of the back schunk. Move to the
final sentinel. *)move_finishedpovitm(* The iterator is currently at the end of the front schunk. *)elselets=it.seqinletmiddle=S.middlesinifnot(M.is_emptymiddle)thenbegin(* [middle] is nonempty. Move to the first element of [middle],
which is a schunk. *)letit_middle=I.createpovmiddleMSWeightinletp=I.unchecked_getit_middleMSWeightinmove_enter_schunkpovitm(PathMiddleit_middle)pendelse(* [middle] is empty. Move to [that]. *)move_to_thatpovitm(thats)|PathMiddleit_middle->(* The iterator has reached the end of a schunk that is an element
of the middle sequence. *)(* Move the middle iterator to the next schunk, if there is one,
or to its final sentinel. *)I.movepovit_middleMSWeight;ifI.finishedit_middleMSWeightthenbegin(* [middle] is exhausted. Move to [that]. *)lets=it.seqinmove_to_thatpovitm(thats)endelsebegin(* [middle] contains another schunk. Move into it. *)letp=I.unchecked_getit_middleMSWeightinmove_enter_schunkpovitmit.pathpend(* [move_from_sentinel] is a special case of [move] where it is known that the
iterator is currently at a sentinel. It exists mainly for documentation
purposes. *)let[@inline]move_from_sentinelpovitm=assert(finisheditm);(* Because the iterator is at a sentinel, [move] can be replaced with
[move_next_schunk]. *)move_next_schunkpovitm(* [move_next_segment pov it m] implements [move pov it m] in the case where
the iterator is at the end of the current segment and must move on to the
next segment. *)let[@specialise]move_next_segmentpovitm=(* [i_cur_sch] denotes the new index in the current schunk, possibly
one-past-the-end. *)leti_cur_sch=_i_cur_schit+signpovin(* Test if there is one more segment inside the current schunk.
(A schunk is made of either one or two segments.) *)letcurrent_schunk_has_next_segment=matchpovwith|Front->i_cur_sch<SChunk.lengthit.schunk|Back->0<=i_cur_schinifcurrent_schunk_has_next_segmentthenbeginassert(0<=i_cur_sch&&i_cur_sch<SChunk.lengthit.schunk);letw_cur_seq=new_w_cur_seqpovitmit.schunki_cur_schin(* LATER We could use a specialized version of [assign_in_current_schunk]
where it is known that [i_cur_sch] is the head of the second segment of
the schunk. This might allow saving a call to [SChunk.segment_max]. *)assign_in_current_schunkiti_cur_schw_cur_seqm;endelsemove_next_schunkpovitmlet[@specialise]move(typea)pov(it:aiter)(m:ameasure)=assert(is_validit);(* First, attempt to move the iterator within the current segment. If we
have reached the end of this segment, then call [move_next_segment]. This
code works also in the case where the iterator points to a sentinel,
because in that case [it.i_cur_sup], [it.i_shd_sup] and [it.i_stl_sup]
are all equal to zero. *)matchpovwith|Front->leti_cur_sup'=it.i_cur_sup+1inifi_cur_sup'<it.i_stl_supthenbegin(* [i_stl_sup] is exclusive *)(* This [match] construct may seem heavy, but an [if] construct
will not type-check here. *)beginmatchmwithMUnit->()|MSWeight->it.w_cur_seq<-it.w_cur_seq+unchecked_current_weightitmend;(* This assignment does *not* commute with the call to
[unchecked_current_weight] above. *)it.i_cur_sup<-i_cur_sup';endelsemove_next_segmentpovitm|Back->ifit.i_cur_sup>it.i_shd_supthenbegin(* [i_shd_sup] is inclusive *)it.i_cur_sup<-it.i_cur_sup-1;beginmatchmwithMUnit->()|MSWeight->it.w_cur_seq<-it.w_cur_seq-applym(unchecked_getitm)endendelsemove_next_segmentpovitm(* [create] is equivalent to the sequential composition of
[create_at_sentinel] and [move]. *)let[@specialise]createpovsm=letit=create_at_sentinelpovsminmove_from_sentinelpovitm;it(* -------------------------------------------------------------------------- *)(* Moving an iterator: random access. *)(* [reach it target m] moves the iterator to the element designated by the
weight index [target]. [target] must be comprised between [-1] and
[it.weight], both included. *)(* [reach_inside it target m] serves the same purpose, but imposes a stricter
requirement: [target] must be comprised between [-1] and [it.weight], both
excluded. In other words, it cannot move the iterator to a sentinel. Also,
it does not handle the case where the iterator already points at the
desired element. (It could, but it would be a mistake to invoke it in such
a case, as there is nothing to do.) *)(* It is worth noting that, when elements are allowed to have non-unit
weights, the weight index [target] can fall within an element. Thus, after
[reach it target m] completes, we do not necessarily have [w_cur_seq it m =
target]. This equality holds if [m] is [MUnit]. Otherwise, in general, we
have [w_cur_seq it m <= target], and the difference [target - w_cur_seq it
m] is a valid weight index into the element that the iterator designates:
i.e., it is less than the weight of this element. *)(* The structure of [reach_inside] is analogous to that of the function [get]
on sequences. *)(* Disabling warning 4 allows us to use a wildcard pattern [_] when analyzing
the structure of a path. *)let[@specialise][@warning"-4"]reach_insideit(target:weight)m=assert(0<=target&&target<it.weight);assert(not(is_at_weightitmtarget));leti_cur_sch=_i_cur_schitinletcurrent:weight=_w_cur_seqitminlets=it.seqin(* In the following, as we restrict our interest to a subsequence (first by
excluding the front schunk, then by also excluding the middle sequence),
we adjust [target] and [current] so that they always remain the weight
index of the target position and the weight index of the current
position, relative to the subsequence of interest. *)(* Test where the target lies. *)letweight_front=S.weight_frontsiniftarget<weight_frontthenbegin(* The desired element lies in the front schunk. *)(* Figure out where to begin the search in the front schunk. If the
iterator already points somewhere into the front schunk, we start
from there. Otherwise, we let [SChunk.reach] decide whether to
start from the left or right end. (It has its own heuristic.) *)letfront=S.frontsinmatchit.pathwith|PathSideFront->letweight_front1,i_cur_sch=SChunk.reach_frommfronti_cur_schcurrenttargetinletcurrent=weight_front1inassign_in_current_schunkiti_cur_schcurrentm|_->letweight_front1,i_cur_sch=SChunk.reachmfronttargetinletcurrent=weight_front1inassignit(PathSideFront)fronti_cur_schcurrentmendelsebegin(* Exclude the front schunk from consideration. *)lettarget=target-weight_frontinletcurrent=current-weight_frontin(* Test where the target lies. *)letmiddle=S.middlesinletweight_middle=M.weightmiddleinifweight_middle<=targetthenbegin(* The desired element lies in the back schunk. *)(* Exclude the middle sequence from consideration. *)lettarget=target-weight_middleinletcurrent=current-weight_middlein(* Figure out where to begin the search in the back schunk. *)letback=S.backsinmatchit.pathwith|PathSideBack->letweight_back1,i_cur_sch=SChunk.reach_frommbacki_cur_schcurrenttargetinletcurrent=weight_front+weight_middle+weight_back1inassign_in_current_schunkiti_cur_schcurrentm|_->letweight_back1,i_cur_sch=SChunk.reachmbacktargetinletcurrent=weight_front+weight_middle+weight_back1inassignit(PathSideBack)backi_cur_schcurrentmendelsebegin(* The desired element lies in the middle sequence. *)(* Three cases arise: 1- we have an iterator for the middle sequence,
and it points to the desired chunk; 2- we have an iterator for the
middle sequence, but it does not point to the desired schunk; 3-
we have no iterator for the middle sequence. Cases 2- and 3- can
in fact be combined. *)(* Regardless of this case distinction, we must ensure two things:
A- the middle iterator points to the desired schunk; B- the
iterator points to the desired element within this schunk. *)matchit.pathwith|PathMiddleit_middlewhenI.is_at_weightit_middleMSWeighttarget->(* Case 1: the middle iterator already points to the desired
schunk. It is the one currently pointed to by [it.schunk]. *)assert(I.unchecked_getit_middleMSWeight==it.schunk);letp=it.schunkinletweight_middle1=I.windexit_middleMSWeightin(* Compute the current weight index into the schunk [p]. *)letcurrent=current-weight_middle1in(* Compute the target weight index into the schunk [p]. *)lettarget=target-weight_middle1in(* Look up this weight index. The pair [(i_cur_sch, current)] can be
used as a hint in a call to [reach_from]. This is expected to
reduce the cost of the linear scan if the target location lies
close to the current location. *)letweight_p1,i_cur_sch=SChunk.reach_frommpi_cur_schcurrenttargetin(* Update the iterator. *)letcurrent=weight_front+weight_middle1+weight_p1inassign_in_current_schunkiti_cur_schcurrentm|_->(* Fetch (case 2) or create (case 3) a middle iterator. *)letit_middle=get_middle_iteratoritmiddlein(* Move it to the desired place, and retrieve the schunk
that it points to. *)I.reach_insideit_middletargetMSWeight;letp=I.unchecked_getit_middleMSWeightin(* Compute a weight index into the schunk [p]. *)letweight_middle1=I.windexit_middleMSWeightinlettarget=target-weight_middle1in(* Look up this weight index. This time, we have no hint. *)letweight_p1,i_cur_sch=SChunk.reachmptargetin(* Update the iterator. *)letcurrent=weight_front+weight_middle1+weight_p1inassignit(PathMiddleit_middle)pi_cur_schcurrentm(* If we distinguished case 2 and case 3, at this point in
case 2, we could avoid an allocation and an assignment
to [it.it_middle], as it already has the correct value. *)endend(* We define [reach] in terms of [reach_inside] to deal with the
special indices [-1] and [weight], and with several fast paths. *)let[@specialise]reach(typea)(it:aiter)target(m:ameasure)=(* [target] must be a valid weight index. *)assert(-1<=target&&target<=it.weight);(* Special cases for reaching a sentinel. These cases are not handled by
[reach_inside], nor by the fast paths below. *)iftarget=-1thenreset_to_sentinelFrontitmelseiftarget=it.weightthenreset_to_sentinelBackitm(* A special case for leaving a sentinel. Handling this case here
simplifies the tests that follow, so is essentially free. *)elseiffinisheditmthenreach_insideittargetm(* At this point, perform case analysis on [m]. The compiler should
specialise [reach], so this test should be free, and allows further
simplifications in each branch. *)elsematchmwith|MUnit->(* [d] denotes the relative distance of the move. *)letd=target-_w_cur_seqitmin(* Fast path for not moving at all. *)ifd=0then()elseleti_cur_sup'=it.i_cur_sup+din(* Fast path for moving within the current segment. *)ifit.i_shd_sup<=i_cur_sup'&&i_cur_sup'<it.i_stl_supthen(* No need to update [it.i_shd_sch] nor [it.w_shd_seq]. *)it.i_cur_sup<-i_cur_sup'(* Fast paths for moving by one unit, reaching a neighbor segment. *)elseifd=1thenmove_next_segmentFrontitmelseifd=-1thenmove_next_segmentBackitm(* The general case is handled by [reach_inside]. *)elsereach_insideittargetm|MSWeight->(* Fast path for not moving at all. *)ifunchecked_is_at_weightitmtargetthen()(* The general case is handled by [reach_inside]. *)elsereach_insideittargetmlet[@inline]reachittargetm=assert(is_validit);reachittargetm;(* Check that the desired weight has been reached. *)assert(is_at_weightitmtarget)(* -------------------------------------------------------------------------- *)(* Moving an iterator. Derived functions. *)(* [reset] is the sequential composition of [reset_to_sentinel] and [move]. *)letresetpovitm=(* The iterator need not be valid. *)reset_to_sentinelpovitm;move_from_sentinelpovitm(* -------------------------------------------------------------------------- *)(* Reading and moving, one segment at a time. *)let[@specialise]get_segmentpovitm=assert(is_validit);iffinisheditmthenraiseEndelsebegin(* The segment that must be returned is a sub-segment of the current
segment. If the user performs iteration by using only [create] and
[get_segment_and_jump], then the iterator always points to the
beginning of a segment, so a full segment is always returned. *)(* [lo] denotes the start index inclusive, and [hi] the end index
exclusive. *)letlo,hi=matchpovwith|Front->it.i_cur_sup,it.i_stl_sup|Back->it.i_shd_sup,it.i_cur_sup+1inassert(lo<hi);letseg=it.support,lo,hi-loinassert(Segment.is_validseg);segend(* [jump] is implemented only in the case [m = MUnit]. Supporting the case
[m = MSWeight] is not necessary. *)(* The time complexity of [jump k] is O(1) as long as [k] is less than the
chunk size, that is, [capacity 0]. In the general case it is [O(log k)],
because [jump] is implemented using [reach]. *)let[@specialise]jumppovitkm=assert(is_validit);assert(m=MUnit);letd=signpov*kin(* The target weight index must be valid. *)assert(letw=_w_cur_seqitm+din-1<=w&&w<=S.weightit.seq);(* We could just call [reach it w m], where [w] is [w_cur_seq it m + d].
However, [reach] would then re-compute [d] as [w - w_cur_seq it m].
Thus, two calls to [w_cur_seq] would be wasted. We prefer to reproduce
some of the fast paths here. *)leti_cur_sup'=it.i_cur_sup+dinifit.i_shd_sup<=i_cur_sup'&&i_cur_sup'<it.i_stl_supthen(* Fast path for jumping within the current segment. *)it.i_cur_sup<-i_cur_sup'elseifd=0then(* Fast path for not moving. *)()elseifpov=Front&&i_cur_sup'=it.i_stl_supthenbegin(* Fast path for jumping to the first item of the next segment,
forward. *)(* Because we have eliminated the case [d = 0], we cannot be at a
sentinel. Indeed, when we are at a sentinel, we have [it.i_stl_sup = 0],
therefore [i_cur_sup' = 0], therefore [it.i_cur_sup = -d]. Since we
have [d <> 0], we have [it.i_cur_sup <> 0]. Contradiction. *)assert(not(finisheditm));(* Therefore, we are looking at a nonempty segment. *)assert(it.i_stl_sup>0);(* Move to the last element of this nonempty segment,
then move into the next segment. *)it.i_cur_sup<-it.i_stl_sup-1;move_next_segmentFrontitmendelseifpov=Back&&i_cur_sup'=it.i_shd_sup-1thenbegin(* Fast path for jumping to the first item of the next segment,
backwards. *)(* Move to the last element (looking backwards) of the current
segment. If we are at a sentinel, then this assignment has no
effect. *)it.i_cur_sup<-it.i_shd_sup;(* Then, move into the next segment. *)move_next_segmentBackitmendelsebegin(* The general case is handled by [reach]. *)letw=_w_cur_seqitm+dinreachitwmend(* -------------------------------------------------------------------------- *)(* Write operations. *)(* These operations update an ephemeral sequence in place. This requires first
ensuring that the sequence has unique ownership of the current schunk. If
this is not the case, the current schunk must be copied and the iterator
must be updated. *)(* [ensure_schunk_uniquely_owned it m] modifies, if needed, the representation
of the current sequence (and of the iterator) so as to ensure that the
current element is stored in a schunk that is uniquely owned by the
sequence. *)(* The iterator must not point to a sentinel. *)(* The current implementation is quite naive. It relies on the operation
[S.ensure_schunk_uniquely_owned], which takes a weight index [i] as an
argument, and rebuilds a valid iterator from scratch. If the sequence
is persistent, then [S.ensure_schunk_uniquely_owned] fails. *)let[@inline]ensure_schunk_uniquely_owneditm=assert(not(finisheditm));lets=it.seqinifS.schunk_uniquely_ownedsit.schunkthenbegin(* No copying is necessary. Invalidate all iterators, then make [it] valid
again by requesting a new birth date for it. *)it.birth<-S.invalidate_iterators_exceptsendelsebegin(* A new unique schunk must be created by copying the current schunk. *)leti=_w_cur_seqitminS.ensure_schunk_uniquely_ownedsiit.schunk;(* Invalidate all iterators, then reset [it] to a valid state. We do so
in a naive way. *)S.invalidate_iteratorss;reset_to_sentinelFrontitm;reachitimend;assert(S.schunk_uniquely_ownedsit.schunk)(* [set it m x] writes the value [x] at the current location of the iterator.
By analogy with [get], we allow the iterator to point at a sentinel, and
we raise [End] in that case. *)let[@inline]setitmx=assert(is_validit);iffinisheditmthenraiseEndelsebeginensure_schunk_uniquely_owneditm;Array.setit.supportit.i_cur_supxend(* [get_writable_segment] returns a segment that can be subsequently be used
for writing. *)let[@specialise]get_writable_segmentpovitm=assert(is_validit);iffinisheditmthenraiseEndelsebeginensure_schunk_uniquely_owneditm;get_segmentpovitmend(* -------------------------------------------------------------------------- *)(* Accessors. *)(* [windex it m] returns the weight index of the current element with respect
to the entire sequence. *)letwindex=_w_cur_seq(* [weight it] returns the weight of the sequence. *)let[@inline]weightit=it.weightend(* Make *)