From 203e378cea8da6d59d7b4bedce39da2f8a71df39 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Jun 2023 12:00:41 -0400 Subject: [PATCH 1/7] add `CCVec`; deprecate `CCVector` get rid of the permission stuff; improve API a bit --- src/core/CCVec.ml | 690 ++++++++++++++++++++++++++++++++++++++++++ src/core/CCVec.mli | 367 ++++++++++++++++++++++ src/core/CCVector.mli | 7 +- 3 files changed, 1063 insertions(+), 1 deletion(-) create mode 100644 src/core/CCVec.ml create mode 100644 src/core/CCVec.mli diff --git a/src/core/CCVec.ml b/src/core/CCVec.ml new file mode 100644 index 000000000..d9616dca0 --- /dev/null +++ b/src/core/CCVec.ml @@ -0,0 +1,690 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +type 'a iter = ('a -> unit) -> unit +type 'a equal = 'a -> 'a -> bool +type 'a ord = 'a -> 'a -> int +type 'a printer = Format.formatter -> 'a -> unit + +type 'a t = { mutable size: int; mutable vec: 'a array } +(** A vector of 'a. *) + +external as_float_arr : 'a array -> float array = "%identity" +external as_obj_arr : 'a array -> Obj.t array = "%identity" + +let fill_with_junk_ (a : _ array) i len : unit = + if Obj.(tag (repr a) = double_array_tag) then + Array.fill (as_float_arr a) i len 0. + else + Array.fill (as_obj_arr a) i len (Obj.repr ()) + +let freeze v = { size = v.size; vec = v.vec } +let freeze_copy v = { size = v.size; vec = Array.sub v.vec 0 v.size } +let create () = { size = 0; vec = [||] } + +let create_with ?(capacity = 128) x = + let vec = Array.make capacity x in + fill_with_junk_ vec 0 capacity; + { size = 0; vec } + +let return x = { size = 1; vec = [| x |] } +let make n x = { size = n; vec = Array.make n x } +let init n f = { size = n; vec = Array.init n f } + +(* is the underlying array empty? *) +let[@inline] array_is_empty_ v = Array.length v.vec = 0 + +(* next capacity, if current one is [n] *) +let[@inline] next_grow_ n = min Sys.max_array_length (n + (n lsr 1) + 2) + +(* resize the underlying array using x to temporarily fill the array *) +let resize_ v newcapacity x = + assert (newcapacity >= v.size); + assert (not (array_is_empty_ v)); + let new_vec = Array.make newcapacity x in + Array.blit v.vec 0 new_vec 0 v.size; + fill_with_junk_ new_vec v.size (newcapacity - v.size); + v.vec <- new_vec; + () + +(* grow the array, using [x] as a filler if required *) +let grow_with_ v ~filler:x = + if array_is_empty_ v then ( + let len = 4 in + v.vec <- Array.make len x; + (* do not really use [x], it was just for knowing the type *) + fill_with_junk_ v.vec 0 len + ) else ( + let n = Array.length v.vec in + let size = next_grow_ n in + if size = n then invalid_arg "vec: can't grow any further"; + resize_ v size v.vec.(0) + ) + +(* v is not empty; ensure it has at least [size] slots. + + Use a doubling-size strategy so that calling many times [ensure] will + behave well *) +let ensure_assuming_not_empty_ v ~size = + if size > Sys.max_array_length then + invalid_arg "vec.ensure: size too big" + else if size < Array.length v.vec then + () + (* nothing to do *) + else ( + let n = ref (Array.length v.vec) in + while !n < size do + n := next_grow_ !n + done; + resize_ v !n v.vec.(0) + ) + +let ensure_with ~init v size = + if array_is_empty_ v then ( + v.vec <- Array.make size init; + fill_with_junk_ v.vec 0 size + ) else + ensure_assuming_not_empty_ v ~size + +let ensure v size = + if not (array_is_empty_ v) then ensure_assuming_not_empty_ v ~size + +let[@inline] clear v = v.size <- 0 + +let clear_and_reset v = + v.size <- 0; + v.vec <- [||] + +(* TODO*) +(* + let v = create() in + let a = Weak.create 1 in + push v ("hello"^"world"); + Weak.set a 0 (Some (get v 0)); + Gc.full_major(); Gc.compact(); + assert_bool "is alive" (Weak.check a 0); + Gc.full_major(); Gc.compact(); + assert_equal None (Weak.get a 0); +*) + +let[@inline] is_empty v = v.size = 0 + +let[@inline] push_unsafe_ v x = + Array.unsafe_set v.vec v.size x; + v.size <- v.size + 1 + +let push v x = + if v.size = Array.length v.vec then grow_with_ v ~filler:x; + push_unsafe_ v x + +let resize_with v f size = + if size < 0 then invalid_arg "Vec.resize_with"; + if Array.length v.vec = 0 then ( + let new_vec = Array.init size f in + v.vec <- new_vec; + v.size <- size + ) else ( + ensure_assuming_not_empty_ v size; + let { size = cur_size; vec } = v in + for i = cur_size to size - 1 do + Array.unsafe_set vec i (f i) + done; + assert (size <= Array.length v.vec); + v.size <- size + ) + +let resize_with_init v ~init size = + if size < 0 then invalid_arg "Vec.resize_with_init"; + if Array.length v.vec = 0 then ( + let vec = Array.make size init in + v.vec <- vec; + v.size <- size + ) else ( + ensure_assuming_not_empty_ v size; + (* nothing will change [v] *) + for i = v.size to size - 1 do + Array.unsafe_set v.vec i init + done; + v.size <- size + ) + +(** Add all elements of b to a *) +let append a b = + if array_is_empty_ a then + if array_is_empty_ b then + () + else ( + a.vec <- Array.copy b.vec; + a.size <- b.size + ) + else ( + ensure_assuming_not_empty_ a ~size:(a.size + b.size); + assert (Array.length a.vec >= a.size + b.size); + Array.blit b.vec 0 a.vec a.size b.size; + a.size <- a.size + b.size + ) + +let[@inline] get v i = + if i < 0 || i >= v.size then invalid_arg "CCVector.get"; + Array.unsafe_get v.vec i + +let[@inline] set v i x = + if i < 0 || i >= v.size then invalid_arg "CCVector.set"; + Array.unsafe_set v.vec i x + +let remove_and_shift v i = + if i < 0 || i >= v.size then invalid_arg "CCVector.remove"; + (* if v.(i) not the last element, then put last element at index i *) + if i < v.size - 1 then Array.blit v.vec (i + 1) v.vec i (v.size - i - 1); + (* remove one element *) + v.size <- v.size - 1; + fill_with_junk_ v.vec v.size 1 + +let remove_unordered v i = + if i < 0 || i >= v.size then invalid_arg "CCVector.remove_unordered"; + (* if v.(i) not the last element, then put last element at index i *) + if i < v.size - 1 then v.vec.(i) <- v.vec.(v.size - 1); + (* remove one element *) + v.size <- v.size - 1; + fill_with_junk_ v.vec v.size 1 + +let insert v i x = + (* Note that we can insert at i=v.size *) + if i < 0 || i > v.size then invalid_arg "CCVector.insert"; + if v.size = Array.length v.vec then grow_with_ v ~filler:x; + (* Shift the following elements, then put the element at i *) + if i < v.size then Array.blit v.vec i v.vec (i + 1) (v.size - i); + v.vec.(i) <- x; + v.size <- v.size + 1 + +let[@inline] append_iter a i = i (fun x -> push a x) +let append_seq a seq = Seq.iter (fun x -> push a x) seq + +let append_array a b = + let len_b = Array.length b in + if array_is_empty_ a then ( + a.vec <- Array.copy b; + a.size <- len_b + ) else ( + ensure_assuming_not_empty_ a ~size:(a.size + len_b); + Array.blit b 0 a.vec a.size len_b; + a.size <- a.size + len_b + ) + +let append_list a b = + match b with + | [] -> () + | x :: _ -> + (* need to push at least one elem *) + let len_a = a.size in + let len_b = List.length b in + ensure_with ~init:x a (len_a + len_b); + List.iter (push_unsafe_ a) b; + () + +let rec append_gen a b = + match b () with + | None -> () + | Some x -> + push a x; + append_gen a b + +let equal eq v1 v2 = + v1.size = v2.size + && + let n = v1.size in + let rec check i = i = n || (eq (get v1 i) (get v2 i) && check (i + 1)) in + check 0 + +let compare cmp v1 v2 = + let n = min v1.size v2.size in + let rec check i = + if i = n then + compare v1.size v2.size + else ( + let c = cmp (get v1 i) (get v2 i) in + if c = 0 then + check (i + 1) + else + c + ) + in + check 0 + +exception Empty + +let pop_exn v = + if v.size = 0 then raise Empty; + let new_size = v.size - 1 in + v.size <- new_size; + let x = v.vec.(new_size) in + (* free last element *) + fill_with_junk_ v.vec new_size 1; + x + +let pop v = try Some (pop_exn v) with Empty -> None + +let[@inline] top v = + if v.size = 0 then + None + else + Some (Array.unsafe_get v.vec (v.size - 1)) + +let[@inline] top_exn v = + if v.size = 0 then raise Empty; + Array.unsafe_get v.vec (v.size - 1) + +let[@inline] copy v = { size = v.size; vec = Array.sub v.vec 0 v.size } + +let truncate v n = + let old_size = v.size in + if n < old_size then ( + v.size <- n; + (* free elements by erasing them *) + fill_with_junk_ v.vec n (old_size - n) + ) + +let shrink_to_fit v : unit = + if v.size = 0 then + v.vec <- [||] + else if v.size < Array.length v.vec then + v.vec <- Array.sub v.vec 0 v.size + +let sort cmp v = + (* possibly copy array (to avoid junk at its end), then sort the array *) + let a = + if Array.length v.vec = v.size then + v.vec + else + Array.sub v.vec 0 v.size + in + Array.fast_sort cmp a; + v.vec <- a + +let sorted cmp v = + let v' = { size = v.size; vec = Array.sub v.vec 0 v.size } in + Array.sort cmp v'.vec; + v' + +let uniq_sort cmp v = + sort cmp v; + let n = v.size in + (* traverse to remove duplicates. i= current index, + j=current append index, j<=i. new_size is the size + the vector will have after removing duplicates. *) + let rec traverse prev i j = + if i >= n then + () + (* done traversing *) + else if cmp prev v.vec.(i) = 0 then ( + v.size <- v.size - 1; + traverse prev (i + 1) j (* duplicate, remove it *) + ) else ( + v.vec.(j) <- v.vec.(i); + traverse v.vec.(i) (i + 1) (j + 1) + ) + (* keep it *) + in + if v.size > 0 then traverse v.vec.(0) 1 1 +(* start at 1, to get the first element in hand *) + +let iter k v = + let { vec; size = n } = v in + for i = 0 to n - 1 do + k (Array.unsafe_get vec i) + done + +let iteri k v = + let { vec; size = n } = v in + for i = 0 to n - 1 do + k i (Array.unsafe_get vec i) + done + +let map f v = + if array_is_empty_ v then + create () + else ( + let { vec; size } = v in + let vec = Array.init size (fun i -> f (Array.unsafe_get vec i)) in + { size; vec } + ) + +let mapi f v = + if array_is_empty_ v then + create () + else ( + let { vec; size } = v in + let vec = Array.init size (fun i -> f i (Array.unsafe_get vec i)) in + { size; vec } + ) + +let map_in_place f v = + let { vec; size = n } = v in + for i = 0 to n - 1 do + Array.unsafe_set vec i (f (Array.unsafe_get vec i)) + done + +let filter_in_place p v = + let i = ref 0 in + (* cur element *) + let j = ref 0 in + (* cur insertion point *) + let n = v.size in + while !i < n do + if p v.vec.(!i) then ( + (* move element i at the first empty slot. + invariant: i >= j*) + if !i > !j then v.vec.(!j) <- v.vec.(!i); + incr i; + incr j + ) else + incr i + done; + (* free elements *) + fill_with_junk_ v.vec !j (v.size - !j); + v.size <- !j + +let filter p v = + if array_is_empty_ v then + create () + else ( + let v' = create_with ~capacity:v.size v.vec.(0) in + iter (fun x -> if p x then push_unsafe_ v' x) v; + v' + ) + +let fold_left f acc v = + let { vec; size } = v in + let rec fold acc i = + if i = size then + acc + else ( + let x = Array.unsafe_get vec i in + fold (f acc x) (i + 1) + ) + in + fold acc 0 + +let fold = fold_left + +let exists p v = + let n = v.size in + let rec check i = + if i = n then + false + else + p v.vec.(i) || check (i + 1) + in + check 0 + +let for_all p v = + let n = v.size in + let rec check i = + if i = n then + true + else + p v.vec.(i) && check (i + 1) + in + check 0 + +let member ~eq x v = exists (eq x) v + +let find_internal_ p v = + let n = v.size in + let rec check i = + if i = n then + raise_notrace Not_found + else ( + let x = v.vec.(i) in + if p x then + x + else + check (i + 1) + ) + in + check 0 + +let find_exn p v = try find_internal_ p v with Not_found -> raise Not_found +let find p v = try Some (find_internal_ p v) with Not_found -> None + +let find_map f v = + let n = v.size in + let rec search i = + if i = n then + None + else ( + match f v.vec.(i) with + | None -> search (i + 1) + | Some _ as res -> res + ) + in + search 0 + +let filter_map f v = + let v' = create () in + iter + (fun x -> + match f x with + | None -> () + | Some y -> push v' y) + v; + v' + +let filter_map_in_place f v = + let i = ref 0 in + (* cur element *) + let j = ref 0 in + (* cur insertion point *) + let n = v.size in + while !i < n do + match f v.vec.(!i) with + | None -> incr i (* drop *) + | Some y -> + (* move element i at the first empty slot. + invariant: i >= j*) + v.vec.(!j) <- y; + incr i; + incr j + done; + (* free elements *) + fill_with_junk_ v.vec !j (v.size - !j); + v.size <- !j + +let flat_map f v = + let v' = create () in + iter (fun x -> iter (push v') (f x)) v; + v' + +let flat_map_iter f v = + let v' = create () in + iter + (fun x -> + let seq = f x in + append_iter v' seq) + v; + v' + +let flat_map_seq f v = + let v' = create () in + iter + (fun x -> + let seq = f x in + append_seq v' seq) + v; + v' + +let flat_map_list f v = + let v' = create () in + iter + (fun x -> + let l = f x in + append_list v' l) + v; + v' + +let cartesian_product f a1 a2 : _ t = + let na1 = a1.size in + init (na1 * a2.size) (fun i_prod -> + let i = i_prod mod na1 in + let j = i_prod / na1 in + f a1.vec.(i) a2.vec.(j)) + +let rev_in_place v = + if v.size > 0 then ( + let n = v.size in + let vec = v.vec in + for i = 0 to (n - 1) / 2 do + let x = Array.unsafe_get vec i in + let y = Array.unsafe_get vec (n - i - 1) in + Array.unsafe_set vec i y; + Array.unsafe_set vec (n - i - 1) x + done + ) + +let rev v = + let v' = copy v in + rev_in_place v'; + v' + +let rev_iter f v = + let { vec; size = n } = v in + for i = n - 1 downto 0 do + f (Array.unsafe_get vec i) + done + +let size v = v.size +let length v = v.size +let capacity v = Array.length v.vec +let unsafe_get_array v = v.vec + +let of_iter ?(init = create ()) seq = + append_iter init seq; + init + +let of_seq ?(init = create ()) seq = + append_seq init seq; + init + +let to_iter v k = iter k v + +let to_iter_rev v k = + let { vec; size = n } = v in + for i = n - 1 downto 0 do + k (Array.unsafe_get vec i) + done + +let to_seq v = + let { size; vec } = v in + let rec aux i () = + if i >= size then + Seq.Nil + else + Seq.Cons (vec.(i), aux (i + 1)) + in + aux 0 + +let to_seq_rev v = + let { size; vec } = v in + let rec aux i () = + if i < 0 then + Seq.Nil + else + Seq.Cons (vec.(i), aux (i - 1)) + in + aux (size - 1) + +let slice_iter v start len = + assert (start >= 0 && len >= 0); + fun k -> + let { size; vec } = v in + assert (start + len <= size); + for i = start to start + len - 1 do + let x = Array.unsafe_get vec i in + k x + done + +let range_inclusive i j = + if i > j then + init (i - j + 1) (fun k -> i - k) + else + init (j - i + 1) (fun k -> i + k) + +let range_exclusive i j = + if i = j then + create () + else if i > j then + init (i - j) (fun k -> i - k) + else + init (j - i) (fun k -> i + k) + +let unsafe_slice v = v.vec, 0, v.size + +let of_array a = + if Array.length a = 0 then + create () + else + { size = Array.length a; vec = Array.copy a } + +let of_list l = + match l with + | [] -> create () + | [ x ] -> return x + | [ x; y ] -> { size = 2; vec = [| x; y |] } + | x :: _ -> + let v = create_with ~capacity:(List.length l) x in + List.iter (push_unsafe_ v) l; + v + +let to_array v = Array.sub v.vec 0 v.size +let to_list v = List.rev (fold (fun acc x -> x :: acc) [] v) + +let of_gen ?(init = create ()) g = + let rec aux g = + match g () with + | None -> init + | Some x -> + push init x; + aux g + in + aux g + +let to_gen v = + let { size; vec } = v in + let i = ref 0 in + fun () -> + if !i < size then ( + let x = vec.(!i) in + incr i; + Some x + ) else + None + +let to_string ?(start = "") ?(stop = "") ?(sep = ", ") item_to_string v = + start ^ (to_list v |> List.map item_to_string |> String.concat sep) ^ stop + +let pp ?(pp_start = fun _ () -> ()) ?(pp_stop = fun _ () -> ()) + ?(pp_sep = fun fmt () -> Format.fprintf fmt ",@ ") pp_item fmt v = + pp_start fmt (); + iteri + (fun i x -> + if i > 0 then pp_sep fmt (); + pp_item fmt x) + v; + pp_stop fmt () + +module Infix = struct + let ( -- ) = range_inclusive + let ( --^ ) = range_exclusive + let ( >>= ) x f = flat_map f x + let ( >|= ) x f = map f x + + [@@@ifge 4.8] + + let ( let+ ) = ( >|= ) + let ( let* ) = ( >>= ) + let[@inline] ( and+ ) a1 a2 = cartesian_product (fun x y -> x, y) a1 a2 + let ( and* ) = ( and+ ) + + [@@@endif] +end + +include Infix diff --git a/src/core/CCVec.mli b/src/core/CCVec.mli new file mode 100644 index 000000000..9aba47236 --- /dev/null +++ b/src/core/CCVec.mli @@ -0,0 +1,367 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +(** Growable, mutable vector. + + This replaces {!CCVector}, removing permissions, and re-vamping the API overall. + @since NEXT_RELEASE +*) + +type 'a t +(** The type of a vector of elements of type ['a], with *) + +type 'a iter = ('a -> unit) -> unit +(** Fast internal iterator. *) + +type 'a equal = 'a -> 'a -> bool +type 'a ord = 'a -> 'a -> int +type 'a printer = Format.formatter -> 'a -> unit + +val create : unit -> 'a t +(** Create a new, empty vector. *) + +val create_with : ?capacity:int -> 'a -> 'a t +(** Create a new vector, the value is used to enforce the type the new vector. + @param capacity the size of the underlying array. *) + +val return : 'a -> 'a t +(** Singleton vector. + @since 0.14 *) + +val make : int -> 'a -> 'a t +(** [make n x] makes a vector of size [n], filled with [x]. *) + +val init : int -> (int -> 'a) -> 'a t +(** Init the vector with the given function and size. *) + +val clear : _ t -> unit +(** Clear the content of the vector. + This ensures that [length v = 0] but the underlying array is kept, + and possibly references to former elements, which are therefore + not garbage collectible. *) + +val clear_and_reset : _ t -> unit +(** Clear the content of the vector, and deallocate the underlying array, + removing references to all the elements. The elements can be collected. *) + +val ensure_with : init:'a -> 'a t -> int -> unit +(** Hint to the vector that it should have at least the given capacity. + This does not affect [length v]. + @param init if [capacity v = 0], used to enforce the type of the vector + (see {!create_with}). + @raise Invalid_arg if the size is not suitable (negative, or too big for OCaml arrays) *) + +val ensure : _ t -> int -> unit +(** Hint to the vector that it should have at least the given capacity. + Just a hint, will not be enforced if the vector is empty and [init] + is not provided. + @raise Invalid_arg if the size is not suitable (negative, or too big for OCaml arrays) +*) + +val is_empty : _ t -> bool +(** Is the vector empty? *) + +val push : 'a t -> 'a -> unit +(** Add an element at the end of the vector. *) + +val resize_with : 'a t -> (int -> 'a) -> int -> unit +(** [resize_with vec f size] resizes vector [vec] up to [size], fills vector + with calls to [f] on indexes [[vec.size-1.. size - 1]]. + The contents and size of vec are untouched if [size] is inferior or equal + to [length vec]. + @raise Invalid_argument if the size is too big *) + +val resize_with_init : 'a t -> init:'a -> int -> unit +(** [resize_with_init vec init size] resizes vector [vec] up to [size], + fills vector with calls to [init] on indexes [[length vec -1.. size - 1]]. + The contents and size of vec are untouched if [size] is inferior or equal + to [length vec]. + @raise Invalid_argument if the size is too big *) + +val append : 'a t -> 'a t -> unit +(** [append a b] adds all elements of [b] to [a]. *) + +val append_array : 'a t -> 'a array -> unit +(** Like {!append}, with an array. *) + +val append_iter : 'a t -> 'a iter -> unit +(** Append content of iterator. *) + +val append_seq : 'a t -> 'a Seq.t -> unit +(** Append content of iterator. + Renamed from [append_std_seq] since 3.0. *) + +val append_list : 'a t -> 'a list -> unit +(** Append content of list. *) + +val equal : 'a equal -> 'a t equal +(** Content-wise equality *) + +val compare : 'a ord -> 'a t ord +(** Total ordering on vectors. Lexicographic comparison. *) + +exception Empty +(** Raised on empty stack/vector. *) + +val pop : 'a t -> 'a option +(** Remove last element, or [None]. *) + +val pop_exn : 'a t -> 'a +(** Remove last element, or raise an exception if empty. + @raise Empty on an empty vector. *) + +val top : 'a t -> 'a option +(** Top element, if present. *) + +val top_exn : 'a t -> 'a +(** Top element, if present. + @raise Empty on an empty vector. *) + +val copy : 'a t -> 'a t +(** Shallow copy. *) + +val truncate : _ t -> int -> unit +(** Truncate to the given size (remove elements above this size). + Does nothing if the parameter is bigger than the current size. *) + +val shrink_to_fit : _ t -> unit +(** Shrink internal array to fit the size of the vector. This will + most likely reallocate the internal array. *) + +val member : ('a -> 'a -> bool) -> 'a -> 'a t -> bool +(** Is the element a member of the vector? *) + +val sorted : ('a -> 'a -> int) -> 'a t -> 'a t +(** Sort the vector, returning a copy of it that is sorted + w.r.t the given ordering. The vector itself is unchanged. + The underlying array of the new vector can be smaller than + the original one. *) + +val sort : ('a -> 'a -> int) -> 'a t -> unit +(** Sort the vector in place (modifying it). + This function change the size of the underlying array. *) + +val uniq_sort : ('a -> 'a -> int) -> 'a t -> unit +(** Sort the array and remove duplicates, in place (e.g. modifying + the vector itself). *) + +val iter : ('a -> unit) -> 'a t -> unit +(** Iterate on the vector's content. *) + +val iteri : (int -> 'a -> unit) -> 'a t -> unit +(** Iterate on the vector, with indexes. *) + +val map : ('a -> 'b) -> 'a t -> 'b t +(** Map elements of the vector, yielding a new vector. *) + +val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t +(** [map f v] is just like {!map}, but it also passes in the index + of each element as the first argument to the function [f]. *) + +val map_in_place : ('a -> 'a) -> 'a t -> unit +(** Map elements of the vector in place. *) + +val filter : ('a -> bool) -> 'a t -> 'a t +(** Filter elements from the vector. [filter p v] leaves [v] unchanged but + returns a new vector that only contains elements of [v] satisfying [p]. *) + +val filter_in_place : ('a -> bool) -> 'a t -> unit +(** Filter elements from the vector in place. *) + +val fold_left : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b +(** Fold on elements of the vector *) + +val fold : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b +(** Fold on elements of the vector. Alias for {!fold_left}. *) + +val exists : ('a -> bool) -> 'a t -> bool +(** Existential test (is there an element that satisfies the predicate?). *) + +val for_all : ('a -> bool) -> 'a t -> bool +(** Universal test (do all the elements satisfy the predicate?). *) + +val find : ('a -> bool) -> 'a t -> 'a option +(** Find an element that satisfies the predicate. *) + +val find_exn : ('a -> bool) -> 'a t -> 'a +(** Find an element that satisfies the predicate, or + @raise Not_found if no element does. *) + +val find_map : ('a -> 'b option) -> 'a t -> 'b option +(** [find_map f v] returns the first [Some y = f x] for [x] in [v], + or [None] if [f x = None] for each [x] in [v]. *) + +val filter_map : ('a -> 'b option) -> 'a t -> 'b t +(** Map elements with a function, possibly filtering some of them out. *) + +val filter_map_in_place : ('a -> 'a option) -> 'a t -> unit +(** Filter-map elements of the vector in place. *) + +val flat_map : ('a -> 'b t) -> 'a t -> 'b t +(** Map each element to a sub-vector. *) + +val flat_map_seq : ('a -> 'b Seq.t) -> 'a t -> 'b t +(** Like {!flat_map}, but using [Seq] for intermediate collections. *) + +val flat_map_list : ('a -> 'b list) -> 'a t -> 'b t +(** Like {!flat_map}, but using {!list} for + intermediate collections. *) + +val cartesian_product : ('a -> 'b -> 'c) -> 'a t -> 'b t -> 'c t +(** All combinaisons of tuples from the two vectors are passed to the function. *) + +val range_inclusive : int -> int -> int t +(** Range of integers, either ascending or descending, where both + bounds are included, therefore the result is never empty). + Example: [1 -- 10] returns the vector [[1;2;3;4;5;6;7;8;9;10]]. *) + +val range_exclusive : int -> int -> int t +(** Range of integers, either ascending or descending, but excluding the second argument. + Example: [1 --^ 10] returns the vector [[1;2;3;4;5;6;7;8;9]]. *) + +module Infix : sig + val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t + (** Infix version of {!flat_map}. *) + + val ( >|= ) : 'a t -> ('a -> 'b) -> 'b t + (** Infix version of {!map}. *) + + val ( -- ) : int -> int -> int t + (** Alias for {!range_inclusive} *) + + val ( --^ ) : int -> int -> int t + (** Alias for {!range_exclusive} *) + + [@@@ifge 4.08] + + val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t + (** @since 2.8 *) + + val ( and+ ) : 'a t -> 'b t -> ('a * 'b) t + (** @since 2.8 *) + + val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t + (** @since 2.8 *) + + val ( and* ) : 'a t -> 'b t -> ('a * 'b) t + (** @since 2.8 *) + + [@@@endif] +end + +include module type of Infix + +val get : 'a t -> int -> 'a +(** Access element by its index, or + @raise Invalid_argument if bad index. *) + +val set : 'a t -> int -> 'a -> unit +(** Modify element at given index, or + @raise Invalid_argument if the index is + invalid (i.e. not in [[0.. length v-1]]). *) + +val remove_and_shift : 'a t -> int -> unit +(** [remove_and_shift v i] remove the [i-th] element from [v]. + Move elements that are after the [i-th] in [v], in linear time. + Preserve the order of the elements in [v]. + See {!remove_unordered} for constant time removal function that doesn't + preserve the order of elements. *) + +val remove_unordered : 'a t -> int -> unit +(** [remove_unordered v i] remove the [i-th] element from [v]. + Does {b NOT} preserve the order of the elements in [v] + (might swap with the last element). + See {!remove_and_shift} if you want to keep the ordering. *) + +val insert : 'a t -> int -> 'a -> unit +(** [insert v i x] insert the given element at index i. + Elements at location [i] and later are first shifted over in linear time before inserting [x]. + Preserve the order of elements in [v]. *) + +val rev : 'a t -> 'a t +(** Reverse the vector. *) + +val rev_in_place : 'a t -> unit +(** Reverse the vector in place. *) + +val rev_iter : ('a -> unit) -> 'a t -> unit +(** [rev_iter f a] is the same as [iter f (rev a)], only more efficient. *) + +val size : _ t -> int +(** Number of elements in the vector. *) + +val length : _ t -> int +(** Synonym for {! size}. *) + +val capacity : _ t -> int +(** Number of elements the vector can contain without being resized. *) + +val unsafe_get_array : 'a t -> 'a array +(** Access the underlying {b shared} array (do not modify!). + [unsafe_get_array v] is longer than [size v], but elements at higher + index than [size v] are undefined (do not access!). *) + +val of_array : 'a array -> 'a t +(** [of_array a] returns a vector corresponding to the array [a]. Operates in [O(n)] time. *) + +val of_list : 'a list -> 'a t + +val to_array : 'a t -> 'a array +(** [to_array v] returns an array corresponding to the vector [v]. + This allocates a new array. *) + +val to_list : 'a t -> 'a list +(** Return a list with the elements contained in the vector. *) + +val of_iter : ?init:'a t -> 'a iter -> 'a t +(** Convert an Iterator to a vector. *) + +val of_seq : ?init:'a t -> 'a Seq.t -> 'a t +(** Convert an Iterator to a vector. *) + +val to_iter : 'a t -> 'a iter +(** Return a [iter] with the elements contained in the vector. *) + +val to_iter_rev : 'a t -> 'a iter +(** [to_iter_rev v] returns the sequence of elements of [v] in reverse order, + that is, the last elements of [v] are iterated on first. +*) + +val to_seq : 'a t -> 'a Seq.t +(** Return an iterator with the elements contained in the vector. + Renamed from [to_std_seq] since 3.0. +*) + +val to_seq_rev : 'a t -> 'a Seq.t +(** [to_seq v] returns the sequence of elements of [v] in reverse order, + that is, the last elements of [v] are iterated on first. +*) + +val unsafe_slice : 'a t -> 'a array * int * int +(** Vector as an array slice. By doing it we expose the internal array, so + be careful!. *) + +val slice_iter : 'a t -> int -> int -> 'a iter +(** [slice_iter v start len] is the sequence of elements from [v.(start)] + to [v.(start+len-1)]. +*) + +val to_string : + ?start:string -> + ?stop:string -> + ?sep:string -> + ('a -> string) -> + 'a t -> + string +(** Print the vector in a string. *) + +val pp : + ?pp_start:unit printer -> + ?pp_stop:unit printer -> + ?pp_sep:unit printer -> + 'a printer -> + 'a t printer +(** [pp ~pp_start ~pp_stop ~pp_sep pp_item ppf v] formats the vector [v] on [ppf]. + Each element is formatted with [pp_item], [pp_start] is called at the beginning, + [pp_stop] is called at the end, [pp_sep] is called between each elements. + By defaults [pp_start] and [pp_stop] does nothing and [pp_sep] defaults to + (fun out -> Format.fprintf out ",@ "). *) diff --git a/src/core/CCVector.mli b/src/core/CCVector.mli index 7e858453b..7d2de5a0a 100644 --- a/src/core/CCVector.mli +++ b/src/core/CCVector.mli @@ -1,6 +1,11 @@ (* This file is free software, part of containers. See file "license" for more details. *) -(** Growable, mutable vector *) +(** Growable, mutable vector + + + @deprecated since NEXT_RELEASE , see {!CCVec} instead *) + +[@@@deprecated "use CCVec"] type ro = [ `RO ] type rw = [ `RW ] From 8388e17ed9d6712f9f7f08b737df32c2b4db2372 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Jun 2023 12:00:56 -0400 Subject: [PATCH 2/7] tests: add tests for CCVec --- tests/core/t.ml | 1 + tests/core/t_vec.ml | 733 +++++++++++++++++++++++++++++++++++++++++ tests/core/t_vector.ml | 3 + 3 files changed, 737 insertions(+) create mode 100644 tests/core/t_vec.ml diff --git a/tests/core/t.ml b/tests/core/t.ml index 8508555a6..9341b10ef 100644 --- a/tests/core/t.ml +++ b/tests/core/t.ml @@ -31,6 +31,7 @@ Containers_testlib.run_all ~descr:"containers" T_sexp.get (); T_string.get (); T_utf8string.get (); + T_vec.get (); T_vector.get (); T_bencode.get (); T_cbor.get (); diff --git a/tests/core/t_vec.ml b/tests/core/t_vec.ml new file mode 100644 index 000000000..a67eec2df --- /dev/null +++ b/tests/core/t_vec.ml @@ -0,0 +1,733 @@ +module T = (val Containers_testlib.make ~__FILE__ ()) +include T +open CCVec;; + +t ~name:__LOC__ @@ fun () -> create_with ~capacity:200 1 |> capacity >= 200;; +t ~name:__LOC__ @@ fun () -> return 42 |> to_list = [ 42 ];; +t ~name:__LOC__ @@ fun () -> return 42 |> length = 1;; + +t ~name:__LOC__ @@ fun () -> +let v = create_with ~capacity:10 1 in +ensure v 200; +capacity v >= 200 +;; + +t ~name:__LOC__ @@ fun () -> +let v = create () in +push v 0.; +push v 1.; +push v 2.; +3 = length v +;; + +t ~name:__LOC__ @@ fun () -> +let v = create () in +push v 1.; +push v 2.; +push v 3.; +6. = get v 0 +. get v 1 +. get v 2 +;; + +t ~name:__LOC__ @@ fun () -> +let v = create () in +push v 0; +push v 1; +push v 2; +3 = length v +;; + +t ~name:__LOC__ @@ fun () -> +let v = create () in +push v 1; +push v 2; +push v 3; +6 = get v 0 + get v 1 + get v 2 +;; + +t ~name:__LOC__ @@ fun () -> +let v = create () in +push v "a"; +push v "b"; +push v "c"; +3 = length v +;; + +t ~name:__LOC__ @@ fun () -> +let v = create () in +push v "a"; +push v "b"; +push v "c"; +"abc" = String.concat "" (to_list v) +;; + +t ~name:__LOC__ @@ fun () -> +let v = create () in +push v 0.; +push v 1.; +clear v; +push v 0.; +push v 1.; +push v 7.; +push v 10.; +push v 12.; +truncate v 2; +assert_equal 1. (fold ( +. ) 0. v); +clear v; +assert_equal 0 (size v); +push v 0.; +push v 1.; +push v 7.; +push v 10.; +push v 12.; +assert_equal (1. +. 7. +. 10. +. 12.) (fold ( +. ) 0. v); +true +;; + +t ~name:__LOC__ @@ fun () -> +let v = of_iter Iter.(1 -- 10) in +assert_equal 10 (size v); +clear v; +assert_equal 0 (size v); +assert (Iter.is_empty (to_iter v)); +true +;; + +t ~name:__LOC__ (fun () -> + let v = of_iter Iter.(1 -- 10) in + assert_equal CCList.(1 -- 10) (to_seq v |> CCList.of_seq); + + (* test that we capture the length *) + let seq = to_seq v in + push v 11; + assert_equal CCList.(1 -- 10) (seq |> CCList.of_seq); + assert_equal CCList.(1 -- 11) (to_seq v |> CCList.of_seq); + + clear v; + assert_equal 0 (size v); + assert (CCSeq.is_empty (to_seq v)); + true) +;; + +t ~name:__LOC__ (fun () -> + let v = of_iter Iter.(1 -- 10) in + assert_equal CCList.(1 -- 10 |> rev) (to_seq_rev v |> CCList.of_seq); + clear v; + assert_equal 0 (size v); + assert (CCSeq.is_empty (to_seq v)); + true) +;; + +t ~name:__LOC__ @@ fun () -> +let v = create () in +push v 1; +to_list v = [ 1 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = of_list [ 1; 2; 3 ] in +push v 4; +to_list v = [ 1; 2; 3; 4 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 1 0 in +resize_with v (fun i -> i) 5; +to_list v = [ 0; 1; 2; 3; 4 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 1 0 in +resize_with v (fun i -> i) 5; +CCList.length (to_list v) = 5 +;; + +t ~name:__LOC__ @@ fun () -> +let v = create_with ~capacity:2 0 in +resize_with v (fun i -> i) 5; +to_list v = [ 0; 1; 2; 3; 4 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 5 0 in +resize_with v (fun i -> i) 5; +to_list v = [ 0; 0; 0; 0; 0 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 5 0 in +resize_with v (fun i -> i) 6; +to_list v = [ 0; 0; 0; 0; 0; 5 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 5 0 in +try + resize_with v (fun i -> i) (-1); + false +with Invalid_argument _ -> true +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 5 0 in +resize_with v (fun i -> i) 5; +List.length (to_list v) = 5 +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 1 0 in +resize_with_init v ~init:1 5; +to_list v = [ 0; 1; 1; 1; 1 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 1 0 in +resize_with_init v ~init:1 5; +List.length (to_list v) = 5 +;; + +t ~name:__LOC__ @@ fun () -> +let v = create_with ~capacity:2 0 in +resize_with_init v ~init:1 5; +to_list v = [ 1; 1; 1; 1; 1 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 5 0 in +resize_with_init v ~init:1 5; +to_list v = [ 0; 0; 0; 0; 0 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 3 0 in +resize_with_init v ~init:1 5; +to_list v = [ 0; 0; 0; 1; 1 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 5 0 in +try + resize_with_init v ~init:1 (-1); + false +with Invalid_argument _ -> true +;; + +t ~name:__LOC__ @@ fun () -> +let v = make 5 0 in +resize_with_init v ~init:1 5; +List.length (to_list v) = 5 +;; + +(* test for asymptotic behavior *) +t ~name:__LOC__ @@ fun () -> +let v = make 1 0 in +for i = 0 to 100_000 do + resize_with_init v ~init:10 i +done; +true +;; + +t ~name:__LOC__ @@ fun () -> +let v1 = init 5 (fun i -> i) and v2 = init 5 (fun i -> i + 5) in +append v1 v2; +to_list v1 = CCList.(0 -- 9) +;; + +t ~name:__LOC__ @@ fun () -> +let empty = create () and v2 = init 5 (fun i -> i) in +append empty v2; +to_list empty = CCList.(0 -- 4) +;; + +t ~name:__LOC__ @@ fun () -> +let v1 = init 5 (fun i -> i) and empty = create () in +append v1 empty; +to_list v1 = CCList.(0 -- 4) +;; + +t ~name:__LOC__ @@ fun () -> +let v = init 3 (fun i -> i) in +append v v; +to_list v = [ 0; 1; 2; 0; 1; 2 ] +;; + +t ~name:__LOC__ @@ fun () -> +let empty = create () in +append empty empty; +to_list empty = [] +;; + +t ~name:__LOC__ @@ fun () -> +let a = of_iter Iter.(1 -- 5) in +let b = of_iter Iter.(6 -- 10) in +append a b; +assert_equal 10 (size a); +assert_equal (Iter.to_array Iter.(1 -- 10)) (to_array a); +assert_equal (Iter.to_array Iter.(6 -- 10)) (to_array b); +true +;; + +q + Q.(list_of_size (Gen.int_range 10 10) small_int) + (fun l -> + let v1 = of_list l and v2 = of_list l in + remove_and_shift v1 9; + remove_unordered v2 9; + to_list v1 = to_list v2) +;; + +q + Q.(list_of_size (Gen.int_range 10 10) small_int) + (fun l -> + let l = List.sort CCInt.compare l in + let v = of_list l in + remove_and_shift v 3; + to_list v = List.sort CCInt.compare (to_list v)) +;; + +q + Q.(list_of_size (Gen.int_range 10 10) small_int) + (fun l -> + let l = List.sort CCInt.compare l in + let v1 = of_list l and v2 = of_list l in + remove_and_shift v1 3; + remove_unordered v2 3; + to_list v1 = List.sort CCInt.compare (to_list v2)) +;; + +t ~name:__LOC__ @@ fun () -> +let v = 1 -- 5 in +insert v 3 9; +to_list v = [ 1; 2; 3; 9; 4; 5 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = create () in +insert v 0 2; +to_list v = [ 2 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = 1 -- 3 in +remove_and_shift v 1; +insert v 1 5; +to_list v = [ 1; 5; 3 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = 1 -- 3 in +remove_and_shift v 0; +insert v 2 5; +to_list v = [ 2; 3; 5 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v = 1 -- 3 in +insert v 3 5; +to_list v = [ 1; 2; 3; 5 ] +;; + +t ~name:__LOC__ @@ fun () -> +let v1 = init 5 (fun i -> i) and v2 = Array.init 5 (fun i -> i + 5) in +append_array v1 v2; +to_list v1 = CCList.(0 -- 9) +;; + +t ~name:__LOC__ @@ fun () -> +let empty = create () in +append_array empty CCArray.(0 -- 5); +to_list empty = CCList.(0 -- 5) +;; + +t ~name:__LOC__ @@ fun () -> +let v1 = init 5 (fun i -> i) in +append_array v1 [||]; +to_list v1 = CCList.(0 -- 4) +;; + +t ~name:__LOC__ @@ fun () -> +let empty = create () in +append_array empty [||]; +to_list empty = [] +;; + +q + Q.(pair (list int) (list int)) + (fun (l1, l2) -> + let v = of_list l1 in + append_list v l2; + to_list v = l1 @ l2) +;; + +q + Q.(pair (list int) (list int)) + (fun (l1, l2) -> + let v = of_list l1 in + append_list v l2; + length v = List.length l1 + List.length l2) + +let gen x = + let small = length in + let print = + CCOption.map (fun p x -> Q.Print.list p (CCVec.to_list x)) x.Q.print + in + Q.make ?print ~small Q.Gen.(list x.Q.gen >|= of_list) +;; + +q + (Q.pair (gen Q.int) (gen Q.int)) + (fun (v1, v2) -> + let l1 = to_list v1 in + append v1 v2; + Iter.to_list (to_iter v1) + = Iter.(to_list (append (of_list l1) (to_iter v2)))) +;; + +t ~name:__LOC__ @@ fun () -> equal ( = ) (create ()) (create ());; +t ~name:__LOC__ @@ fun () -> equal ( = ) (return 42) (return 42);; +t ~name:__LOC__ @@ fun () -> not (equal ( = ) (create ()) (return 42));; +t ~name:__LOC__ @@ fun () -> not (equal ( = ) (return 42) (create ()));; + +q + Q.( + let g = list_of_size Gen.(0 -- 10) small_int in + pair g g) + (fun (l1, l2) -> equal ( = ) (of_list l1) (of_list l2) = (l1 = l2)) +;; + +q + Q.(pair (small_list small_int) (small_list small_int)) + (fun (l1, l2) -> + let v1 = of_list l1 in + let v2 = of_list l2 in + equal ( = ) v1 v2 = (l1 = l2)) +;; + +q + Q.(pair (small_list small_int) (small_list small_int)) + (fun (l1, l2) -> + let v1 = of_list l1 in + let v2 = of_list l2 in + compare Stdlib.compare v1 v2 = CCList.compare Stdlib.compare l1 l2) +;; + +t ~name:__LOC__ @@ fun () -> 1 -- 10 |> top = Some 10;; +t ~name:__LOC__ @@ fun () -> create () |> top = None;; +t ~name:__LOC__ @@ fun () -> 1 -- 10 |> top_exn = 10;; + +t ~name:__LOC__ @@ fun () -> +let v = of_list [ 1; 2; 3 ] in +let v' = copy v in +to_list v' = [ 1; 2; 3 ] +;; + +t ~name:__LOC__ @@ fun () -> create () |> copy |> is_empty;; + +t ~name:__LOC__ @@ fun () -> +let v = of_iter Iter.(1 -- 100) in +assert_equal 100 (size v); +let v' = copy v in +assert_equal 100 (size v'); +clear v'; +assert (is_empty v'); +assert (not (is_empty v)); +true +;; + +q + Q.(small_list small_int) + (fun l -> + let v = of_list l in + let v' = copy v in + equal ( = ) v v') +;; + +t ~name:__LOC__ @@ fun () -> +let v = of_iter Iter.(1 -- 10) in +truncate v 5; +assert_equal [ 1; 2; 3; 4; 5 ] (to_list v); +true +;; + +q (gen Q.small_int) (fun v -> + let n = size v / 2 in + let l = to_list v in + let h = Iter.(to_list (take n (of_list l))) in + let v' = copy v in + truncate v' n; + h = to_list v') +;; + +q (gen Q.small_int) (fun v -> + let v' = copy v in + shrink_to_fit v; + to_list v = to_list v') +;; + +q (gen Q.small_int) (fun v -> + let v' = sorted Stdlib.compare v in + let l = to_list v' in + List.sort Stdlib.compare l = l) +;; + +q (gen Q.small_int) (fun v -> + let v' = copy v in + sort Stdlib.compare v'; + let l = to_list v' in + List.sort Stdlib.compare l = l) +;; + +t ~name:__LOC__ @@ fun () -> +let v = of_list [ 1; 4; 5; 3; 2; 4; 1 ] in +uniq_sort Stdlib.compare v; +to_list v = [ 1; 2; 3; 4; 5 ] +;; + +q ~long_factor:10 + Q.(small_list small_int) + (fun l -> + let v = of_list l in + uniq_sort Stdlib.compare v; + to_list v = CCList.sort_uniq ~cmp:Stdlib.compare l) +;; + +t ~name:__LOC__ @@ fun () -> +let v = 0 -- 6 in +iteri (fun i _ -> if i = 3 then remove_unordered v i) v; +length v = 6 +;; + +t ~name:__LOC__ @@ fun () -> +let v = create () in +push v 1; +push v 2; +push v 3; +to_list (map string_of_int v) = [ "1"; "2"; "3" ] +;; + +q + Q.(pair (fun1 Observable.int small_int) (small_list small_int)) + (fun (Q.Fun (_, f), l) -> + let v = of_list l in + to_list (map f v) = List.map f l) +;; + +t ~name:__LOC__ @@ fun () -> +let v = create () in +push v 1; +push v 2; +push v 3; +to_list (mapi (fun i e -> Printf.sprintf "%i %i" i e) v) += [ "0 1"; "1 2"; "2 3" ] +;; + +q + Q.(pair (fun2 Observable.int Observable.int small_int) (small_list small_int)) + (fun (Q.Fun (_, f), l) -> + let v = of_list l in + to_list (mapi f v) = List.mapi f l) +;; + +q + Q.(pair (fun1 Observable.int small_int) (small_list small_int)) + (fun (Q.Fun (_, f), l) -> + let v = of_list l in + map_in_place f v; + to_list v = List.map f l) +;; + +t ~name:__LOC__ @@ fun () -> +let v = 1 -- 10 in +filter_in_place (fun x -> x < 4) v; +to_list v = [ 1; 2; 3 ] +;; + +q + Q.(pair (fun1 Observable.int bool) (small_list small_int)) + (fun (Q.Fun (_, f), l) -> + let v = of_list l in + filter_in_place f v; + to_list v = List.filter f l) +;; + +t ~name:__LOC__ @@ fun () -> +filter (fun x -> x mod 2 = 0) (of_list [ 1; 2; 3; 4; 5 ]) |> to_list = [ 2; 4 ] +;; + +t ~name:__LOC__ @@ fun () -> +filter (fun x -> x mod 2 = 0) (1 -- 1_000_000) |> length = 500_000 +;; + +q + Q.(pair (fun1 Observable.int bool) (small_list small_int)) + (fun (Q.Fun (_, f), l) -> + let v = of_list l in + to_list (filter f v) = List.filter f l) +;; + +t ~name:__LOC__ @@ fun () -> fold ( + ) 0 (of_list [ 1; 2; 3; 4; 5 ]) = 15;; +t ~name:__LOC__ @@ fun () -> fold ( + ) 0 (create ()) = 0;; + +q + Q.(pair (fun2 Observable.int Observable.int small_int) (small_list small_int)) + (fun (Q.Fun (_, f), l) -> + let v = of_list l in + fold f 0 v = List.fold_left f 0 l) +;; + +q + Q.(pair (fun1 Observable.int bool) (small_list small_int)) + (fun (Q.Fun (_, f), l) -> + let v = of_list l in + exists f v = List.exists f l) +;; + +q + Q.(pair (fun1 Observable.int bool) (small_list small_int)) + (fun (Q.Fun (_, f), l) -> + let v = of_list l in + for_all f v = List.for_all f l) +;; + +q + Q.(pair (fun1 Observable.int bool) (small_list small_int)) + (fun (Q.Fun (_, f), l) -> + let v = of_list l in + find f v = CCList.find_pred f l) +;; + +q + Q.(list small_int) + (fun l -> + let v = of_list l in + let f x = x > 30 && x < 35 in + find_map + (fun x -> + if f x then + Some x + else + None) + v + = find f v) +;; + +q + Q.(pair (fun1 Observable.int (option bool)) (small_list small_int)) + (fun (Q.Fun (_, f), l) -> + let v = of_list l in + to_list (filter_map f v) = CCList.filter_map f l) +;; + +q + Q.(pair (fun1 Observable.int (option small_int)) (small_list small_int)) + (fun (Q.Fun (_, f), l) -> + let v = of_list l in + filter_map_in_place f v; + to_list v = CCList.filter_map f l) +;; + +(* check it frees memory properly *) +t ~name:__LOC__ @@ fun () -> +let w = Weak.create 1 in +let v = + let s = "coucou" ^ "lol" in + Weak.set w 0 (Some s); + of_list [ "a"; s ] +in +filter_in_place (fun s -> String.length s <= 1) v; +assert_equal 1 (length v); +assert_equal "a" (get v 0); +Gc.full_major (); +assert_equal None (Weak.get w 0); +true +;; + +eq ~cmp:( = ) + ~printer:Q.Print.(list int) + [ 11; 12; 21; 22 ] + (List.sort CCInt.compare @@ to_list + @@ cartesian_product ( + ) (of_list [ 10; 20 ]) (of_list [ 1; 2 ])) +;; + +eq ~cmp:( = ) + ~printer:Q.Print.(list int) + [ 11; 12; 13; 14 ] + (List.sort CCInt.compare @@ to_list + @@ cartesian_product ( + ) (of_list [ 10 ]) (of_list [ 1; 2; 3; 4 ])) +;; + +q + Q.(small_list small_int) + (fun l -> + let v = of_list l in + rev_in_place v; + to_list v = List.rev l) +;; + +t ~name:__LOC__ @@ fun () -> +rev (of_list [ 1; 2; 3; 4 ]) |> to_list = [ 4; 3; 2; 1 ] +;; + +t ~name:__LOC__ @@ fun () -> +rev (of_list [ 1; 2; 3; 4; 5 ]) |> to_list = [ 5; 4; 3; 2; 1 ] +;; + +t ~name:__LOC__ @@ fun () -> rev (create ()) |> to_list = [];; + +q + Q.(small_list small_int) + (fun l -> + let v = of_list l in + to_list (rev v) = List.rev l) +;; + +t ~name:__LOC__ @@ fun () -> +let v = of_list [ 1; 2; 3 ] in +(fun f -> rev_iter f v) |> Iter.to_list = [ 3; 2; 1 ] +;; + +q + Q.(list int) + (fun l -> + let v = of_list l in + (fun f -> rev_iter f v) |> Iter.to_list = List.rev l) +;; + +t ~name:__LOC__ @@ fun () -> +of_iter Iter.(1 -- 10) |> to_list = CCList.(1 -- 10) +;; + +q + Q.(list int) + (fun l -> + let v = of_list l in + v |> to_iter_rev |> Iter.to_rev_list = l) +;; + +t ~name:__LOC__ @@ fun () -> +slice_iter (of_list [ 0; 1; 2; 3; 4 ]) 1 3 |> CCList.of_iter = [ 1; 2; 3 ] +;; + +t ~name:__LOC__ @@ fun () -> +slice_iter (of_list [ 0; 1; 2; 3; 4 ]) 1 4 |> CCList.of_iter = [ 1; 2; 3; 4 ] +;; + +t ~name:__LOC__ @@ fun () -> +slice_iter (of_list [ 0; 1; 2; 3; 4 ]) 0 5 |> CCList.of_iter = [ 0; 1; 2; 3; 4 ] +;; + +t ~name:__LOC__ @@ fun () -> 1 -- 4 |> to_list = [ 1; 2; 3; 4 ];; +t ~name:__LOC__ @@ fun () -> 4 -- 1 |> to_list = [ 4; 3; 2; 1 ];; +t ~name:__LOC__ @@ fun () -> 0 -- 0 |> to_list = [ 0 ];; + +q + Q.(pair small_int small_int) + (fun (a, b) -> a -- b |> to_list = CCList.(a -- b)) +;; + +q + Q.(pair small_int small_int) + (fun (a, b) -> a --^ b |> to_list = CCList.(a --^ b)) +;; + +t ~name:__LOC__ @@ fun () -> +of_list CCList.(1 -- 300_000) |> to_list = CCList.(1 -- 300_000) diff --git a/tests/core/t_vector.ml b/tests/core/t_vector.ml index 75fed217d..5414bf77e 100644 --- a/tests/core/t_vector.ml +++ b/tests/core/t_vector.ml @@ -1,5 +1,8 @@ module T = (val Containers_testlib.make ~__FILE__ ()) include T + +[@@@ocaml.alert "-deprecated"] + open CCVector;; t @@ fun () -> create_with ~capacity:200 1 |> capacity >= 200;; From 8d5b01211167486d863c9899626163fef809b5e1 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Jun 2023 13:30:22 -0400 Subject: [PATCH 3/7] update readme and CCParse to remove uses of CCVector --- README.md | 53 ++++++++++++++++++++------------------------- src/core/CCParse.ml | 10 ++++----- 2 files changed, 29 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index d12b5a18a..cfa390f0e 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ changes in this release. 2. Another large change is the removal (at last!) of functions deprecated in 2.8, related to the spread of `Seq.t` as the standard iterator type. - Functions like `CCVector.of_seq` now operate on this standard `Seq.t` type, + Functions like `CCVec.of_seq` now operate on this standard `Seq.t` type, and old-time iteration based on [iter](https://github.com/c-cube/iter) is now named `of_iter`, `to_iter`, etc. @@ -439,18 +439,14 @@ map = - : string option = Some "33" ``` -### New types: `CCVector`, `CCHeap`, `CCResult`, `CCSexp`, `CCByte_buffer` +### New types: `CCVec`, `CCHeap`, `CCResult`, `CCSexp`, `CCByte_buffer` Containers also contains (!) a few datatypes that are not from the standard library but that are useful in a lot of situations: -- `CCVector`: - A resizable array, with a mutability parameter. A value of type - `('a, CCVector.ro) CCVector.t` is an immutable vector of values of type `'a`, - whereas a `('a, CCVector.rw) CCVector.t` is a mutable vector that - can be modified. This way, vectors can be used in a quite functional - way, using operations such as `map` or `flat_map`, or in a more - imperative way. +- `CCVec`: + A simple and efficient dynamic array. + This was called `CCVector` but the old module is deprecated now. - `CCHeap`: A priority queue (currently, leftist heaps) functorized over a module `sig val t val leq : t -> t -> bool` that provides a type `t` @@ -474,43 +470,42 @@ library but that are useful in a lot of situations: Now for a few examples: ```ocaml -# (* create a new empty vector. It is mutable, for otherwise it would - not be very useful. *) - CCVector.create;; -- : unit -> ('a, CCVector.rw) CCVector.t = - -# (* init, similar to Array.init, can be used to produce a - vector that is mutable OR immutable (see the 'mut parameter?) *) - CCVector.init ;; -- : int -> (int -> 'a) -> ('a, 'mut) CCVector.t = +# (* create a new empty vector. *) + CCVec.create;; +- : unit -> 'a CCVec.t = + +# (* init, similar to Array.init, can be used to produce a new vector + of given size *) + CCVec.init ;; +- : int -> (int -> 'a) -> 'a CCVec.t = ``` ```ocaml non-deterministic=output # (* use the infix (--) operator for creating a range. Notice that v is a vector of integer but its mutability is not decided yet. *) - let v = CCVector.(1 -- 10);; + let v = CCVec.(1 -- 10);; val v : (int, '_a) CCVector.t = ``` ```ocaml -# Format.printf "v = @[%a@]@." (CCVector.pp CCInt.pp) v;; +# Format.printf "v = @[%a@]@." (CCVec.pp CCInt.pp) v;; v = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 - : unit = () -# CCVector.push v 42;; +# CCVec.push v 42;; - : unit = () # v (* now v is a mutable vector *);; -- : (int, CCVector.rw) CCVector.t = +- : int CCVec.t = # (* functional combinators! *) - let v2 : _ CCVector.ro_vector = v - |> CCVector.map (fun x-> x+1) - |> CCVector.filter (fun x-> x mod 2=0) - |> CCVector.rev ;; -val v2 : int CCVector.ro_vector = + let v2 : _ CCVec.t = v + |> CCVec.map (fun x-> x+1) + |> CCVec.filter (fun x-> x mod 2=0) + |> CCVec.rev ;; +val v2 : int CCVec.t = -# Format.printf "v2 = @[%a@]@." (CCVector.pp CCInt.pp) v2;; +# Format.printf "v2 = @[%a@]@." (CCVec.pp CCInt.pp) v2;; v2 = 10, 8, 6, 4, 2 - : unit = () ``` @@ -521,7 +516,7 @@ module IntHeap = CCHeap.Make(struct type t = int let leq = (<=) end);; ``` ```ocaml -# let h = v2 |> CCVector.to_iter |> IntHeap.of_iter ;; +# let h = v2 |> CCVec.to_iter |> IntHeap.of_iter ;; val h : IntHeap.t = # (* We can print the content of h diff --git a/src/core/CCParse.ml b/src/core/CCParse.ml index 6c865bb9e..cec17d208 100644 --- a/src/core/CCParse.ml +++ b/src/core/CCParse.ml @@ -30,17 +30,17 @@ module Position = struct type t = position let compute_line_offsets_ (s : string) : int array = - let lines = CCVector.create () in + let lines = CCVec.create () in let i = ref 0 in - CCVector.push lines 0; + CCVec.push lines 0; while !i < String.length s do match String.index_from s !i '\n' with | exception Not_found -> i := String.length s | j -> - CCVector.push lines j; + CCVec.push lines j; i := j + 1 done; - CCVector.to_array lines + CCVec.to_array lines let line_offsets_ cs = match cs.line_offsets with @@ -114,7 +114,7 @@ type state = { where: [type global = { mutable memo: Memo_state.t option; - line_offsets: int CCVector.vector; + line_offsets: int CCVec.vector; } with line_offsets used to cache the offset where each line begins, From d4e2daeab5b5820d6696b3f30e28ff4707e98d93 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Jun 2023 21:57:29 -0400 Subject: [PATCH 4/7] remove dead code --- src/core/CCVec.ml | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/core/CCVec.ml b/src/core/CCVec.ml index d9616dca0..96dc1ae3b 100644 --- a/src/core/CCVec.ml +++ b/src/core/CCVec.ml @@ -17,8 +17,6 @@ let fill_with_junk_ (a : _ array) i len : unit = else Array.fill (as_obj_arr a) i len (Obj.repr ()) -let freeze v = { size = v.size; vec = v.vec } -let freeze_copy v = { size = v.size; vec = Array.sub v.vec 0 v.size } let create () = { size = 0; vec = [||] } let create_with ?(capacity = 128) x = From ebff0be5ea6a7e4e18f42f135536e351438f1fff Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Jun 2023 22:00:00 -0400 Subject: [PATCH 5/7] containers: add `Vec = CCVec` --- src/core/containers.ml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/containers.ml b/src/core/containers.ml index be529ebaa..70332108c 100644 --- a/src/core/containers.ml +++ b/src/core/containers.ml @@ -47,7 +47,8 @@ module Result = CCResult module Seq = CCSeq module Set = CCSet module String = CCString -module Vector = CCVector +module Vec = CCVec +module Vector = CCVector [@@deprecated "use Vec"] module Monomorphic = CCMonomorphic module Utf8_string = CCUtf8_string module Unit = CCUnit From 94c89541f53ee6acdd2a2ffcbe328ddd14b4b681 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Jun 2023 22:59:50 -0400 Subject: [PATCH 6/7] remove dead code --- src/core/CCVec.ml | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/src/core/CCVec.ml b/src/core/CCVec.ml index 96dc1ae3b..e70dac668 100644 --- a/src/core/CCVec.ml +++ b/src/core/CCVec.ml @@ -219,13 +219,6 @@ let append_list a b = List.iter (push_unsafe_ a) b; () -let rec append_gen a b = - match b () with - | None -> () - | Some x -> - push a x; - append_gen a b - let equal eq v1 v2 = v1.size = v2.size && @@ -492,15 +485,6 @@ let flat_map f v = iter (fun x -> iter (push v') (f x)) v; v' -let flat_map_iter f v = - let v' = create () in - iter - (fun x -> - let seq = f x in - append_iter v' seq) - v; - v' - let flat_map_seq f v = let v' = create () in iter @@ -635,27 +619,6 @@ let of_list l = let to_array v = Array.sub v.vec 0 v.size let to_list v = List.rev (fold (fun acc x -> x :: acc) [] v) -let of_gen ?(init = create ()) g = - let rec aux g = - match g () with - | None -> init - | Some x -> - push init x; - aux g - in - aux g - -let to_gen v = - let { size; vec } = v in - let i = ref 0 in - fun () -> - if !i < size then ( - let x = vec.(!i) in - incr i; - Some x - ) else - None - let to_string ?(start = "") ?(stop = "") ?(sep = ", ") item_to_string v = start ^ (to_list v |> List.map item_to_string |> String.concat sep) ^ stop From 3b10a14c93d59a942e82f606a3fd44e6b2cd6b54 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Jun 2023 23:12:34 -0400 Subject: [PATCH 7/7] remove dead comment --- src/core/CCVec.ml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/core/CCVec.ml b/src/core/CCVec.ml index e70dac668..b92e6d4b6 100644 --- a/src/core/CCVec.ml +++ b/src/core/CCVec.ml @@ -92,18 +92,6 @@ let clear_and_reset v = v.size <- 0; v.vec <- [||] -(* TODO*) -(* - let v = create() in - let a = Weak.create 1 in - push v ("hello"^"world"); - Weak.set a 0 (Some (get v 0)); - Gc.full_major(); Gc.compact(); - assert_bool "is alive" (Weak.check a 0); - Gc.full_major(); Gc.compact(); - assert_equal None (Weak.get a 0); -*) - let[@inline] is_empty v = v.size = 0 let[@inline] push_unsafe_ v x =