123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378(******************************************************************************)(* *)(* Fix *)(* *)(* François Pottier, Inria Paris *)(* *)(* Copyright Inria. All rights reserved. This file is distributed under the *)(* terms of the GNU Library General Public License version 2, with a *)(* special exception on linking, as described in the file LICENSE. *)(* *)(******************************************************************************)openIndexing(**An element is represented as an integer index in the range [\[0, n)]. *)type'nelt='nindex(**A block index. Blocks are normally numbered from [0] to [z-1], where
[z] is the current number of blocks. The block index [-1] plays a
special role: it is used to group elements that have been discarded.
A normal block is never empty. The function [discard_unmarked], which
discards elements, can temporarily cause a normal block to become
empty; this is immediately fixed by invoking [compress].
The total number of normal blocks cannot exceed [n]. *)typeblock=int(**An array indexed by blocks. There are at most ever [n] blocks, so the
length of such an array is [n]. Only valid block indices must be
accessed. *)type'ablock_array='aarray(**A location is an index into the [element] array. The locations stored
in the arrays [location] and [first] inhabit the range [\[0, n)]. The
locations stored in the array [past] inhabit the range [(0, n\]]. *)typeloc=int(**An array indexed by locations. *)type'aloc_array='aarraytype'nt={mutablez:block;(**The current number of blocks. This number cannot exceed [n].
This number increases in [split] and can decrease in [compress]. *)element:'neltloc_array;(* E *)(**An array of all elements. This array is mutable: the location of an
element in this array can change over time. The function [mark] moves
elements around. The elements of a block [b] are stored in this array
from index [first.(b)] inclusive to index [past.(b)] exclusive. *)location:('n,loc)vector;(* L *)(**An array that maps an element to its location.
So [element.(location.(e))] is [e]
and [location.(element.(i))] is [i].
Like the array E, this array is mutable. *)block:('n,block)vector;(* S *)(**An array that maps an element to its block. An element that has been
discarded is mapped to the special block [-1]. *)first:locblock_array;(* F *)(**An array that maps a block to the start location of this block. *)past:locblock_array;(* P *)(**An array that maps a block to the end location of this block. *)marked:intblock_array;(* M *)(**An array that maps a block to the number of marked elements that it
currently contains. At indices in the range [\[p.z, n)], this array
is filled with zeroes. *)workset:blockarray;(* W *)(**A bag of the blocks that currently have at least one marked element.
No block appears twice in this bag, so it is actually a set. It is
implemented as a stack of capacity [n]. *)mutablew:int;(**The current number of elements in the workset. In other words, this
is the index of the top of the stack. The stack grows towards the
right. *)}letcreate_empty():Empty.nt={z=0;element=[||];location=Vector.empty;block=Vector.empty;first=[||];past=[||];marked=[||];workset=[||];w=0}letcreate_nonempty(typen)?partition(n:ncardinal)=assert(cardinaln>0);letp={z=1;element=Vector.as_array(Vector.initn(fune->e));(* same as [Array.init n (fun e -> e)], with a richer type *)location=Vector.initn(fune->(e:>loc));block=Vector.maken0;first=Array.make(cardinaln)0;(* uninitialized *)past=Array.make(cardinaln)0;(* uninitialized *)marked=Array.make(cardinaln)0;workset=Array.make(cardinaln)0;(* irrelevant value *)w=0}in(* Every element is now in block 0. The arrays [first] and [past] are
uninitialized. *)matchpartitionwith|None->(* We want just one block. *)p.first.(0)<-0;p.past.(0)<-cardinaln;p|Somecmp->(* Sort the array of elements. The array [location] must now be
considered uninitialized. *)Array.sortcmpp.element;(* Iterate over the sorted array so as to identify the runs of
adjacent elements that are equal according to [cmp]. *)(* During this loop, [current] is an element of the current block;
[z] is the index of the current block. *)letcurrent=refp.element.(0)inp.first.(0)<-0;letz=ref0in(* This loop writes [block], [location], [first], [past]. *)forloc=0tocardinaln-1doletelt=p.element.(loc)inifcmp!currentelt<>0thenbegin(* Close the current block. *)p.past.(!z)<-loc;(* Allocate a new block index. *)incrz;(* Open a new block. *)p.first.(!z)<-loc;current:=eltend;Vector.setp.blockelt(!z);Vector.setp.locationeltlocdone;(* Close the current block, which is the last block. *)p.past.(!z)<-cardinaln;(* The number of blocks is the index of the last block, plus one. *)p.z<-!z+1;pletcreate(typen)?partition(n:ncardinal):nt=matchequaln(Vector.lengthVector.empty)with|SomeRefl->create_empty()|None->create_nonempty?partitionnletmark(p:'at)elt=letblk=Vector.getp.blockeltin(* Marking a discarded element has no effect. *)ifblk>-1thenbegin(* Find the position of the leftmost unmarked element in this block,
if there is one. *)letunmarked_loc=p.first.(blk)+p.marked.(blk)inletloc=Vector.getp.locationeltinifloc>=unmarked_locthenbegin(* The test [loc >= unmarked_loc] confirms that [elt] is unmarked and
that [unmarked_loc] is indeed the position of the leftmost unmarked
element of the block. *)ifloc>unmarked_locthenbegin(* The test [loc > unmarked_loc] confirms that the two elements that
we have been discussing are distinct. Swap them. *)letunmarked_elt=p.element.(unmarked_loc)inassert(p.element.(loc)=elt);p.element.(loc)<-unmarked_elt;p.element.(unmarked_loc)<-elt;Vector.setp.locationunmarked_eltloc;Vector.setp.locationeltunmarked_locend;(* If this block becomes marked, insert it into the workset. *)ifp.marked.(blk)=0thenbeginp.workset.(p.w)<-blk;p.w<-p.w+1end;(* Update the count of marked elements in this block. *)p.marked.(blk)<-p.marked.(blk)+1endend(* [clear_iter_work p yield] applies [yield] to every block in the workset
and empties the workset. The function [yield] is not allowed to mark new
elements. *)letclear_iter_worksetpyield=fori=0top.w-1doletblk=p.workset.(i)inyieldblkdone;p.w<-0letclear_marksp=clear_iter_worksetpbeginfunblk->p.marked.(blk)<-0end(* The time complexity of [split] is O(number of marked blocks) + O(sum of the
sizes of the smaller subblocks). The first summand can be charged to [mark]
and therefore disappears. The second summand remains, and, by virtue of
Hopcroft's argument, the total cost of a series of calls to [split] is
O(n.log n). *)letsplitp=(* Iterate over every block [blk] that has at least one marked element. *)clear_iter_worksetpbeginfunblk->assert(p.marked.(blk)>0);(* Let [frontier] be the location of the leftmost unmarked element
in this block. *)letfrontier=p.first.(blk)+p.marked.(blk)iniffrontier=p.past.(blk)then(* Every element in this block is marked. Unmark every element and
do not split this block. *)p.marked.(blk)<-0elsebegin(* This block has both marked and unmarked elements, so it must be
split. The index of the new block is [p.z]. *)letblk'=p.zinassert(letn=Array.lengthp.elementinblk'<n);ifp.marked.(blk)<=p.past.(blk)-frontierthenbegin(* There are fewer marked elements than unmarked elements. *)(* The new block contains the marked elements. *)p.first.(blk')<-p.first.(blk);p.past.(blk')<-frontier;(* The existing block retains the unmarked elements. *)p.first.(blk)<-frontierendelsebegin(* There are fewer unmarked elements than marked elements. *)(* The new block contains the unmarked elements. *)p.first.(blk')<-frontier;p.past.(blk')<-p.past.(blk);(* The existing block retains the marked elements. *)p.past.(blk)<-frontierend;(* Assign the elements of the new block to the new block. *)forloc=p.first.(blk')top.past.(blk')-1doVector.setp.blockp.element.(loc)blk'done;(* Unmark every element in the two new blocks. *)p.marked.(blk)<-0;assert(p.marked.(blk')=0);p.z<-blk'+1endendletiter_block_elementspblkyield=assert(0<=blk&&blk<p.z);forloc=p.first.(blk)top.past.(blk)-1doletelt=p.element.(loc)inyieldeltdoneletiter_all_elementspyield=forblk=0top.z-1doiter_block_elementspblkyielddonelet[@inline]postincrementr=letx=!rinr:=x+1;x(* [compress p] destroys all empty blocks. That is, it reindexes the blocks,
so that empty blocks do not receive an index in the new scheme. *)(* The arrays [element], [location] are not affected because they do not
depend on block indices. The arrays [marked] and [workset] are not
affected because no block is currently marked. The arrays [block],
[first], and [past] must be adjusted. *)letcompressp=assert(p.w=0);letnext=ref0inforblk=0top.z-1do(* If [p.first.(blk)] is equal to [p.past.(blk)], then this block
is empty. Then, skip it; do not allocate a new block index. *)ifp.first.(blk)<p.past.(blk)thenbegin(* This block is nonempty. Allocate a new block index. *)letblk'=postincrementnextinifblk'<blkthenbegin(* Adjust [block]. *)iter_block_elementspblkbeginfunelt->Vector.setp.blockeltblk'end;(* Adjust [first] and [past]. *)p.first.(blk')<-p.first.(blk);p.past.(blk')<-p.past.(blk)endenddone;(* The new number of blocks is the final value of [next]. *)p.z<-!nextletdiscard_unmarkedp=p.w<-0;(* For every block, *)forblk=0top.z-1doletfrontier=p.first.(blk)+p.marked.(blk)in(* Assign every unmarked element of this block to the special block [-1]. *)forloc=frontiertop.past.(blk)-1doVector.setp.blockp.element.(loc)(-1)done;(* Shrink this block to just its marked elements. The block possibly
becomes empty at this point. *)p.past.(blk)<-frontier;(* Unmark every element of this block. *)p.marked.(blk)<-0done;(* Empty blocks may have been created: invoke [compress] to destroy them. *)compresspletdiscardpf=(* Initially, we expect that no element is marked. *)assert(p.w=0);iter_all_elementsp(funelt->ifnot(felt)thenmarkpelt);discard_unmarkedpletblock_countp=p.zletfindpelt=Vector.getp.blockeltletchoosepblk=assert(0<=blk&&blk<p.z);assert(p.first.(blk)<p.past.(blk));p.element.(p.first.(blk))letis_chosenpelt=letblk=Vector.getp.blockeltinblk>-1&&letloc=Vector.getp.locationeltin(* The block [blk] cannot be empty. *)assert(loc<p.past.(blk));loc=p.first.(blk)letexhaust_marked_elementspblkyield=assert(0<=blk&&blk<p.z);(* [next] is the location of the next marked element that we plan
to process. *)letnext=refp.first.(blk)in(* As long as there remain unprocessed marked elements, *)while!next<p.first.(blk)+p.marked.(blk)do(* Process the marked elements in the interval [\[next, goal)]. *)letgoal=p.first.(blk)+p.marked.(blk)inforloc=!nexttogoal-1doyieldp.element.(loc)done;(* Adjust [next]. Because the function [yield] may mark more
elements, we might not be done; hence the [while] loop. *)(* The reason why this works is a bit subtle: we exploit the fact
that when [mark] marks a new element, the newly marked element
is placed to the right of the elements that are already marked,
without disturbing them. *)next:=goaldoneletget_marked_blocksp=Array.subp.workset0p.w