From c575f97369876eff77da067aa8b58880af3c8e11 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 29 Jul 2017 19:24:37 +0200 Subject: [PATCH 01/29] whitespace --- src/core/CCList.ml | 12 ++++++------ src/core/CCList.mli | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/core/CCList.ml b/src/core/CCList.ml index f044884c8..79e508104 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -387,8 +387,8 @@ let combine_gen l1 l2 = res1 = res2) *) -let split l = - let rec direct i l = match l with +let split l = + let rec direct i l = match l with | [] -> [], [] | [x1, y1] -> [x1], [y1] | [x1, y1; x2, y2] -> [x1;x2], [y1;y2] @@ -396,19 +396,19 @@ let split l = | [x1, y1; x2, y2; x3, y3; x4, y4] -> [x1;x2;x3;x4], [y1;y2;y3;y4] | _ when i=0 -> split_slow [] [] l | (x1, y1) :: (x2, y2) :: (x3, y3) :: (x4, y4) :: (x5, y5) :: l' -> - let rx, ry = direct (i-1) l' in + let rx, ry = direct (i-1) l' in x1 :: x2 :: x3 :: x4 :: x5 :: rx, y1 :: y2 :: y3 :: y4 :: y5 :: ry and split_slow acc1 acc2 l = match l with | [] -> List.rev acc1, List.rev acc2 | (x1, x2) :: tail -> let acc1 = x1 :: acc1 - and acc2 = x2 :: acc2 in + and acc2 = x2 :: acc2 in split_slow acc1 acc2 tail - in + in direct direct_depth_default_ l -(*$Q +(*$Q (Q.(list_of_size Gen.(0--10_000) (pair small_int small_string))) (fun l -> \ let (l1, l2) = split l in \ List.length l1 = List.length l \ diff --git a/src/core/CCList.mli b/src/core/CCList.mli index 710dadc8e..f143de167 100644 --- a/src/core/CCList.mli +++ b/src/core/CCList.mli @@ -92,7 +92,7 @@ val combine_gen : 'a list -> 'b list -> ('a * 'b) gen instead, the output has as many pairs as the smallest input list. @since 1.2 *) -val split : ('a * 'b) t -> 'a t * 'b t +val split : ('a * 'b) t -> 'a t * 'b t (** A tail-recursive version of {!List.split}. *) val compare : ('a -> 'a -> int) -> 'a t -> 'a t -> int From 0e3a659cd1b670491e8df64b524813e893125510 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 9 Aug 2017 10:05:39 +0200 Subject: [PATCH 02/29] remove warning in merlin --- .merlin | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.merlin b/.merlin index 09ba253eb..0110f5804 100644 --- a/.merlin +++ b/.merlin @@ -18,4 +18,4 @@ PKG threads PKG threads.posix PKG lwt PKG qcheck -FLG -w +a -w -4 -w -44 +FLG -w +a-4-44-48-60 From cf09112f796e4c8d4b85b889694cadd17b3fe89a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 9 Aug 2017 10:05:55 +0200 Subject: [PATCH 03/29] add `CCWBTree.get_rank{,_exn}` --- src/data/CCWBTree.ml | 42 ++++++++++++++++++++++++++++++++++++++++++ src/data/CCWBTree.mli | 10 ++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/data/CCWBTree.ml b/src/data/CCWBTree.ml index 27d47a4e7..5139d5d36 100644 --- a/src/data/CCWBTree.ml +++ b/src/data/CCWBTree.ml @@ -82,6 +82,16 @@ module type S = sig val nth_exn : int -> 'a t -> key * 'a (** @raise Not_found if the index is invalid *) + val get_rank : key -> 'a t -> int option + (** [get_rank k m] looks for the rank of [k] in [m], i.e. the index + of [k] in the sorted list of bindings of [m]. + [nth_exn (get_rank k m |> Opt.get_exn) m = get m k] should hold. + @since NEXT_RELEASE *) + + val get_rank_exn : key -> 'a t -> int + (** Unsafe version of {!get_rank} + @since NEXT_RELEASE *) + val add : key -> 'a -> 'a t -> 'a t val remove : key -> 'a t -> 'a t @@ -98,8 +108,14 @@ module type S = sig val fold : f:('b -> key -> 'a -> 'b) -> x:'b -> 'a t -> 'b val mapi : f:(key -> 'a -> 'b) -> 'a t -> 'b t + (** Map values, giving both key and value. Will use {!WORD.of_list} to rebuild keys. + @since 0.17 + *) val map : f:('a -> 'b) -> 'a t -> 'b t + (** Map values, giving only the value. + @since 0.17 + *) val iter : f:(key -> 'a -> unit) -> 'a t -> unit @@ -365,6 +381,32 @@ module MakeFull(K : KEY) : S with type key = K.t = struct List.for_all (fun i -> M.nth_exn i m = (i,i)) CCList.(0--1000) *) + let get_rank_exn k m : int = + let rec aux i k m = match m with + | E -> raise Not_found + | N (k', _, l, r, _) -> + match K.compare k k' with + | 0 -> i + weight l + | n when n<0 -> aux i k l + | _ -> aux (1 + weight l + i) k r + in + aux 0 k m + + let get_rank k m : int option = + try Some (get_rank_exn k m) + with Not_found -> None + + (*$QR & ~count:1_000 + Q.(list_of_size Gen.(0 -- 30) (pair small_int small_int)) (fun l -> + let l = CCList.sort_uniq ~cmp:(CCFun.compose_binop fst compare) l in + let m = M.of_list l in + List.for_all + (fun (k,v) -> match M.get_rank k m with + | None -> true + | Some n -> (k,v) = M.nth_exn n m) + l) + *) + let rec fold ~f ~x:acc m = match m with | E -> acc | N (k, v, l, r, _) -> diff --git a/src/data/CCWBTree.mli b/src/data/CCWBTree.mli index c3c85ef88..0f59dc21d 100644 --- a/src/data/CCWBTree.mli +++ b/src/data/CCWBTree.mli @@ -47,6 +47,16 @@ module type S = sig val nth_exn : int -> 'a t -> key * 'a (** @raise Not_found if the index is invalid *) + val get_rank : key -> 'a t -> int option + (** [get_rank k m] looks for the rank of [k] in [m], i.e. the index + of [k] in the sorted list of bindings of [m]. + [nth_exn (get_rank k m |> Opt.get_exn) m = get m k] should hold. + @since NEXT_RELEASE *) + + val get_rank_exn : key -> 'a t -> int + (** Unsafe version of {!get_rank} + @since NEXT_RELEASE *) + val add : key -> 'a -> 'a t -> 'a t val remove : key -> 'a t -> 'a t From 62d76ce5557a6a9500d638671c1a5076bcda3e34 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 11 Aug 2017 00:41:26 +0200 Subject: [PATCH 04/29] change signature of `CCWBTree.get_rank` --- src/data/CCWBTree.ml | 22 +++++++--------------- src/data/CCWBTree.mli | 8 ++------ 2 files changed, 9 insertions(+), 21 deletions(-) diff --git a/src/data/CCWBTree.ml b/src/data/CCWBTree.ml index 5139d5d36..7277707e3 100644 --- a/src/data/CCWBTree.ml +++ b/src/data/CCWBTree.ml @@ -82,14 +82,10 @@ module type S = sig val nth_exn : int -> 'a t -> key * 'a (** @raise Not_found if the index is invalid *) - val get_rank : key -> 'a t -> int option + val get_rank : key -> 'a t -> [`At of int | `After of int | `First] (** [get_rank k m] looks for the rank of [k] in [m], i.e. the index of [k] in the sorted list of bindings of [m]. - [nth_exn (get_rank k m |> Opt.get_exn) m = get m k] should hold. - @since NEXT_RELEASE *) - - val get_rank_exn : key -> 'a t -> int - (** Unsafe version of {!get_rank} + [let (`At n) = get_rank k m in nth_exn n m = get m k] should hold. @since NEXT_RELEASE *) val add : key -> 'a -> 'a t -> 'a t @@ -381,29 +377,25 @@ module MakeFull(K : KEY) : S with type key = K.t = struct List.for_all (fun i -> M.nth_exn i m = (i,i)) CCList.(0--1000) *) - let get_rank_exn k m : int = + let get_rank k m = let rec aux i k m = match m with - | E -> raise Not_found + | E -> if i=0 then `First else `After i | N (k', _, l, r, _) -> match K.compare k k' with - | 0 -> i + weight l + | 0 -> `At (i + weight l) | n when n<0 -> aux i k l | _ -> aux (1 + weight l + i) k r in aux 0 k m - let get_rank k m : int option = - try Some (get_rank_exn k m) - with Not_found -> None - (*$QR & ~count:1_000 Q.(list_of_size Gen.(0 -- 30) (pair small_int small_int)) (fun l -> let l = CCList.sort_uniq ~cmp:(CCFun.compose_binop fst compare) l in let m = M.of_list l in List.for_all (fun (k,v) -> match M.get_rank k m with - | None -> true - | Some n -> (k,v) = M.nth_exn n m) + | `First | `After _ -> true + | `At n -> (k,v) = M.nth_exn n m) l) *) diff --git a/src/data/CCWBTree.mli b/src/data/CCWBTree.mli index 0f59dc21d..40f89b55f 100644 --- a/src/data/CCWBTree.mli +++ b/src/data/CCWBTree.mli @@ -47,14 +47,10 @@ module type S = sig val nth_exn : int -> 'a t -> key * 'a (** @raise Not_found if the index is invalid *) - val get_rank : key -> 'a t -> int option + val get_rank : key -> 'a t -> [`At of int | `After of int | `First] (** [get_rank k m] looks for the rank of [k] in [m], i.e. the index of [k] in the sorted list of bindings of [m]. - [nth_exn (get_rank k m |> Opt.get_exn) m = get m k] should hold. - @since NEXT_RELEASE *) - - val get_rank_exn : key -> 'a t -> int - (** Unsafe version of {!get_rank} + [let (`At n) = get_rank k m in nth_exn n m = get m k] should hold. @since NEXT_RELEASE *) val add : key -> 'a -> 'a t -> 'a t From d086a617f35b3374a1b6957f37a4883cb25edca9 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Aug 2017 15:19:13 +0200 Subject: [PATCH 05/29] update doc gen --- doc/intro.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/intro.txt b/doc/intro.txt index 012442845..43c894e2f 100644 --- a/doc/intro.txt +++ b/doc/intro.txt @@ -82,6 +82,7 @@ CCFlatHashtbl CCGraph CCHashSet CCHashTrie +CCHet CCImmutArray CCIntMap CCMixmap From 90e96e633975ddb7da41b66c5bd6a826e25b8c27 Mon Sep 17 00:00:00 2001 From: CHEN Xian-an Date: Mon, 28 Aug 2017 23:44:43 +0800 Subject: [PATCH 06/29] fix `CCFun.tap` example in doc --- src/core/CCFun.mli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/CCFun.mli b/src/core/CCFun.mli index 496aa1b41..94618a5bd 100644 --- a/src/core/CCFun.mli +++ b/src/core/CCFun.mli @@ -40,7 +40,7 @@ val tap : ('a -> _) -> 'a -> 'a in a pipeline, for instance: {[CCArray.(1 -- 10) |> tap CCArray.shuffle - |> tap CCArray.sort Pervasives.compare + |> tap @@ CCArray.sort Pervasives.compare ]} *) From 54e12b7f628e54a30075a8b857f57b54c518a641 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 7 Sep 2017 10:10:34 +0200 Subject: [PATCH 07/29] style update for some string functions --- src/core/CCString.cppo.ml | 20 +++++++++++--------- src/core/CCString.mli | 6 ++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/core/CCString.cppo.ml b/src/core/CCString.cppo.ml index 14498171f..fab96e70f 100644 --- a/src/core/CCString.cppo.ml +++ b/src/core/CCString.cppo.ml @@ -492,18 +492,20 @@ let repeat s n = let prefix ~pre s = String.length pre <= String.length s && - (let i = ref 0 in - while !i < String.length pre && s.[!i] = pre.[!i] do incr i done; - !i = String.length pre - ) + begin + let i = ref 0 in + while !i < String.length pre && s.[!i] = pre.[!i] do incr i done; + !i = String.length pre + end let suffix ~suf s = String.length suf <= String.length s && - let off = String.length s - String.length suf in - (let i = ref 0 in - while !i < String.length suf && s.[off + !i] = suf.[!i] do incr i done; - !i = String.length suf - ) + begin + let off = String.length s - String.length suf in + let i = ref 0 in + while !i < String.length suf && s.[off + !i] = suf.[!i] do incr i done; + !i = String.length suf + end let take n s = if n < String.length s diff --git a/src/core/CCString.mli b/src/core/CCString.mli index 3d61b5b1e..cb9bbb4f9 100644 --- a/src/core/CCString.mli +++ b/src/core/CCString.mli @@ -212,6 +212,10 @@ val prefix : pre:string -> string -> bool prefix ~pre:"aab" "aabcd" not (prefix ~pre:"ab" "aabcd") not (prefix ~pre:"abcd" "abc") + prefix ~pre:"abc" "abcde" + prefix ~pre:"" "" + prefix ~pre:"" "abc" + prefix ~pre:"abc" "abc" *) val suffix : suf:string -> string -> bool @@ -220,6 +224,8 @@ val suffix : suf:string -> string -> bool (*$T suffix ~suf:"cd" "abcd" + suffix ~suf:"" "" + suffix ~suf:"" "abc" not (suffix ~suf:"cd" "abcde") not (suffix ~suf:"abcd" "cd") *) From d7b90d3ba388f4506396bf1815f06c24dedf3b6d Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 25 Aug 2017 20:17:33 +0200 Subject: [PATCH 08/29] add `CCArray.swap` --- src/core/CCArray.ml | 29 +++++++++++++++++++++++++++++ src/core/CCArray.mli | 10 +++++++--- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/core/CCArray.ml b/src/core/CCArray.ml index 76ed250ae..3daf1e7e7 100644 --- a/src/core/CCArray.ml +++ b/src/core/CCArray.ml @@ -468,6 +468,35 @@ let compare cmp a b = compare CCOrd.compare [| 1; 2; 3 |] [| 1; 2; |] > 0 *) +(* swap elements of array *) +let swap a i j = + if i<>j then ( + let tmp = a.(i) in + a.(i) <- a.(j); + a.(j) <- tmp; + ) + +(*$T + let a = [| 1;2;3 |] in \ + swap a 0 1; \ + a = [| 2;1;3 |] + let a = [| 1;2;3 |] in \ + swap a 0 2; \ + a = [| 3;2;1 |] +*) + +(*$QR + Q.(array small_int) (fun a -> + let b = Array.copy a in + for i = 0 to Array.length a-1 do + for j = i+1 to Array.length a-1 do + swap a i j; done; done; + for i = 0 to Array.length a-1 do + for j = i+1 to Array.length a-1 do + swap a i j; done; done; + a=b) +*) + (* shuffle a[i...j[ using the given int random generator See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle *) let _shuffle _rand_int a i j = diff --git a/src/core/CCArray.mli b/src/core/CCArray.mli index 4511a56be..90dbb2b39 100644 --- a/src/core/CCArray.mli +++ b/src/core/CCArray.mli @@ -23,6 +23,10 @@ val equal : 'a equal -> 'a t equal val compare : 'a ord -> 'a t ord +val swap : 'a t -> int -> int -> unit +(** [swap arr i j] swaps elements at indices [i] and [j]. + @since NEXT_RELEASE *) + val get : 'a t -> int -> 'a val get_safe : 'a t -> int -> 'a option @@ -71,10 +75,10 @@ val sorted : ('a -> 'a -> int) -> 'a t -> 'a array val sort_indices : ('a -> 'a -> int) -> 'a t -> int array (** [sort_indices cmp a] returns a new array [b], with the same length as [a], - such that [b.(i)] is the index at which the [i]-th element of [sorted cmp a] + such that [b.(i)] is the index at which the [i]-th element of [sorted cmp a] appears in [a]. [a] is not modified. - In other words, [map (fun i -> a.(i)) (sort_indices cmp a) = sorted cmp a]. + In other words, [map (fun i -> a.(i)) (sort_indices cmp a) = sorted cmp a]. [sort_indices] yields the inverse permutation of {!sort_ranking}. @since 1.0 *) @@ -84,7 +88,7 @@ val sort_ranking : ('a -> 'a -> int) -> 'a t -> int array such that [b.(i)] is the index at which the [i]-the element of [a] appears in [sorted cmp a]. [a] is not modified. - In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. + In other words, [map (fun i -> (sorted cmp a).(i)) (sort_ranking cmp a) = a]. [sort_ranking] yields the inverse permutation of {!sort_indices}. In the absence of duplicate elements in [a], we also have From 56928a1a156a84fd3c2221362aca62499a0248bb Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 10 Sep 2017 19:56:01 +0200 Subject: [PATCH 09/29] specify behavior of `CCFQueue.take_{front,back}_l` in some corner cases --- src/data/CCFQueue.ml | 6 ++++++ src/data/CCFQueue.mli | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/data/CCFQueue.ml b/src/data/CCFQueue.ml index e1cb77362..dfab9c50b 100644 --- a/src/data/CCFQueue.ml +++ b/src/data/CCFQueue.ml @@ -132,6 +132,9 @@ let take_front q = with Empty -> None let take_front_l n q = + if n<0 then ( + invalid_arg "take_back_l: cannot take negative number of arguments" + ); let rec aux acc q n = if n=0 || is_empty q then List.rev acc, q else @@ -183,6 +186,9 @@ let take_back q = with Empty -> None let take_back_l n q = + if n<0 then ( + invalid_arg "take_back_l: cannot take negative number of arguments" + ); let rec aux acc q n = if n=0 || is_empty q then q, acc else diff --git a/src/data/CCFQueue.mli b/src/data/CCFQueue.mli index fe159c4ee..6bb56af58 100644 --- a/src/data/CCFQueue.mli +++ b/src/data/CCFQueue.mli @@ -38,7 +38,8 @@ val take_front_exn : 'a t -> ('a * 'a t) val take_front_l : int -> 'a t -> 'a list * 'a t (** [take_front_l n q] takes at most [n] elements from the front - of [q], and returns them wrapped in a list *) + of [q], and returns them wrapped in a list + @raise Invalid_argument if n<0 *) val take_front_while : ('a -> bool) -> 'a t -> 'a list * 'a t @@ -51,7 +52,8 @@ val take_back_l : int -> 'a t -> 'a t * 'a list (** [take_back_l n q] removes and returns the last [n] elements of [q]. The elements are in the order of the queue, that is, the head of the returned list is the first element to appear via {!take_front}. - [take_back_l 2 (of_list [1;2;3;4]) = of_list [1;2], [3;4]] *) + [take_back_l 2 (of_list [1;2;3;4]) = of_list [1;2], [3;4]] + @raise Invalid_argument if n<0 *) val take_back_while : ('a -> bool) -> 'a t -> 'a t * 'a list From a323809aa0a2300d18ca061b2b4b8e956c871834 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 12 Sep 2017 10:28:00 +0200 Subject: [PATCH 10/29] faster `CCString.{prefix,suffix}` see https://github.com/ocaml-batteries-team/batteries-included/issues/792 for some discussion --- src/core/CCString.cppo.ml | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/core/CCString.cppo.ml b/src/core/CCString.cppo.ml index fab96e70f..5c8e20874 100644 --- a/src/core/CCString.cppo.ml +++ b/src/core/CCString.cppo.ml @@ -490,22 +490,28 @@ let repeat s n = assert(len > 0); init (len * n) (fun i -> s.[i mod len]) +exception Exit_false + let prefix ~pre s = String.length pre <= String.length s && - begin - let i = ref 0 in - while !i < String.length pre && s.[!i] = pre.[!i] do incr i done; - !i = String.length pre - end + try + for i=0 to String.length pre-1 do + if String.unsafe_get s i != String.unsafe_get pre i + then raise Exit_false + done; + true + with Exit_false -> false let suffix ~suf s = String.length suf <= String.length s && - begin + try let off = String.length s - String.length suf in - let i = ref 0 in - while !i < String.length suf && s.[off + !i] = suf.[!i] do incr i done; - !i = String.length suf - end + for i=0 to String.length suf-1 do + if String.unsafe_get s (off+i) != String.unsafe_get suf i + then raise Exit_false + done; + true + with Exit_false -> false let take n s = if n < String.length s From ff469211afe8da9c898e40e365d1584925c10949 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 12 Sep 2017 10:28:18 +0200 Subject: [PATCH 11/29] benchmarks for string prefix checking --- benchs/run_benchs.ml | 97 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/benchs/run_benchs.ml b/benchs/run_benchs.ml index 1b56a613f..8b89edd6b 100644 --- a/benchs/run_benchs.ml +++ b/benchs/run_benchs.ml @@ -1177,6 +1177,99 @@ module Str = struct let bench_find = bench_find_ ~dir:`Direct let bench_rfind = bench_find_ ~dir:`Reverse + module Pre = struct + let prefix_pure ~pre s = + let rec same s1 s2 i = + if i = String.length s1 then true + else ( + String.unsafe_get s1 i = String.unsafe_get s2 i && same s1 s2 (i+1) + ) + in + String.length pre <= String.length s && + same pre s 0 + + let prefix_while ~pre s = + String.length pre <= String.length s && + begin + let i = ref 0 in + while !i < String.length pre && + String.unsafe_get s !i = String.unsafe_get pre !i + do incr i done; + !i = String.length pre + end + + exception Exit_false + + let prefix_loop ~pre s = + String.length pre <= String.length s && + try + for i=0 to String.length pre-1 do + if String.unsafe_get s i != String.unsafe_get pre i + then raise Exit_false + done; + true + with Exit_false -> false + + let prefix_sub ~pre:prfx s = + let len_s = String.length s in + let len_p = String.length prfx in + if len_s < len_p then + false + else + let sub = String.sub s 0 len_p in + String.equal prfx sub + + let bat_prefix ~pre:p str = + let len = String.length p in + if String.length str < len then false + else + let rec loop str p i = + if i = len then true + else if String.unsafe_get str i <> String.unsafe_get p i then false + else loop str p (i + 1) + in loop str p 0 + + let make ~max_len ~max_len_prefix n = + let rand = Random.State.make_self_init () in + let input = + Array.init n + (fun _ -> + let str = + QCheck.Gen.(string_size ~gen:printable (10 -- max_len)) + |> QCheck.Gen.generate1 ~rand + in + let prfx_len = Random.State.int rand (min max_len_prefix (String.length str + 1)) in + let prfx = + if Random.State.bool rand then + String.sub str 0 prfx_len + else + String.sub str (String.length str - prfx_len) prfx_len + in + (prfx, str)) + in + let output = + Array.map + (fun (pre, str) -> prefix_pure ~pre str) + input + in + let test f () = + Array.iteri + (fun i (pre, y) -> + let res = f ~pre y in + assert (res = output.(i))) + input + in + Benchmark.throughputN 3 + [ + "containers", test CCString.prefix, (); + "while_unsafe", test prefix_while, (); + "loop_unsafe", test prefix_pure, (); + "for_unsafe", test prefix_loop, (); + "sub_eq", test prefix_sub, (); + "bat_prefix", test bat_prefix, (); + ] + end + let () = B.Tree.register ( "string" @>>> [ "find" @>>> @@ -1205,6 +1298,10 @@ module Str = struct ; "50" @>> app_ints (bench_rfind ~size:50) [100; 100_000; 500_000] ; "500" @>> app_ints (bench_rfind ~size:500) [100_000; 500_000] ]; + "prefix" @>>> + [ "max_len:1000,max_pre_len:15" @>> app_ints (Pre.make ~max_len:1000 ~max_len_prefix:15) [100; 1_000]; + "max_len:1000,max_pre_len:100" @>> app_ints (Pre.make ~max_len:1000 ~max_len_prefix:100) [100; 1_000]; + ] ]) end From 7405c1c34693c9ad6419fc1f2d7256233389e983 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 12 Sep 2017 10:55:24 +0200 Subject: [PATCH 12/29] more tweaks and benchmarks for `CCString.{prefix,suffix}` --- benchs/run_benchs.ml | 11 ++++++----- src/core/CCString.cppo.ml | 40 ++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/benchs/run_benchs.ml b/benchs/run_benchs.ml index 8b89edd6b..c7cf8fe76 100644 --- a/benchs/run_benchs.ml +++ b/benchs/run_benchs.ml @@ -1178,7 +1178,7 @@ module Str = struct let bench_rfind = bench_find_ ~dir:`Reverse module Pre = struct - let prefix_pure ~pre s = + let prefix_rec ~pre s = let rec same s1 s2 i = if i = String.length s1 then true else ( @@ -1200,7 +1200,7 @@ module Str = struct exception Exit_false - let prefix_loop ~pre s = + let prefix_for_exn ~pre s = String.length pre <= String.length s && try for i=0 to String.length pre-1 do @@ -1249,7 +1249,7 @@ module Str = struct in let output = Array.map - (fun (pre, str) -> prefix_pure ~pre str) + (fun (pre, str) -> prefix_rec ~pre str) input in let test f () = @@ -1263,8 +1263,8 @@ module Str = struct [ "containers", test CCString.prefix, (); "while_unsafe", test prefix_while, (); - "loop_unsafe", test prefix_pure, (); - "for_unsafe", test prefix_loop, (); + "rec_unsafe", test prefix_rec, (); + "for_exn_unsafe", test prefix_for_exn, (); "sub_eq", test prefix_sub, (); "bat_prefix", test bat_prefix, (); ] @@ -1301,6 +1301,7 @@ module Str = struct "prefix" @>>> [ "max_len:1000,max_pre_len:15" @>> app_ints (Pre.make ~max_len:1000 ~max_len_prefix:15) [100; 1_000]; "max_len:1000,max_pre_len:100" @>> app_ints (Pre.make ~max_len:1000 ~max_len_prefix:100) [100; 1_000]; + "max_len:1000,max_pre_len:300" @>> app_ints (Pre.make ~max_len:1000 ~max_len_prefix:300) [100; 1_000]; ] ]) diff --git a/src/core/CCString.cppo.ml b/src/core/CCString.cppo.ml index 5c8e20874..9e588999e 100644 --- a/src/core/CCString.cppo.ml +++ b/src/core/CCString.cppo.ml @@ -490,28 +490,30 @@ let repeat s n = assert(len > 0); init (len * n) (fun i -> s.[i mod len]) -exception Exit_false - let prefix ~pre s = - String.length pre <= String.length s && - try - for i=0 to String.length pre-1 do - if String.unsafe_get s i != String.unsafe_get pre i - then raise Exit_false - done; - true - with Exit_false -> false + let len = String.length pre in + if len > String.length s then false + else ( + let rec check i = + if i=len then true + else if String.unsafe_get s i != String.unsafe_get pre i then false + else check (i+1) + in + check 0 + ) let suffix ~suf s = - String.length suf <= String.length s && - try - let off = String.length s - String.length suf in - for i=0 to String.length suf-1 do - if String.unsafe_get s (off+i) != String.unsafe_get suf i - then raise Exit_false - done; - true - with Exit_false -> false + let len = String.length suf in + if len > String.length s then false + else ( + let off = String.length s - len in + let rec check i = + if i=len then true + else if String.unsafe_get s (off+i) != String.unsafe_get suf i then false + else check (i+1) + in + check 0 + ) let take n s = if n < String.length s From c792d70ac7cccfcc0faf466ff23943b3808f4d6c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 13 Sep 2017 18:39:14 +0200 Subject: [PATCH 13/29] assertions and cleanup in `CCPool` --- src/threads/CCPool.ml | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/threads/CCPool.ml b/src/threads/CCPool.ml index 1863e2a8c..914461cce 100644 --- a/src/threads/CCPool.ml +++ b/src/threads/CCPool.ml @@ -69,6 +69,8 @@ module Make(P : PARAM) = struct let incr_size_ p = p.cur_size <- p.cur_size + 1 let decr_size_ p = p.cur_size <- p.cur_size - 1 + let incr_idle_ p = p.cur_idle <- p.cur_idle + 1 + let decr_idle_ p = p.cur_idle <- p.cur_idle - 1 (* next thing a thread should do *) type command = @@ -85,8 +87,7 @@ module Make(P : PARAM) = struct assert (pool.cur_size > 0); decr_size_ pool; Die - ) - else if Queue.is_empty pool.jobs then Wait + ) else if Queue.is_empty pool.jobs then Wait else ( let job = Queue.pop pool.jobs in Process job @@ -94,6 +95,8 @@ module Make(P : PARAM) = struct (* Thread: entry point. They seek jobs in the queue *) let rec serve pool = + assert (pool.cur_size <= P.max_size); + assert (pool.cur_size > 0); let cmd = with_lock_ pool get_next_ in run_cmd cmd @@ -101,7 +104,12 @@ module Make(P : PARAM) = struct and run_cmd = function | Die -> () | Wait -> - with_lock_ pool (fun p -> Condition.wait p.cond p.mutex) + with_lock_ pool + (fun p -> + incr_idle_ pool; + Condition.wait p.cond p.mutex; + decr_idle_ pool); + serve pool | Process (Job1 (f, x)) -> begin try ignore (f x) with e -> pool.exn_handler e end; serve pool | Process (Job2 (f, x, y)) -> @@ -116,6 +124,8 @@ module Make(P : PARAM) = struct (* launch the minimum required number of threads *) let () = + if P.min_size < 0 then invalid_arg "CCPool: min_size must be >= 0"; + if P.min_size > P.max_size then invalid_arg "CCPool: min_size must be <= max_size"; for _i = 1 to P.min_size do launch_worker_ pool done (* heuristic criterion for starting a new thread. *) @@ -137,7 +147,7 @@ module Make(P : PARAM) = struct ) else ( (* cannot start thread, push and wait for some worker to pick it up *) Queue.push job pool.jobs; - Condition.signal pool.cond; (* wake up *) + Condition.signal pool.cond; (* wake up some worker, if any *) (* might want to process in the background, if all threads are busy *) if pool.cur_idle = 0 && can_start_thread_ pool then ( incr_size_ pool; @@ -264,7 +274,7 @@ module Make(P : PARAM) = struct let l = List.rev_map (fun i -> Fut.make (fun () -> - Thread.delay 0.05; + Thread.delay 0.01; 1 )) l in let l' = List.map Fut.get l in From f1942fd0d455e43ba596253e2f1a965fa87e546a Mon Sep 17 00:00:00 2001 From: Fabian Date: Sat, 16 Sep 2017 21:25:55 +0200 Subject: [PATCH 14/29] More tests for CCVector.append and CCVector.append_array --- src/core/CCVector.ml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index fcfd4c87c..05f61ea72 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -165,6 +165,14 @@ let append a b = (*$T 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) + let empty = create () and v2 = init 5 (fun i->i) in \ + append empty v2; to_list empty = CCList.(0--4) + let v1 = init 5 (fun i->i) and empty = create () in \ + append v1 empty; to_list v1 = CCList.(0--4) + let v = init 3 (fun i->i) in \ + append v v; to_list v = [0; 1; 2; 0; 1; 2] + let empty = create () in \ + append empty empty; to_list empty = [] *) (*$R @@ -204,6 +212,12 @@ let append_array a b = (*$T 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) + let empty = create () in \ + append_array empty CCArray.(0--5); to_list empty = CCList.(0--5) + let v1 = init 5 (fun i->i) in \ + append_array v1 [| |]; to_list v1 = CCList.(0--4) + let empty = create () in \ + append_array empty [| |]; to_list empty = [] *) let append_list a b = match b with From 9219d2435631b0970ee217cd9cddaec2ee325f0a Mon Sep 17 00:00:00 2001 From: Fabian Date: Sat, 16 Sep 2017 21:29:47 +0200 Subject: [PATCH 15/29] Fix CCVector.append_array (empty vector case) --- src/core/CCVector.ml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index 05f61ea72..57f5548b1 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -205,9 +205,15 @@ let append_seq a seq = let append_array a b = let len_b = Array.length b in - ensure a (a.size + len_b); - Array.blit b 0 a.vec a.size len_b; - a.size <- a.size + len_b + if _empty_array a then ( + a.vec <- Array.copy b; + a.size <- len_b; + ) + else ( + ensure a (a.size + len_b); + Array.blit b 0 a.vec a.size len_b; + a.size <- a.size + len_b + ) (*$T let v1 = init 5 (fun i->i) and v2 = Array.init 5 (fun i->i+5) in \ From 03f6a1fe5ee4fca81a6b4252fb81c787213ef541 Mon Sep 17 00:00:00 2001 From: Fabian Date: Sat, 16 Sep 2017 21:31:56 +0200 Subject: [PATCH 16/29] Use ensure_not_empty_ when vector is known to be non-empty --- src/core/CCVector.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index 57f5548b1..2d115673d 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -156,7 +156,7 @@ let append a b = a.size <- b.size ) else ( - ensure a (a.size + b.size); + ensure_not_empty_ a (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 @@ -210,7 +210,7 @@ let append_array a b = a.size <- len_b; ) else ( - ensure a (a.size + len_b); + ensure_not_empty_ a (a.size + len_b); Array.blit b 0 a.vec a.size len_b; a.size <- a.size + len_b ) From ea54fdff32c3f099ee91962c2b21c2a2057454ba Mon Sep 17 00:00:00 2001 From: Fabian Date: Sat, 16 Sep 2017 21:34:36 +0200 Subject: [PATCH 17/29] Update authors --- AUTHORS.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AUTHORS.adoc b/AUTHORS.adoc index 561fa6339..b31a8f64b 100644 --- a/AUTHORS.adoc +++ b/AUTHORS.adoc @@ -21,4 +21,5 @@ - Glenn Slotte (glennsl) - @LemonBoy - Leonid Rozenberg (@rleonid) -- Bikal Gurung (@bikalgurung) \ No newline at end of file +- Bikal Gurung (@bikalgurung) +- Fabian Hemmer (copy) From 4cc9862ef8739ed69200b00d80b85a4d205fec44 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 17 Sep 2017 21:24:38 +0200 Subject: [PATCH 18/29] refactoring in vector --- src/core/CCVector.ml | 45 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index 2d115673d..13586ae74 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -67,14 +67,14 @@ let init n f = { vec=Array.init n f; } -(* is the underlying empty? *) -let _empty_array v = +(* is the underlying array empty? *) +let array_is_empty_ v = Array.length v.vec = 0 (* assuming the underlying array isn't empty, resize it *) -let _resize v newcapacity = +let resize_ v newcapacity = assert (newcapacity >= v.size); - assert (not (_empty_array v)); + assert (not (array_is_empty_ v)); let new_vec = Array.make newcapacity v.vec.(0) in Array.blit v.vec 0 new_vec 0 v.size; v.vec <- new_vec; @@ -86,38 +86,38 @@ let _resize v newcapacity = *) (* grow the array, using [x] as a filler if required *) -let _grow v x = - if _empty_array v +let grow_with_ v ~filler:x = + if array_is_empty_ v then v.vec <- Array.make 32 x else ( let n = Array.length v.vec in let size = min (2 * n + 10) Sys.max_array_length in if size = n then failwith "vec: can't grow any further"; - _resize v size + resize_ v size ) (* 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_not_empty_ v size = +let ensure_assuming_not_empty_ v ~size = if size > Sys.max_array_length then failwith "vec.ensure: size too big" else ( let n = ref (max 16 (Array.length v.vec)) in while !n < size do n := min Sys.max_array_length (2* !n) done; - _resize v !n + resize_ v !n ) let ensure_with ~init v size = if Array.length v.vec = 0 then v.vec <- Array.make size init - else ensure_not_empty_ v size + else ensure_assuming_not_empty_ v ~size let ensure v size = if Array.length v.vec = 0 then () - else ensure_not_empty_ v size + else ensure_assuming_not_empty_ v ~size let clear v = v.size <- 0 @@ -137,8 +137,7 @@ let push_unsafe_ v x = v.size <- v.size + 1 let push v x = - if v.size = Array.length v.vec - then _grow v x; + if v.size = Array.length v.vec then grow_with_ v ~filler:x; push_unsafe_ v x (*$T @@ -148,15 +147,14 @@ let push v x = (** Add all elements of b to a *) let append a b = - if _empty_array a - then if _empty_array b - then () + 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_not_empty_ a (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 @@ -205,12 +203,11 @@ let append_seq a seq = let append_array a b = let len_b = Array.length b in - if _empty_array a then ( + if array_is_empty_ a then ( a.vec <- Array.copy b; a.size <- len_b; - ) - else ( - ensure_not_empty_ a (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 ) @@ -441,7 +438,7 @@ let iteri k v = *) let map f v = - if _empty_array v + if array_is_empty_ v then create () else ( let vec = Array.init v.size (fun i -> f (Array.unsafe_get v.vec i)) in @@ -474,7 +471,7 @@ let filter' p v = *) let filter p v = - if _empty_array v + if array_is_empty_ v then create () else ( let v' = create_with ~capacity:v.size v.vec.(0) in From 01e8720797d59a18beeed60ca43a00239208dc14 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 18 Sep 2017 10:23:33 +0200 Subject: [PATCH 19/29] small change for benchs on 4.02 --- benchs/run_benchs.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchs/run_benchs.ml b/benchs/run_benchs.ml index c7cf8fe76..8e10fc0d3 100644 --- a/benchs/run_benchs.ml +++ b/benchs/run_benchs.ml @@ -1217,7 +1217,7 @@ module Str = struct false else let sub = String.sub s 0 len_p in - String.equal prfx sub + CCString.equal prfx sub let bat_prefix ~pre:p str = let len = String.length p in From f254a0f6e423345f3b468b65f31b4cbe0671eb43 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 18 Sep 2017 10:38:54 +0200 Subject: [PATCH 20/29] small doc --- src/data/CCFQueue.mli | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/data/CCFQueue.mli b/src/data/CCFQueue.mli index 6bb56af58..fddb78acd 100644 --- a/src/data/CCFQueue.mli +++ b/src/data/CCFQueue.mli @@ -47,6 +47,8 @@ val take_back : 'a t -> ('a t * 'a) option (** Take last element *) val take_back_exn : 'a t -> ('a t * 'a) +(** Same as {!take_back}, but fails on empty queues. + @raise Empty if the queue is empty *) val take_back_l : int -> 'a t -> 'a t * 'a list (** [take_back_l n q] removes and returns the last [n] elements of [q]. The From 49721c4bc542ae5faafed78768aae0fa6d892ed5 Mon Sep 17 00:00:00 2001 From: Martin Date: Mon, 18 Sep 2017 10:39:56 +0200 Subject: [PATCH 21/29] CCFQueue.take_back_exn raised InvalidArg instead of Empty on an empty queue --- src/data/CCFQueue.ml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/data/CCFQueue.ml b/src/data/CCFQueue.ml index dfab9c50b..de6848f1b 100644 --- a/src/data/CCFQueue.ml +++ b/src/data/CCFQueue.ml @@ -131,6 +131,10 @@ let take_front q = try Some (take_front_exn q) with Empty -> None +(*$T + take_front empty = None +*) + let take_front_l n q = if n<0 then ( invalid_arg "take_back_l: cannot take negative number of arguments" @@ -161,7 +165,7 @@ let take_front_while p q = let rec take_back_exn : 'a. 'a t -> 'a t * 'a = fun q -> match q with - | Shallow Zero -> invalid_arg "FQueue.take_back_exn" + | Shallow Zero -> raise Empty | Shallow (One x) -> empty, x | Shallow (Two (x,y)) -> _single x, y | Shallow (Three (x,y,z)) -> Shallow (Two(x,y)), z @@ -185,6 +189,10 @@ let take_back q = try Some (take_back_exn q) with Empty -> None +(*$T + take_back empty = None +*) + let take_back_l n q = if n<0 then ( invalid_arg "take_back_l: cannot take negative number of arguments" From 9beab5c3e6e29cc1b4db947e31ea237ed06ecac7 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 18 Sep 2017 10:58:58 +0200 Subject: [PATCH 22/29] update readme --- README.adoc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/README.adoc b/README.adoc index c84423671..35751dd01 100644 --- a/README.adoc +++ b/README.adoc @@ -96,14 +96,8 @@ and <> for a gentle introduction. In general, see http://c-cube.github.io/ocaml-containers/last/ or http://cedeela.fr/~simon/software/containers for the **API documentation**. -Some examples can be found link:doc/containers.adoc[there]. - -API documentation by version: - -- http://c-cube.github.io/ocaml-containers/dev/[dev branch] -- http://c-cube.github.io/ocaml-containers/1.0/[1.0] -- http://c-cube.github.io/ocaml-containers/0.19/[0.19] -- http://c-cube.github.io/ocaml-containers/0.17/[0.17] +Some examples can be found link:doc/containers.adoc[there], +per-version doc http://c-cube.github.io/ocaml-containers/[there]. [[build]] == Build From e9b9ed1d9252edcbd9bf531a850f2f9a65bfdd00 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 22 Sep 2017 17:52:24 +0200 Subject: [PATCH 23/29] add `CCRef.swap` --- src/core/CCRef.ml | 5 +++++ src/core/CCRef.mli | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/core/CCRef.ml b/src/core/CCRef.ml index 5fb260b03..44eab3448 100644 --- a/src/core/CCRef.ml +++ b/src/core/CCRef.ml @@ -32,6 +32,11 @@ let compare f r1 r2 = f !r1 !r2 let equal f r1 r2 = f !r1 !r2 +let swap a b = + let x = !a in + a := !b; + b := x + let to_list r = [!r] let to_seq r yield = yield !r diff --git a/src/core/CCRef.mli b/src/core/CCRef.mli index 4a488600c..ed7fc05a3 100644 --- a/src/core/CCRef.mli +++ b/src/core/CCRef.mli @@ -31,6 +31,10 @@ val get_then_incr : int t -> int (** [get_then_incr r] increments [r] and returns its old value, think [r++] @since 0.17 *) +val swap : 'a t -> 'a t -> unit +(** Swap values. + @since NEXT_RELEASE *) + val compare : 'a ord -> 'a t ord val equal : 'a eq -> 'a t eq From 409612297994996e82ca977db765958d466d1282 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 30 Sep 2017 15:02:03 +0200 Subject: [PATCH 24/29] add `CCMap.union` --- src/core/CCMap.ml | 14 ++++++++++++++ src/core/CCMap.mli | 5 +++++ 2 files changed, 19 insertions(+) diff --git a/src/core/CCMap.ml b/src/core/CCMap.ml index 056c01d2b..1f2a5d559 100644 --- a/src/core/CCMap.ml +++ b/src/core/CCMap.ml @@ -29,6 +29,11 @@ module type S = sig (** [merge_safe ~f a b] merges the maps [a] and [b] together. @since 0.17 *) + val union : (key -> 'a -> 'a -> 'a option) -> 'a t -> 'a t -> 'a t + (** Union of both maps, using the function to combine bindings + that belong to both inputs + @since NEXT_RELEASE *) + val of_seq : (key * 'a) sequence -> 'a t val add_seq : 'a t -> (key * 'a) sequence -> 'a t @@ -85,6 +90,15 @@ module Make(O : Map.OrderedType) = struct | Some v1, Some v2 -> f k (`Both (v1,v2))) a b + let union f a b = + merge + (fun k v1 v2 -> match v1, v2 with + | None, None -> assert false + | None, (Some _ as r) -> r + | Some _ as r, None -> r + | Some v1, Some v2 -> f k v1 v2) + a b + let add_seq m s = let m = ref m in s (fun (k,v) -> m := add k v !m); diff --git a/src/core/CCMap.mli b/src/core/CCMap.mli index 8ba47dd73..15e3c24aa 100644 --- a/src/core/CCMap.mli +++ b/src/core/CCMap.mli @@ -32,6 +32,11 @@ module type S = sig (** [merge_safe ~f a b] merges the maps [a] and [b] together. @since 0.17 *) + val union : (key -> 'a -> 'a -> 'a option) -> 'a t -> 'a t -> 'a t + (** Union of both maps, using the function to combine bindings + that belong to both inputs + @since NEXT_RELEASE *) + val of_seq : (key * 'a) sequence -> 'a t (** Same as {!of_list} *) From 0aaab670f728dcf937c145120c59f34a50a45724 Mon Sep 17 00:00:00 2001 From: Yotam Barnoy Date: Tue, 3 Oct 2017 17:45:26 -0400 Subject: [PATCH 25/29] list: Use efficient chunking algorithm for tail-recursive list map See discussion at https://discuss.ocaml.org/t/a-new-list-map-that-is-both-stack-safe-and-fast/865/32 --- src/core/CCList.ml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/core/CCList.ml b/src/core/CCList.ml index 79e508104..16cc5ce70 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -20,13 +20,33 @@ let is_empty = function (* max depth for direct recursion *) let direct_depth_default_ = 1000 +let tail_map f l = + (* Unwind the list of tuples, reconstructing the full list front-to-back *) + let rec rise ys = function + | [] -> ys + | (y0, y1, y2, y3, y4, y5, y6, y7, y8) :: bs -> + rise (y0 :: y1 :: y2 :: y3 :: y4 :: y5 :: y6 :: y7 :: y8 :: ys) bs + in + (* Create a compressed reverse-list representation using tuples *) + let rec dive acc = function + | x0 :: x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: x8 :: xs -> + let y0 = f x0 in let y1 = f x1 in let y2 = f x2 in + let y3 = f x3 in let y4 = f x4 in let y5 = f x5 in + let y6 = f x6 in let y7 = f x7 in + dive ((y0, y1, y2, y3, y4, y5, y6, y7, f x8) :: acc) xs + | xs -> + (* Reverse direction, finishing off with a direct map *) + rise (List.map f xs) acc + in + dive [] l + let map f l = let rec direct f i l = match l with | [] -> [] | [x] -> [f x] | [x1;x2] -> let y1 = f x1 in [y1; f x2] | [x1;x2;x3] -> let y1 = f x1 in let y2 = f x2 in [y1; y2; f x3] - | _ when i=0 -> List.rev (List.rev_map f l) + | _ when i=0 -> tail_map f l | x1::x2::x3::x4::l' -> let y1 = f x1 in let y2 = f x2 in From 1afd0311fc05e7604ee6c0e80f9d15faa8db4747 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 4 Oct 2017 09:08:38 +0200 Subject: [PATCH 26/29] add ocp-indent file --- .ocp-indent | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .ocp-indent diff --git a/.ocp-indent b/.ocp-indent new file mode 100644 index 000000000..98dcc784a --- /dev/null +++ b/.ocp-indent @@ -0,0 +1,2 @@ +match_clause=2 +with=2 From 336ebe63f957f41ec7831c59780a5015c09fb7ea Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 5 Oct 2017 11:59:03 +0200 Subject: [PATCH 27/29] style and comments for the new `CCList.map` --- src/core/CCList.ml | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/core/CCList.ml b/src/core/CCList.ml index 16cc5ce70..4d54f0df9 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -21,22 +21,26 @@ let is_empty = function let direct_depth_default_ = 1000 let tail_map f l = - (* Unwind the list of tuples, reconstructing the full list front-to-back *) - let rec rise ys = function - | [] -> ys + (* Unwind the list of tuples, reconstructing the full list front-to-back. + @param tail_acc a suffix of the final list; we append tuples' content + at the front of it *) + let rec rebuild tail_acc = function + | [] -> tail_acc | (y0, y1, y2, y3, y4, y5, y6, y7, y8) :: bs -> - rise (y0 :: y1 :: y2 :: y3 :: y4 :: y5 :: y6 :: y7 :: y8 :: ys) bs + rebuild (y0 :: y1 :: y2 :: y3 :: y4 :: y5 :: y6 :: y7 :: y8 :: tail_acc) bs in - (* Create a compressed reverse-list representation using tuples *) - let rec dive acc = function + (* Create a compressed reverse-list representation using tuples + @param tuple_acc a reverse list of chunks mapped with [f] *) + let rec dive tuple_acc = function | x0 :: x1 :: x2 :: x3 :: x4 :: x5 :: x6 :: x7 :: x8 :: xs -> - let y0 = f x0 in let y1 = f x1 in let y2 = f x2 in - let y3 = f x3 in let y4 = f x4 in let y5 = f x5 in - let y6 = f x6 in let y7 = f x7 in - dive ((y0, y1, y2, y3, y4, y5, y6, y7, f x8) :: acc) xs + let y0 = f x0 in let y1 = f x1 in let y2 = f x2 in + let y3 = f x3 in let y4 = f x4 in let y5 = f x5 in + let y6 = f x6 in let y7 = f x7 in let y8 = f x8 in + dive ((y0, y1, y2, y3, y4, y5, y6, y7, y8) :: tuple_acc) xs | xs -> - (* Reverse direction, finishing off with a direct map *) - rise (List.map f xs) acc + (* Reverse direction, finishing off with a direct map *) + let tail = List.map f xs in + rebuild tail tuple_acc in dive [] l From 66a8dfc3969f24c133a54499d0c44cf5cf52601e Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 5 Oct 2017 18:19:48 +0200 Subject: [PATCH 28/29] add a test --- src/core/CCList.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/CCList.ml b/src/core/CCList.ml index 4d54f0df9..89dd6a805 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -950,6 +950,11 @@ let uniq ?(eq=(=)) l = uniq [1;1;2;2;3;4;4;2;4;1;5] |> List.sort Pervasives.compare = [1;2;3;4;5] *) +(*$Q + Q.(small_list small_int) (fun l -> \ + sort_uniq l = (uniq l |> sort Pervasives.compare)) + *) + let union ?(eq=(=)) l1 l2 = let rec union eq acc l1 l2 = match l1 with | [] -> List.rev_append acc l2 From d6f7f1570ef08771205714ab47bde3262c2335c2 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 11 Oct 2017 09:29:31 +0200 Subject: [PATCH 29/29] prepare for 1.4 --- CHANGELOG.adoc | 20 ++++++++++++++++++++ HOWTO.adoc | 2 +- _oasis | 2 +- src/core/CCArray.mli | 2 +- src/core/CCFormat.mli | 8 ++++---- src/core/CCMap.ml | 2 +- src/core/CCMap.mli | 2 +- src/core/CCRef.mli | 2 +- src/data/CCWBTree.ml | 2 +- src/data/CCWBTree.mli | 2 +- 10 files changed, 32 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 92ce2c9d0..badb978ef 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,5 +1,25 @@ = Changelog +== 1.4 + +- add `CCMap.union` +- add `CCRef.swap` +- add `CCArray.swap` +- change signature of `CCWBTree.get_rank` +- add `CCWBTree.get_rank{,_exn}` + +- more efficient `List.map` Using efficient chunking algorithm +- Fix `CCVector.append_array` (empty vector case) +- `CCFQueue.take_back_exn` raised InvalidArg instead of Empty on an empty queue +- faster `CCString.{prefix,suffix}` +- speed improvements and benchmarks for `CCString.{prefix,suffix}` + +- add ocp-indent file +- fix `CCFun.tap` example in doc +- specify behavior of `CCFQueue.take_{front,back}_l` in some corner cases +- More tests for CCVector.append and CCVector.append_array +- assertions and cleanup in `CCPool` + == 1.3 - deprecate `CCBool.negate` diff --git a/HOWTO.adoc b/HOWTO.adoc index 599a3e7ca..ea0f84d96 100644 --- a/HOWTO.adoc +++ b/HOWTO.adoc @@ -10,7 +10,7 @@ can be removed. . `make update_next_tag` (to update `@since` comments; be careful not to change symlinks) . check status of modules (`{b status: foo}`) and update if required; removed deprecated functions, etc. -. update `CHANGELOG.md` (see its end to find the right git command) +. update `CHANGELOG.adoc` (see its end to find the right git command) . commit the changes . `git checkout stable` . `git merge master` diff --git a/_oasis b/_oasis index 7547035f2..99dfa6f11 100644 --- a/_oasis +++ b/_oasis @@ -1,6 +1,6 @@ OASISFormat: 0.4 Name: containers -Version: 1.3 +Version: 1.4 Homepage: https://github.com/c-cube/ocaml-containers Authors: Simon Cruanes License: BSD-2-clause diff --git a/src/core/CCArray.mli b/src/core/CCArray.mli index 90dbb2b39..d0b5e4f94 100644 --- a/src/core/CCArray.mli +++ b/src/core/CCArray.mli @@ -25,7 +25,7 @@ val compare : 'a ord -> 'a t ord val swap : 'a t -> int -> int -> unit (** [swap arr i j] swaps elements at indices [i] and [j]. - @since NEXT_RELEASE *) + @since 1.4 *) val get : 'a t -> int -> 'a diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index 293608233..658307915 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -162,7 +162,7 @@ val some : 'a printer -> 'a option printer "what is your @{favorite color@}? @{blue@}! No, @{red@}! Ahhhhhhh@.";; ]} - {b status: experimental} + {b status: unstable} @since 0.15 *) val set_color_tag_handling : t -> unit @@ -177,13 +177,13 @@ val set_color_default : bool -> unit val with_color : string -> 'a printer -> 'a printer (** [with_color "Blue" pp] behaves like the printer [pp], but with the given style. - {b status: experimental} + {b status: unstable} @since 0.16 *) val with_colorf : string -> t -> ('a, t, unit, unit) format4 -> 'a (** [with_colorf "Blue" out "%s %d" "yolo" 42] will behave like {!Format.fprintf}, but wrapping the content with the given style - {b status: experimental} + {b status: unstable} @since 0.16 *) val with_color_sf : string -> ('a, t, unit, string) format4 -> 'a @@ -193,7 +193,7 @@ val with_color_sf : string -> ('a, t, unit, string) format4 -> 'a {[ CCFormat.with_color_sf "red" "%a" CCFormat.Dump.(list int) [1;2;3] |> print_endline;; ]} - {b status: experimental} + {b status: unstable} @since 0.21 *) val with_color_ksf : f:(string -> 'b) -> string -> ('a, t, unit, 'b) format4 -> 'a diff --git a/src/core/CCMap.ml b/src/core/CCMap.ml index 1f2a5d559..8caa6a111 100644 --- a/src/core/CCMap.ml +++ b/src/core/CCMap.ml @@ -32,7 +32,7 @@ module type S = sig val union : (key -> 'a -> 'a -> 'a option) -> 'a t -> 'a t -> 'a t (** Union of both maps, using the function to combine bindings that belong to both inputs - @since NEXT_RELEASE *) + @since 1.4 *) val of_seq : (key * 'a) sequence -> 'a t diff --git a/src/core/CCMap.mli b/src/core/CCMap.mli index 15e3c24aa..5804b5a6d 100644 --- a/src/core/CCMap.mli +++ b/src/core/CCMap.mli @@ -35,7 +35,7 @@ module type S = sig val union : (key -> 'a -> 'a -> 'a option) -> 'a t -> 'a t -> 'a t (** Union of both maps, using the function to combine bindings that belong to both inputs - @since NEXT_RELEASE *) + @since 1.4 *) val of_seq : (key * 'a) sequence -> 'a t (** Same as {!of_list} *) diff --git a/src/core/CCRef.mli b/src/core/CCRef.mli index ed7fc05a3..c5d4750c3 100644 --- a/src/core/CCRef.mli +++ b/src/core/CCRef.mli @@ -33,7 +33,7 @@ val get_then_incr : int t -> int val swap : 'a t -> 'a t -> unit (** Swap values. - @since NEXT_RELEASE *) + @since 1.4 *) val compare : 'a ord -> 'a t ord diff --git a/src/data/CCWBTree.ml b/src/data/CCWBTree.ml index 7277707e3..f3ac33362 100644 --- a/src/data/CCWBTree.ml +++ b/src/data/CCWBTree.ml @@ -86,7 +86,7 @@ module type S = sig (** [get_rank k m] looks for the rank of [k] in [m], i.e. the index of [k] in the sorted list of bindings of [m]. [let (`At n) = get_rank k m in nth_exn n m = get m k] should hold. - @since NEXT_RELEASE *) + @since 1.4 *) val add : key -> 'a -> 'a t -> 'a t diff --git a/src/data/CCWBTree.mli b/src/data/CCWBTree.mli index 40f89b55f..767735db4 100644 --- a/src/data/CCWBTree.mli +++ b/src/data/CCWBTree.mli @@ -51,7 +51,7 @@ module type S = sig (** [get_rank k m] looks for the rank of [k] in [m], i.e. the index of [k] in the sorted list of bindings of [m]. [let (`At n) = get_rank k m in nth_exn n m = get m k] should hold. - @since NEXT_RELEASE *) + @since 1.4 *) val add : key -> 'a -> 'a t -> 'a t