open SequenceSig (* "require" rather than "open" *) open NullPointers (* "require" rather than "open" *) (*-----------------------------------------------------------------------------*) module Make (Chunk : SequenceSig.FixedCapacityS) = struct (*-----------------------------------------------------------------------------*) (** Representation -- filling outer buffers before inner buffers, unlike in the paper; -- a level is shallow iff the middle pointer is null; in this case: - all the data gets stored in the front_outer buffer, - the back_outer pointer is equal to the front_outer pointer, - both inner buffers are null. *) type 'a t = { mutable front_outer : 'a Chunk.t; mutable front_inner : 'a Chunk.t; mutable middle : ('a Chunk.t) t; mutable back_inner : 'a Chunk.t; mutable back_outer : 'a Chunk.t; } (*-----------------------------------------------------------------------------*) (** Creation of an empty structure (for representing the middle sequence of a shallow level) *) let empty : 'a. unit -> ('a Chunk.t) t = fun () -> NullPointers.null (** Test if level is shallow *) let is_shallow s = s.middle == empty() (** Creation *) let create () = let c = Chunk.create() in { front_outer = c; front_inner = Chunk.null; middle = empty(); back_inner = Chunk.null; back_outer = c; } (** Conversion from deep to shallow, where chunk c holds all the data *) let set_shallow c s = s.front_outer <- c; s.front_inner <- Chunk.null; s.middle <- empty(); s.back_inner <- Chunk.null; s.back_outer <- c (** Emptiness test *) let is_empty s = (* is_shallow s && Chunk.is_empty s.front_outer *) if is_shallow s then Chunk.is_empty s.front_outer else false (** Push front *) let rec push_front : 'a. 'a -> 'a t -> unit = fun x s -> let full_front = let c = s.front_outer in Chunk.is_full c in if full_front then begin if is_shallow s then begin s.front_outer <- Chunk.create(); s.front_inner <- Chunk.create(); s.middle <- create(); s.back_inner <- Chunk.create() end else begin let ci = s.front_inner in s.front_inner <- s.front_outer; if Chunk.is_empty ci then begin s.front_outer <- ci; end else begin (* assert (Chunk.is_full ci); *) push_front ci s.middle; s.front_outer <- Chunk.create(); end end end; Chunk.push_front x s.front_outer (** Push back *) let rec push_back : 'a. 'a -> 'a t -> unit = fun x s -> assert false (** Pop front *) let rec pop_front : 'a. 'a t -> 'a = fun s -> let co = s.front_outer in let check () = set_shallow_if_needed s in if not (Chunk.is_empty co) then begin let x = Chunk.pop_front co in if Chunk.length co <= 1 then check(); x end else begin (* assert (not (is_shallow s)); *) let ci = s.front_inner in let m = s.middle in if not (Chunk.is_empty ci) then begin s.front_outer <- ci; s.front_inner <- co; Chunk.pop_front s.front_outer end else if not (is_empty m) then begin s.front_outer <- pop_front m; let x = Chunk.pop_front s.front_outer in check(); x end else if not (Chunk.is_empty s.back_inner) then begin s.front_outer <- s.back_inner; s.back_inner <- co; Chunk.pop_front s.front_outer end else begin (* assert (not (Chunk.is_empty s.back_outer)); *) let bo = s.back_outer in let x = Chunk.pop_front bo in if Chunk.length bo <= 1 then check(); x end end (** Conversion from deep to shallow if the layer stores only 0 or 1 item *) and set_shallow_if_needed : 'a. 'a t -> unit = fun s -> let fo = s.front_outer in let bo = s.back_outer in let mid = s.middle in let n = Chunk.length fo + Chunk.length s.front_inner + Chunk.length s.back_inner + Chunk.length bo in if n <= 1 && is_empty mid then begin let c = if Chunk.is_empty fo then bo else fo in set_shallow c s; end else if n = 0 then begin let c = pop_front mid in if Chunk.length c <= 1 && is_empty mid then set_shallow c s else s.front_outer <- c end (** Pop back *) let pop_back : 'a. 'a t -> 'a = fun s -> assert false (** Front *) let front : 'a. 'a t -> 'a = fun s -> assert false (** Back *) let back : 'a. 'a t -> 'a = fun s -> assert false (** Push a buffer to the back of the middle sequence, possibly merging it with the back chunk in the middle sequence *) let middle_merge_back m c = let sc = Chunk.length c in if sc > 0 then begin if is_empty m then begin push_back c m end else begin let b = pop_back m in let sb = Chunk.length b in if sc + sb > Chunk.capacity then begin push_back b m; push_back c m end else begin Chunk.append b c; push_back b m; end end end (** -- Symmetric to above *) let middle_merge_front m c = assert false (** Swap the content of two sequences *) let swap_shallow_with_other : 'a. 'a t -> 'a t -> unit = fun s1 s2 -> let c = s1.front_outer in s1.front_outer <- s2.front_outer; s1.front_inner <- s2.front_inner; s1.middle <- s2.middle; s1.back_inner <- s2.back_inner; s1.back_outer <- s2.back_outer; set_shallow c s2 (** Merge the items of a chunk to the back of a sequence *) let append_chunk_back c s = while not (Chunk.is_empty c) do let x = Chunk.pop_front c in push_back x s; done (* Chunk.iter (fun x -> push_back x s) c *) (** Merge the items of a chunk to the front of a sequence *) let append_chunk_front c s = while not (Chunk.is_empty c) do let x = Chunk.pop_back c in push_front x s; done (* Chunk.iter (fun x -> push_front x s) c *) (** Append to the back of q1 the items of s2; s2 becomes invalid *) let rec append : 'a. 'a t -> 'a t -> unit = fun s1 s2 -> if is_shallow s2 then begin append_chunk_back s2.front_outer s1 end else if is_shallow s1 then begin swap_shallow_with_other s1 s2; append_chunk_front s2.front_outer s1 end else begin let m1 = s1.middle in let ci = s1.back_inner in let co = s1.back_outer in if Chunk.is_empty ci then begin middle_merge_back m1 co end else begin push_back ci m1; if not (Chunk.is_empty co) then push_back co m1; end; let m2 = s2.middle in let ci = s2.front_inner in let co = s2.front_outer in if Chunk.is_empty ci then begin middle_merge_front m2 co end else begin push_front ci m2; if not (Chunk.is_empty co) then push_front co m2; end; s1.back_inner <- s2.back_inner; s1.back_outer <- s2.back_outer; if not (is_empty m1) && not (is_empty m2) then begin let c1 = pop_back m1 in let sc1 = Chunk.length c1 in let c2 = pop_front m2 in let sc2 = Chunk.length c2 in if sc1 + sc2 > Chunk.capacity then begin push_back c1 m1; push_front c2 m2; end else begin Chunk.append c1 c2; push_back c1 m1; end; end; append m1 m2; end end |