From f78ee1bf92e9de4ae71ea2e506d7c576229654ff Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 11 Feb 2018 10:26:02 -0600 Subject: [PATCH 01/60] add migration guide to the readme --- README.adoc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/README.adoc b/README.adoc index 6df889c9..53a5beab 100644 --- a/README.adoc +++ b/README.adoc @@ -45,6 +45,27 @@ Containers is: Some of the modules have been moved to their own repository (e.g. `sequence`, `gen`, `qcheck`) and are on opam for great fun and profit. +== Migration Guide + +=== To 2.0 + +- The type system should detect issues related to `print` renamed into `pp` easily. + If you are lucky, a call to `sed -i 's/print/pp/g'` on the concerned files + might help rename all the calls + properly. + +- many optional arguments have become mandatory, because their default value + would be a polymorphic "magic" operator such as `(=)` or `(>=)`. + Now these have to be specified explicitly, but during the transition + you can use `Pervasives.(=)` and `Pervasives.(>=)` as explicit arguments. + +- if your code contains `open Containers`, the biggest hurdle you face + might be that operators have become monomorphic by default. + We believe this is a useful change that prevents many subtle bugs. + However, during migration and until you use proper combinators for + equality (`CCEqual`), comparison (`CCOrd`), and hashing (`CCHash`), + you might want to add `open Pervasives` just after the `open Containers`. + == Change Log See link:CHANGELOG.adoc[this file]. From a4697946ac66b2a91495e30101446494bce0b413 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 11 Feb 2018 11:08:51 -0600 Subject: [PATCH 02/60] small typo in readme --- README.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index 53a5beab..1debf137 100644 --- a/README.adoc +++ b/README.adoc @@ -123,7 +123,7 @@ per-version doc http://c-cube.github.io/ocaml-containers/[there]. [[build]] == Build -You will need OCaml `>=` 4.01.0. +You will need OCaml `>=` 4.02.0. === Via opam From f1adbcf2f312e2edd1f0db5931860f4ebe0c3354 Mon Sep 17 00:00:00 2001 From: Etienne Millon Date: Wed, 14 Feb 2018 15:13:50 +0100 Subject: [PATCH 03/60] Add CCFun.iterate This adds a new `CCFun.iterate` function that computes the nth-iterate of a function. That is, that function composed with itself n times. --- AUTHORS.adoc | 1 + src/core/CCFun.ml | 20 ++++++++++++++++++++ src/core/CCFun.mli | 5 +++++ 3 files changed, 26 insertions(+) diff --git a/AUTHORS.adoc b/AUTHORS.adoc index 56dba742..93b85e2b 100644 --- a/AUTHORS.adoc +++ b/AUTHORS.adoc @@ -27,3 +27,4 @@ - Orbifx (Stavros Polymenis) - Rand (@rand00) - Dave Aitken (@actionshrimp) +- Etienne Millon (@emillon) diff --git a/src/core/CCFun.ml b/src/core/CCFun.ml index 60bd7c73..e9e49878 100644 --- a/src/core/CCFun.ml +++ b/src/core/CCFun.ml @@ -66,6 +66,26 @@ let finally2 ~h f x y = ignore (h ()); raise e +let rec iterate n f x = + if n < 0 then + invalid_arg "CCFun.iterate" + else if n = 0 then + x + else + iterate (n - 1) f (f x) + +(*$= iterate & ~printer:Q.Print.int + 10 (iterate 0 succ 10) + 11 (iterate 1 succ 10) + 12 (iterate 2 succ 10) + 15 (iterate 5 succ 10) +*) +(*$R + assert_raises + (Invalid_argument "CCFun.iterate") + (fun () -> iterate (-1) succ 10) +*) + module Monad(X : sig type t end) = struct type 'a t = X.t -> 'a let return x _ = x diff --git a/src/core/CCFun.mli b/src/core/CCFun.mli index ca630f7c..55d82c1e 100644 --- a/src/core/CCFun.mli +++ b/src/core/CCFun.mli @@ -76,6 +76,11 @@ val opaque_identity : 'a -> 'a in OCaml >= 4.03). @since 0.18 *) +val iterate : int -> ('a -> 'a) -> 'a -> 'a +(** [iterate n f] is [f] iterated [n] times. That is to say, [iterate 0 f x] is + [x], [iterate 1 f x] is [f x], [iterate 2 f x] is [f (f x)], etc. + @since NEXT_RELEASE *) + (** {2 Monad} Functions with a fixed domain are monads in their codomain. *) From 7e08d7c7c1c7a15a77b939f8c049a927e0acb4bb Mon Sep 17 00:00:00 2001 From: Jacques-Pascal Deplaix Date: Wed, 14 Feb 2018 17:44:48 +0000 Subject: [PATCH 04/60] Fix #188 --- containers.opam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/containers.opam b/containers.opam index 86a309a6..0fc9832b 100644 --- a/containers.opam +++ b/containers.opam @@ -9,7 +9,7 @@ build: [ build-doc: [ "jbuilder" "build" "@doc" ] build-test: [ "jbuilder" "runtest" "-p" name "-j" jobs] depends: [ - "jbuilder" {build} + "jbuilder" {build & >= "1.0+beta12"} "result" ] depopts: [ From ccad1f3a2cac2997a5467d0b15aa225b6d57f41b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 14 Feb 2018 23:40:15 -0600 Subject: [PATCH 05/60] wip: add `CCFun_vec` data structure for fast functional vectors --- src/data/CCFun_vec.ml | 636 +++++++++++++++++++++++++++++++++++++++++ src/data/CCFun_vec.mli | 142 +++++++++ 2 files changed, 778 insertions(+) create mode 100644 src/data/CCFun_vec.ml create mode 100644 src/data/CCFun_vec.mli diff --git a/src/data/CCFun_vec.ml b/src/data/CCFun_vec.ml new file mode 100644 index 00000000..b91370d4 --- /dev/null +++ b/src/data/CCFun_vec.ml @@ -0,0 +1,636 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +(*$inject + + let _listuniq = + let g = Q.(small_list (pair small_int small_int)) in + Q.map_same_type + (fun l -> + CCList.sort_uniq ~cmp:(fun a b -> Pervasives.compare (fst a)(fst b)) l + ) g + ;; +*) + +(** {1 Hash Tries} *) + +type 'a sequence = ('a -> unit) -> unit +type 'a gen = unit -> 'a option +type 'a printer = Format.formatter -> 'a -> unit +type 'a ktree = unit -> [`Nil | `Node of 'a * 'a ktree list] + +(** {2 Transient IDs} *) +module Transient = struct + type state = { mutable frozen: bool } + type t = Nil | St of state + let empty = Nil + let equal a b = Pervasives.(==) a b + let create () = St {frozen=false} + let active = function Nil -> false | St st -> not st.frozen + let frozen = function Nil -> true | St st -> st.frozen + let freeze = function Nil -> () | St st -> st.frozen <- true + let with_ f = + let r = create() in + try + let x = f r in + freeze r; + x + with e -> + freeze r; + raise e + exception Frozen +end + +(* function array *) +module A = struct + type 'a t = { + arr: 'a array; + id: Transient.t; + } + + let length_log = 5 + let max_length = 32 + let mask = max_length-1 + + let () = assert (max_length = 1 lsl length_log) + + let length a = Array.length a.arr + + let create ~id = { arr= [| |]; id; } + + let empty = {arr=[| |]; id=Transient.empty} + let is_empty a = length a = 0 + + let return x = { arr=[| x |]; id=Transient.empty} + + let owns ~id a = + Transient.active id && Transient.equal id a.id + + let get a i = + if i<0 || i >= length a then invalid_arg "A.get"; + Array.unsafe_get a.arr i + + (* push at the back *) + let push x a = + let n = length a in + if n = max_length then invalid_arg "A.push"; + let arr = Array.make (n+1) x in + Array.blit a.arr 0 arr 0 n; + {a with arr;} + + let pop a = + let n = length a in + if n=0 then invalid_arg "A.pop"; + let arr = Array.sub a.arr 0 (n-1) in + {a with arr} + + let append a b = + let n_a = length a in + let n_b = length b in + if n_a + n_b > max_length then invalid_arg "A.append"; + if n_a = 0 then b + else if n_b = 0 then a + else ( + let arr = Array.make (n_a+n_b) (a.arr.(0)) in + Array.blit a.arr 0 arr 0 n_a; + Array.blit b.arr 0 arr n_a n_b; + {id=Transient.empty; arr} + ) + + let set ~mut a i x = + if i<0 || i > length a || i >= max_length then invalid_arg "A.set"; + if i=length a then ( + (* insert in a longer copy *) + let arr = Array.make (i+1) x in + Array.blit a.arr 0 arr 0 i; + {a with arr} + ) else if mut then ( + (* replace element at [i] in place *) + a.arr.(i) <- x; + a + ) else ( + (* replace element at [i] in copy *) + let arr = Array.copy a.arr in + arr.(i) <- x; + {a with arr} + ) + + let iteri f a = Array.iteri f a.arr + + let fold f acc a = Array.fold_left f acc a.arr +end + +(** {2 Functors} *) + +type 'a t = { + size: int; + leaves: 'a A.t; + subs: 'a t A.t; +} +(* invariant: + - [A.length leaves < A.max_length ==> A.is_empty subs] + - either: + * [exists n. forall i. subs[i].size = n] (all subtrees of same size) + * [exists n i. + (forall j=i, sub[j].size<32^{n+1}-1)] + (prefix of subs has size of complete binary tree; suffix has + smaller size (actually decreasing)) + *) + + +let empty = {size=0; leaves=A.empty; subs=A.empty} + +let is_empty {size;_} = size=0 + +(*$T + is_empty empty +*) + +let length {size;_} = size + +(*$T + not (is_empty (return 2)) + length (return 2) = 1 +*) + +let return x = {leaves=A.return x; subs=A.empty; size=1} + +type idx_l = + | L1 of int + | L2 of int * int + | L3 of int * int * int + | L4 of int * int * int * int + | L_cons of int * idx_l + +let cons_idx x1 l = match l with + | L1 x2 -> L2 (x1,x2) + | L2 (x2,x3) -> L3 (x1,x2,x3) + | L3 (x2,x3,x4) -> L4 (x1,x2,x3,x4) + | L4 _ | L_cons _ -> L_cons (x1, l) + +(* split an index into a low and high parts *) +let low_idx_ i = i land A.mask + +let high_idx_ i = i lsr A.length_log + +let combine_idx i j = (i lsl A.length_log) lor j + +(* split an index into a high part, < 32, and a low part *) +let split_idx i : idx_l = + let rec aux high low = + if high = 0 then low + else if high < A.max_length then cons_idx (high-1) low + else aux (high_idx_ high) (cons_idx (low_idx_ high) low) + in + aux (high_idx_ i) (L1 (low_idx_ i)) + +let get_ (i:int) (m:'a t) : 'a = + let rec aux l m = match l with + | L1 x1 -> + assert (x1 < A.length m.leaves); + A.get m.leaves x1 + | L2 (x1,x2) -> aux (L1 x2) (A.get m.subs x1) + | L3 (x1,x2,x3) -> aux (L2 (x2,x3)) (A.get m.subs x1) + | L4 (x1,x2,x3,x4) -> aux (L3 (x2,x3,x4)) (A.get m.subs x1) + | L_cons (x1,x2) -> aux x2 (A.get m.subs x1) + in + aux (split_idx i) m + +(*$Q + _listuniq (fun l -> \ + let m = of_list l in \ + List.for_all (fun (i,y) -> get_exn i m = y) @@ List.mapi CCPair.make l) +*) + +let get_exn i v = + if i >= 0 && i < length v then get_ i v else raise Not_found + +let get i v = + if i >= 0 && i < length v then Some (get_ i v) else None + +let push_ (i:int) (x:'a) (m:'a t) : 'a t = + let rec aux l m = match l with + | L1 x1 -> + assert (x1=A.length m.leaves); + assert (A.length m.leaves < A.max_length); + assert (A.is_empty m.subs); + {m with size=m.size+1; leaves=A.push x m.leaves} + | L2 (x1,x2) -> aux_replace_sub (L1 x2) m x1 + | L3 (x1,x2,x3) -> aux_replace_sub (L2 (x2,x3)) m x1 + | L4 (x1,x2,x3,x4) -> aux_replace_sub (L3 (x2,x3,x4)) m x1 + | L_cons (x1,x2) -> aux_replace_sub x2 m x1 + and aux_replace_sub l m x = + assert (x <= A.length m.subs); + (* insert in subtree, possibly a new one *) + let sub_m = + if x < A.length m.subs then A.get m.subs x else empty + in + let sub_m = aux l sub_m in + {m with size=m.size+1; subs=A.set ~mut:false m.subs x sub_m} + in + aux (split_idx i) m + +let push x (v:_ t) : _ t = push_ v.size x v + +let pop_ i (m:'a t) : 'a * 'a t = + let rec aux l m = match l with + | L1 x1 -> + assert (x1+1 = A.length m.leaves); (* last one *) + let x = A.get m.leaves x1 in + x, {m with size=m.size-1; leaves=A.pop m.leaves} + | L2 (x1,x2) -> aux_remove_sub (L1 x2) m x1 + | L3 (x1,x2,x3) -> aux_remove_sub (L2 (x2,x3)) m x1 + | L4 (x1,x2,x3,x4) -> aux_remove_sub (L3 (x2,x3,x4)) m x1 + | L_cons (x1,x2) -> aux_remove_sub x2 m x1 + and aux_remove_sub l m x = + let sub = A.get m.subs x in + let y, sub' = aux l sub in + if is_empty sub' then ( + assert (i+1 = A.length m.subs); (* last one *) + y, {m with size=m.size-1; subs=A.pop m.subs} + ) else ( + y, {m with size=m.size-1; subs=A.set ~mut:false m.subs x sub} + ) + in + aux (split_idx i) m + +let pop_exn (v:'a t) : 'a * 'a t = + if v.size=0 then failwith "Fun_vec.pop_exn"; + pop_ v.size v + +let pop (v:'a t) : ('a * 'a t) option = + if v.size=0 then None else Some (pop_ v.size v) + +let iteri ~f (m : 'a t) : unit = + (* basically, a 32-way BFS traversal. + The queue contains subtrees to explore, along with their high_idx_ offsets *) + let q : (int * 'a t) Queue.t = Queue.create() in + Queue.push (0,m) q; + while not (Queue.is_empty q) do + let high, m = Queue.pop q in + A.iteri (fun i x -> f (combine_idx high i) x) m.leaves; + A.iteri (fun i sub -> Queue.push (combine_idx i high, sub) q) m.subs; + done + +let foldi ~f ~x m = + let acc = ref x in + iteri m + ~f:(fun i x -> acc := f !acc i x); + !acc + +let iter ~f m = iteri ~f:(fun _ x -> f x) m + +let fold ~f ~x m = foldi ~f:(fun acc _ x -> f acc x) ~x m + +let of_list l = List.fold_left (fun v x -> push x v) empty l + +let to_list m = fold m ~f:(fun acc x -> x::acc) ~x:[] |> List.rev + +(* TODO + +(* add [k,v] to the list [l], removing old binding if any *) +let rec add_list_ k v l = match l with + | Nil -> One (k,v) + | One (k1, v1) -> + if Key.equal k k1 then One (k, v) else Two (k,v,k1,v1) + | Two (k1, v1, k2, v2) -> + if Key.equal k k1 then Two (k, v, k2, v2) + else if Key.equal k k2 then Two (k, v, k1, v1) + else Cons (k, v, l) + | Cons (k', v', tail) -> + if Key.equal k k' + then Cons (k, v, tail) (* replace *) + else Cons (k', v', add_list_ k v tail) + +let node_ leaf a = N (leaf, a) + + +(* [h]: hash, with the part required to reach this leaf removed + [id] is the transient ID used for mutability *) +let rec add_ ~id k v ~h m = match m with + | E -> S (h, k, v) + | S (h', k', v') -> + if Hash.equal h h' + then if Key.equal k k' + then S (h, k, v) (* replace *) + else L (h, Cons (k, v, Cons (k', v', Nil))) + else + make_array_ ~id ~leaf:(Cons (k', v', Nil)) ~h_leaf:h' k v ~h + | L (h', l) -> + if Hash.equal h h' + then L (h, add_list_ k v l) + else (* split into N *) + make_array_ ~id ~leaf:l ~h_leaf:h' k v ~h + | N (leaf, a) -> + if Hash.is_0 h + then node_ (add_list_ k v leaf) a + else + let mut = A.owns ~id a in (* can we modify [a] in place? *) + node_ leaf (add_to_array_ ~id ~mut k v ~h a) + +(* make an array containing a leaf, and insert (k,v) in it *) +and make_array_ ~id ~leaf ~h_leaf:h' k v ~h = + let a = A.create ~id in + let a, leaf = + if Hash.is_0 h' then a, leaf + else + (* put leaf in the right bucket *) + let i = Hash.rem h' in + let h'' = Hash.quotient h' in + A.set ~mut:true a i (L (h'', leaf)), Nil + in + (* then add new node *) + let a, leaf = + if Hash.is_0 h then a, add_list_ k v leaf + else add_to_array_ ~id ~mut:true k v ~h a, leaf + in + N (leaf, a) + +(* add k->v to [a] *) +and add_to_array_ ~id ~mut k v ~h a = + (* insert in a bucket *) + let i = Hash.rem h in + let h' = Hash.quotient h in + A.update ~default:E ~mut a i (fun x -> add_ ~id k v ~h:h' x) + +let add k v m = add_ ~id:Transient.empty k v ~h:(hash_ k) m + +(*$Q + _listuniq (fun l -> \ + let m = List.fold_left (fun m (x,y) -> add x y m) empty l in \ + List.for_all (fun (x,y) -> get_exn x m = y) l) +*) + +let add_mut ~id k v m = + if Transient.frozen id then raise Transient.Frozen; + add_ ~id k v ~h:(hash_ k) m + +(*$R + let lsort = List.sort Pervasives.compare in + let m = of_list [1, 1; 2, 2] in + let id = Transient.create() in + let m' = add_mut ~id 3 3 m in + let m' = add_mut ~id 4 4 m' in + assert_equal [1, 1; 2, 2] (to_list m |> lsort); + assert_equal [1, 1; 2, 2; 3,3; 4,4] (to_list m' |> lsort); + Transient.freeze id; + assert_bool "must raise" + (try ignore(add_mut ~id 5 5 m'); false with Transient.Frozen -> true) +*) + + +exception LocalExit + +let is_empty_arr_ a = + try + A.iter (fun t -> if not (is_empty t) then raise LocalExit) a; + true + with LocalExit -> false + +let is_empty_list_ = function + | Nil -> true + | One _ + | Two _ + | Cons _ -> false + +let rec remove_list_ k l = match l with + | Nil -> Nil + | One (k', _) -> + if Key.equal k k' then Nil else l + | Two (k1, v1, k2, v2) -> + if Key.equal k k1 then One (k2, v2) + else if Key.equal k k2 then One (k1, v1) + else l + | Cons (k', v', tail) -> + if Key.equal k k' + then tail + else Cons (k', v', remove_list_ k tail) + +let rec remove_rec_ ~id k ~h m = match m with + | E -> E + | S (_, k', _) -> + if Key.equal k k' then E else m + | L (h, l) -> + let l = remove_list_ k l in + if is_empty_list_ l then E else L (h, l) + | N (leaf, a) -> + let leaf, a = + if Hash.is_0 h + then remove_list_ k leaf, a + else + let i = Hash.rem h in + let h' = Hash.quotient h in + let new_t = remove_rec_ ~id k ~h:h' (A.get ~default:E a i) in + if is_empty new_t + then leaf, A.remove a i (* remove sub-tree *) + else + let mut = A.owns ~id a in + leaf, A.set ~mut a i new_t + in + if is_empty_list_ leaf && is_empty_arr_ a + then E + else N (leaf, a) + +let remove k m = remove_rec_ ~id:Transient.empty k ~h:(hash_ k) m + +let remove_mut ~id k m = + if Transient.frozen id then raise Transient.Frozen; + remove_rec_ ~id k ~h:(hash_ k) m + +(*$QR + _listuniq (fun l -> + let m = of_list l in + List.for_all + (fun (x,_) -> + let m' = remove x m in + not (mem x m') && + cardinal m' = cardinal m - 1 && + List.for_all + (fun (y,v) -> y = x || get_exn y m' = v) + l + ) l + ) +*) + +let update_ ~id k f m = + let h = hash_ k in + let opt_v = try Some (get_ k ~h m) with Not_found -> None in + match opt_v, f opt_v with + | None, None -> m + | Some _, Some v + | None, Some v -> add_ ~id k v ~h m + | Some _, None -> remove_rec_ ~id k ~h m + +let update k ~f m = update_ ~id:Transient.empty k f m + +let update_mut ~id k ~f m = + if Transient.frozen id then raise Transient.Frozen; + update_ ~id k f m + +(*$R + let m = of_list [1, 1; 2, 2; 5, 5] in + let m' = update 4 + (function + | None -> Some 4 + | Some _ -> Some 0 + ) m + in + assert_equal [1,1; 2,2; 4,4; 5,5] (to_list m' |> List.sort Pervasives.compare); +*) + +let iter ~f t = + let rec aux = function + | E -> () + | S (_, k, v) -> f k v + | L (_,l) -> aux_list l + | N (l,a) -> aux_list l; A.iter aux a + and aux_list = function + | Nil -> () + | One (k,v) -> f k v + | Two (k1,v1,k2,v2) -> f k1 v1; f k2 v2 + | Cons (k, v, tl) -> f k v; aux_list tl + in + aux t + +let fold ~f ~x:acc t = + let rec aux acc t = match t with + | E -> acc + | S (_,k,v) -> f acc k v + | L (_,l) -> aux_list acc l + | N (l,a) -> let acc = aux_list acc l in A.fold aux acc a + and aux_list acc l = match l with + | Nil -> acc + | One (k,v) -> f acc k v + | Two (k1,v1,k2,v2) -> f (f acc k1 v1) k2 v2 + | Cons (k, v, tl) -> let acc = f acc k v in aux_list acc tl + in + aux acc t + +(*$T + let l = CCList.(1 -- 10 |> map (fun x->x,x)) in \ + of_list l \ + |> fold ~f:(fun acc x y -> (x,y)::acc) ~x:[] \ + |> List.sort Pervasives.compare = l +*) + +let cardinal m = fold ~f:(fun n _ _ -> n+1) ~x:0 m + +let to_list m = fold ~f:(fun acc k v -> (k,v)::acc) ~x:[] m + +let add_list_mut ~id m l = + List.fold_left (fun acc (k,v) -> add_mut ~id k v acc) m l + +let add_list m l = + Transient.with_ (fun id -> add_list_mut ~id m l) + +let of_list l = add_list empty l + +let add_seq_mut ~id m seq = + let m = ref m in + seq (fun (k,v) -> m := add_mut ~id k v !m); + !m + +let add_seq m seq = + Transient.with_ (fun id -> add_seq_mut ~id m seq) + +let of_seq s = add_seq empty s + +let to_seq m yield = iter ~f:(fun k v -> yield (k,v)) m + +(*$Q + _listuniq (fun l -> \ + (List.sort Pervasives.compare l) = \ + (l |> Sequence.of_list |> of_seq |> to_seq |> Sequence.to_list \ + |> List.sort Pervasives.compare) ) +*) + +let rec add_gen_mut ~id m g = match g() with + | None -> m + | Some (k,v) -> add_gen_mut ~id (add_mut ~id k v m) g + +let add_gen m g = + Transient.with_ (fun id -> add_gen_mut ~id m g) + +let of_gen g = add_gen empty g + +(* traverse the tree by increasing hash order, where the order compares + hashes lexicographically by A.length_log-wide chunks of bits, + least-significant chunks first *) +let to_gen m = + let st = Stack.create() in + Stack.push m st; + let rec next() = + if Stack.is_empty st then None + else match Stack.pop st with + | E -> next () + | S (_,k,v) -> Some (k,v) + | L (_, Nil) -> next() + | L (_, One (k,v)) -> Some (k,v) + | L (h, Two (k1,v1,k2,v2)) -> + Stack.push (L (h, One (k2,v2))) st; + Some (k1,v1) + | L (h, Cons(k,v,tl)) -> + Stack.push (L (h, tl)) st; (* tail *) + Some (k,v) + | N (l, a) -> + A.iter + (fun sub -> Stack.push sub st) + a; + Stack.push (L (Hash.zero, l)) st; (* leaf *) + next() + in + next + +(*$Q + _listuniq (fun l -> \ + (List.sort Pervasives.compare l) = \ + (l |> Gen.of_list |> of_gen |> to_gen |> Gen.to_list \ + |> List.sort Pervasives.compare) ) +*) + +let choose m = to_gen m () + +(*$T + choose empty = None + choose (of_list [1,1; 2,2]) <> None +*) + +let choose_exn m = match choose m with + | None -> raise Not_found + | Some (k,v) -> k, v + +let pp ppk ppv out m = + let first = ref true in + iter m + ~f:(fun k v -> + if !first then first := false else Format.fprintf out ";@ "; + ppk out k; + Format.pp_print_string out " -> "; + ppv out v + ) + +let rec as_tree m () = match m with + | E -> `Nil + | S (h,k,v) -> `Node (`L ((h:>int), [k,v]), []) + | L (h,l) -> `Node (`L ((h:>int), list_as_tree_ l), []) + | N (l,a) -> `Node (`N, as_tree (L (Hash.zero, l)) :: array_as_tree_ a) +and list_as_tree_ l = match l with + | Nil -> [] + | One (k,v) -> [k,v] + | Two (k1,v1,k2,v2) -> [k1,v1; k2,v2] + | Cons (k, v, tail) -> (k,v) :: list_as_tree_ tail +and array_as_tree_ a = A.fold (fun acc t -> as_tree t :: acc) [] a + *) + +(* TODO: $R again? + let m = of_list CCList.( (501 -- 1000) @ (500 -- 1) |> map (fun i->i,i)) in + assert_equal ~printer:CCInt.to_string 1000 (length m); + assert_bool "check all get" + (Sequence.for_all (fun i -> i = get_exn i m) Sequence.(1 -- 1000)); + let m = Sequence.(501 -- 1000 |> fold (fun m i -> remove i m) m) in + assert_equal ~printer:CCInt.to_string 500 (length m); + assert_bool "check all get after remove" + (Sequence.for_all (fun i -> i = get_exn i m) Sequence.(1 -- 500)); + assert_bool "check all get after remove" + (Sequence.for_all (fun i -> None = get i m) Sequence.(501 -- 1000)); +*) diff --git a/src/data/CCFun_vec.mli b/src/data/CCFun_vec.mli new file mode 100644 index 00000000..ad2a3d88 --- /dev/null +++ b/src/data/CCFun_vec.mli @@ -0,0 +1,142 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 Functional Vectors} *) + +(** Tree with a large branching factor for logarithmic operations with + a low multiplicative factor. + + {b status: unstable} + + @since NEXT_RELEASE +*) + +type 'a sequence = ('a -> unit) -> unit +type 'a gen = unit -> 'a option +type 'a printer = Format.formatter -> 'a -> unit +type 'a ktree = unit -> [`Nil | `Node of 'a * 'a ktree list] + +(** {2 Transient Identifiers} *) +module Transient : sig + type t + (** Identifiers for transient modifications. A transient modification + is uniquely identified by a [Transient.t]. Once [Transient.freeze r] + is called, [r] cannot be used to modify the structure again. *) + + val create : unit -> t + (** Create a new, active ID. *) + + val equal : t -> t -> bool + (** Equality between IDs. *) + + val frozen : t -> bool + (** [frozen i] returns [true] if [freeze i] was called before. In this case, + the ID cannot be used for modifications again. *) + + val active : t -> bool + (** [active i] is [not (frozen i)]. *) + + val freeze : t -> unit + (** [freeze i] makes [i] unusable for new modifications. The values + created with [i] will now be immutable. *) + + val with_ : (t -> 'a) -> 'a + (** [with_ f] creates a transient ID [i], calls [f i], + freezes the ID [i] and returns the result of [f i]. *) + + exception Frozen + (** Raised when a frozen ID is used. *) +end + +(** {2 Signature} *) + +type 'a t + +val empty : 'a t + +val is_empty : _ t -> bool + +val return : 'a -> 'a t + +val length : _ t -> int + +val push : 'a -> 'a t -> 'a t +(** Add element at the end *) + +val get : int -> 'a t -> 'a option + +val get_exn : int -> 'a t -> 'a +(** @raise Not_found if key not present. *) + +val pop_exn : 'a t -> 'a * 'a t +(** Pop last element *) + +val iter : f:('a -> unit) -> 'a t -> unit + +val iteri : f:(int -> 'a -> unit) -> 'a t -> unit + +val fold : f:('b -> 'a -> 'b) -> x:'b -> 'a t -> 'b + +val foldi : f:('b -> int -> 'a -> 'b) -> x:'b -> 'a t -> 'b + +(* TODO + +val push_mut : id:Transient.t -> 'a -> 'a t -> 'a t +(** [add_mut ~id k v m] behaves like [add k v m], except it will mutate + in place whenever possible. Changes done with an [id] might affect all + versions of the structure obtained with the same [id] (but not + other versions). + @raise Transient.Frozen if [id] is frozen. *) + +val pop_mut : id:Transient.t -> 'a t -> 'a * 'a t +(** Same as {!remove}, but modifies in place whenever possible. + @raise Transient.Frozen if [id] is frozen. *) + +val append : 'a t -> 'a t -> 'a t + +val append_mut : id:Transient.t -> into:'a t -> 'a t -> 'a t + +val map : ('a -> 'b) -> 'a t -> 'b t + *) + +(** {6 Conversions} *) + +val to_list : 'a t -> 'a list + +val of_list : 'a list -> 'a t + +(* TODO + +val add_list : 'a t -> 'a list -> 'a t + +val add_list_mut : id:Transient.t -> 'a t -> 'a list -> 'a t +(** @raise Frozen if the ID is frozen. *) + +val add_seq : 'a t -> 'a sequence -> 'a t + +val add_seq_mut : id:Transient.t -> 'a t -> 'a sequence -> 'a t +(** @raise Frozen if the ID is frozen. *) + +val of_seq : 'a sequence -> 'a t + +val to_seq : 'a t -> 'a sequence + +val add_gen : 'a t -> 'a gen -> 'a t + +val add_gen_mut : id:Transient.t -> 'a t -> 'a gen -> 'a t +(** @raise Frozen if the ID is frozen. *) + +val of_gen : 'a gen -> 'a t + +val to_gen : 'a t -> 'a gen + +(** {6 IO} *) + +val pp : 'a printer -> 'a t printer + +val as_tree : 'a t -> [`L of int * 'a list | `N ] ktree +(** For debugging purpose: explore the structure of the tree, + with [`L (h,l)] being a leaf (with shared hash [h]) + and [`N] an inner node. *) + + *) + From deb266e1b393e76272a50c397725f80baeb65200 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 15 Feb 2018 00:27:04 -0600 Subject: [PATCH 06/60] add CCFun_vec to benchmarks --- benchs/run_benchs.ml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/benchs/run_benchs.ml b/benchs/run_benchs.ml index 2891b06f..362a2933 100644 --- a/benchs/run_benchs.ml +++ b/benchs/run_benchs.ml @@ -110,14 +110,18 @@ module L = struct let bench_nth ?(time=2) n = let l = CCList.(1 -- n) in let ral = CCRAL.of_list l in + let v = CCFun_vec.of_list l in let bench_list l () = - for i = 0 to n-1 do ignore (List.nth l i) done + for i = 0 to n-1 do Sys.opaque_identity (ignore (List.nth l i)) done and bench_ral l () = - for i = 0 to n-1 do ignore (CCRAL.get_exn l i) done + for i = 0 to n-1 do Sys.opaque_identity (ignore (CCRAL.get_exn l i)) done + and bench_funvec l () = + for i = 0 to n-1 do Sys.opaque_identity (ignore (CCFun_vec.get_exn i l)) done in B.throughputN time ~repeat [ "List.nth", bench_list l, () ; "RAL.get", bench_ral ral, () + ; "funvec.get", bench_funvec v, () ] (* MAIN *) From 74d3b0f29fb8df3c16a507069f1b5ffeaf80f29a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 15 Feb 2018 08:30:05 -0600 Subject: [PATCH 07/60] add BatVec to some benchmarks --- benchs/jbuild | 3 ++- benchs/run_benchs.ml | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/benchs/jbuild b/benchs/jbuild index 957f0ed2..c0c4c2f4 100644 --- a/benchs/jbuild +++ b/benchs/jbuild @@ -2,7 +2,8 @@ (executables ((names (run_benchs run_bench_hash)) (libraries (containers containers.data containers.iter - containers.thread benchmark gen sequence qcheck)) + containers.thread benchmark gen sequence qcheck + batteries)) (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -safe-string -color always)) (ocamlopt_flags (:standard -O3 -color always -unbox-closures -unbox-closures-factor 20)) diff --git a/benchs/run_benchs.ml b/benchs/run_benchs.ml index 362a2933..93e96782 100644 --- a/benchs/run_benchs.ml +++ b/benchs/run_benchs.ml @@ -111,17 +111,21 @@ module L = struct let l = CCList.(1 -- n) in let ral = CCRAL.of_list l in let v = CCFun_vec.of_list l in + let bv = BatVect.of_list l in let bench_list l () = for i = 0 to n-1 do Sys.opaque_identity (ignore (List.nth l i)) done and bench_ral l () = for i = 0 to n-1 do Sys.opaque_identity (ignore (CCRAL.get_exn l i)) done and bench_funvec l () = for i = 0 to n-1 do Sys.opaque_identity (ignore (CCFun_vec.get_exn i l)) done + and bench_batvec l () = + for i = 0 to n-1 do Sys.opaque_identity (ignore (BatVect.get l i)) done in B.throughputN time ~repeat [ "List.nth", bench_list l, () ; "RAL.get", bench_ral ral, () ; "funvec.get", bench_funvec v, () + ; "batvec.get", bench_batvec bv, () ] (* MAIN *) From bff146456069a8ecd2ce2c05896e79abdd0f5fc2 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 15 Feb 2018 08:30:16 -0600 Subject: [PATCH 08/60] use non empty lists as indexes in Fun_vec --- src/data/CCFun_vec.ml | 50 ++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/src/data/CCFun_vec.ml b/src/data/CCFun_vec.ml index b91370d4..220fce8d 100644 --- a/src/data/CCFun_vec.ml +++ b/src/data/CCFun_vec.ml @@ -156,17 +156,8 @@ let length {size;_} = size let return x = {leaves=A.return x; subs=A.empty; size=1} type idx_l = - | L1 of int - | L2 of int * int - | L3 of int * int * int - | L4 of int * int * int * int - | L_cons of int * idx_l - -let cons_idx x1 l = match l with - | L1 x2 -> L2 (x1,x2) - | L2 (x2,x3) -> L3 (x1,x2,x3) - | L3 (x2,x3,x4) -> L4 (x1,x2,x3,x4) - | L4 _ | L_cons _ -> L_cons (x1, l) + | I_one of int + | I_cons of int * idx_l (* split an index into a low and high parts *) let low_idx_ i = i land A.mask @@ -179,20 +170,17 @@ let combine_idx i j = (i lsl A.length_log) lor j let split_idx i : idx_l = let rec aux high low = if high = 0 then low - else if high < A.max_length then cons_idx (high-1) low - else aux (high_idx_ high) (cons_idx (low_idx_ high) low) + else if high < A.max_length then I_cons (high-1, low) + else aux (high_idx_ high) (I_cons (low_idx_ high, low)) in - aux (high_idx_ i) (L1 (low_idx_ i)) + aux (high_idx_ i) (I_one(low_idx_ i)) let get_ (i:int) (m:'a t) : 'a = let rec aux l m = match l with - | L1 x1 -> - assert (x1 < A.length m.leaves); - A.get m.leaves x1 - | L2 (x1,x2) -> aux (L1 x2) (A.get m.subs x1) - | L3 (x1,x2,x3) -> aux (L2 (x2,x3)) (A.get m.subs x1) - | L4 (x1,x2,x3,x4) -> aux (L3 (x2,x3,x4)) (A.get m.subs x1) - | L_cons (x1,x2) -> aux x2 (A.get m.subs x1) + | I_one x -> + assert (x < A.length m.leaves); + A.get m.leaves x + | I_cons (x, tl) -> aux tl (A.get m.subs x) in aux (split_idx i) m @@ -210,15 +198,12 @@ let get i v = let push_ (i:int) (x:'a) (m:'a t) : 'a t = let rec aux l m = match l with - | L1 x1 -> - assert (x1=A.length m.leaves); + | I_one i -> + assert (i=A.length m.leaves); assert (A.length m.leaves < A.max_length); assert (A.is_empty m.subs); {m with size=m.size+1; leaves=A.push x m.leaves} - | L2 (x1,x2) -> aux_replace_sub (L1 x2) m x1 - | L3 (x1,x2,x3) -> aux_replace_sub (L2 (x2,x3)) m x1 - | L4 (x1,x2,x3,x4) -> aux_replace_sub (L3 (x2,x3,x4)) m x1 - | L_cons (x1,x2) -> aux_replace_sub x2 m x1 + | I_cons (i,tl) -> aux_replace_sub tl m i and aux_replace_sub l m x = assert (x <= A.length m.subs); (* insert in subtree, possibly a new one *) @@ -234,14 +219,11 @@ let push x (v:_ t) : _ t = push_ v.size x v let pop_ i (m:'a t) : 'a * 'a t = let rec aux l m = match l with - | L1 x1 -> - assert (x1+1 = A.length m.leaves); (* last one *) - let x = A.get m.leaves x1 in + | I_one x -> + assert (x+1 = A.length m.leaves); (* last one *) + let x = A.get m.leaves x in x, {m with size=m.size-1; leaves=A.pop m.leaves} - | L2 (x1,x2) -> aux_remove_sub (L1 x2) m x1 - | L3 (x1,x2,x3) -> aux_remove_sub (L2 (x2,x3)) m x1 - | L4 (x1,x2,x3,x4) -> aux_remove_sub (L3 (x2,x3,x4)) m x1 - | L_cons (x1,x2) -> aux_remove_sub x2 m x1 + | I_cons (x,tl) -> aux_remove_sub tl m x and aux_remove_sub l m x = let sub = A.get m.subs x in let y, sub' = aux l sub in From 1640ee89f280b330d9594cfc97dda37c5ec76805 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 17 Feb 2018 10:16:08 -0600 Subject: [PATCH 09/60] add missing signatures of `CCArrayLabels` (closes #193) --- src/core/CCArrayLabels.mli | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/src/core/CCArrayLabels.mli b/src/core/CCArrayLabels.mli index 05c1895c..2cbe9330 100644 --- a/src/core/CCArrayLabels.mli +++ b/src/core/CCArrayLabels.mli @@ -27,6 +27,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 1.4 *) + val get : 'a t -> int -> 'a (** [get a n] returns the element number [n] of array [a]. The first element has number 0. @@ -63,6 +67,18 @@ val fold_while : f:('a -> 'b -> 'a * [`Stop | `Continue]) -> init:'a -> 'b t -> indicated by the accumulator. @since 0.8 *) +val fold_map : f:('acc -> 'a -> 'acc * 'b) -> init:'acc -> 'a t -> 'acc * 'b t +(** [fold_map f acc a] is a [fold_left]-like function, but it also maps the + array to another array. + @since NEXT_RELEASE *) + +val scan_left : f:('acc -> 'a -> 'acc) -> init:'acc -> 'a t -> 'acc t +(** [scan_left f acc a] returns the array + [ [|acc; f acc x0; f (f acc a.(0)) a.(1); …|] ]. + + @since NEXT_RELEASE *) + + val iter : f:('a -> unit) -> 'a t -> unit (** [iter f a] applies function [f] in turn to all the elements of [a]. It is equivalent to @@ -111,13 +127,24 @@ val sort_ranking : f:('a -> 'a -> int) -> 'a t -> int array [lookup_exn a.(i) (sorted a) = (sorted_ranking a).(i)]. @since 1.0 *) +val find_map : f:('a -> 'b option) -> 'a t -> 'b option +(** [find_map f a] returns [Some y] if there is an element [x] such + that [f x = Some y], else it returns [None]. + @since NEXT_RELEASE *) + val find : f:('a -> 'b option) -> 'a t -> 'b option (** [find f a] returns [Some y] if there is an element [x] such - that [f x = Some y], else it returns [None]. *) + that [f x = Some y], else it returns [None]. + @deprecated since NEXT_RELEASE *) + +val find_map_i : f:(int -> 'a -> 'b option) -> 'a t -> 'b option +(** Like {!find_map}, but also pass the index to the predicate function. + @since NEXT_RELEASE *) val findi : f:(int -> 'a -> 'b option) -> 'a t -> 'b option (** Like {!find}, but also pass the index to the predicate function. - @since 0.3.4 *) + @since 0.3.4 + @deprecated since NEXT_RELEASE *) val find_idx : f:('a -> bool) -> 'a t -> (int * 'a) option (** [find_idx p x] returns [Some (i,x)] where [x] is the [i]-th element of [l], From 580dc58979c1a00e72a7acda642d7cf27ca82502 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 17 Feb 2018 10:16:45 -0600 Subject: [PATCH 10/60] remove junk file --- src/core/_tags | 1 - 1 file changed, 1 deletion(-) delete mode 100644 src/core/_tags diff --git a/src/core/_tags b/src/core/_tags deleted file mode 100644 index 1ebb483c..00000000 --- a/src/core/_tags +++ /dev/null @@ -1 +0,0 @@ -: inline(20) From 62ba3c00afeac0b0c8c60ce7f9d45e1f87050403 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 17 Feb 2018 10:25:57 -0600 Subject: [PATCH 11/60] make `CCInt64` compatible with `Int64` (breaking!) (closes #192) conversion functions are total, even when the bit widths do not correspond. Returning options does not make sense in this context. --- src/core/CCInt64.ml | 12 +---------- src/core/CCInt64.mli | 48 +++++++++++++++++++++++--------------------- 2 files changed, 26 insertions(+), 34 deletions(-) diff --git a/src/core/CCInt64.ml b/src/core/CCInt64.ml index 9ac33506..025142c2 100644 --- a/src/core/CCInt64.ml +++ b/src/core/CCInt64.ml @@ -35,21 +35,11 @@ let hash x = Pervasives.abs (to_int x) (** {2 Conversion} *) let of_int_exn = of_int - -let of_int x = try Some (of_int_exn x) with Failure _ -> None - let of_nativeint_exn = of_nativeint - -let of_nativeint x = try Some (of_nativeint_exn x) with Failure _ -> None - let of_int32_exn = of_int32 - -let of_int32 x = try Some (of_int32_exn x) with Failure _ -> None - let of_float_exn = of_float -let of_float x = try Some (of_float_exn x) with Failure _ -> None - let of_string_exn = of_string let of_string x = try Some (of_string_exn x) with Failure _ -> None +let of_string_opt = of_string diff --git a/src/core/CCInt64.mli b/src/core/CCInt64.mli index b3805aa5..2f830c45 100644 --- a/src/core/CCInt64.mli +++ b/src/core/CCInt64.mli @@ -6,7 +6,7 @@ @since 0.13 *) -type t = int64 +include module type of struct include Int64 end val (+) : t -> t -> t (** Addition. *) @@ -89,14 +89,13 @@ val to_int : t -> int is taken modulo 2{^31}, i.e. the top 33 bits are lost during the conversion. *) -val of_int : int -> t option -(** Safe version of {!of_int_exn}. *) +val of_int : int -> t +(** Alias to {!Int64.of_int} + NOTE: used to return an option, but the function actually never fails *) val of_int_exn : int -> t (** Alias to {!Int64.of_int}. - Convert the given integer (type [int]) to a 64-bit integer - (type [int64]). - @raise Failure in case of failure. *) + @deprecated since NEXT_RELEASE *) val to_int32 : t -> int32 (** Convert the given 64-bit integer (type [int64]) to a @@ -104,14 +103,13 @@ val to_int32 : t -> int32 is taken modulo 2{^32}, i.e. the top 32 bits are lost during the conversion. *) -val of_int32 : int32 -> t option -(** Safe version of {!of_int32_exn}. *) +val of_int32 : int32 -> t +(** Alias to {!Int64.of_int32} + NOTE: use to return an option, but the function actually never fails. *) val of_int32_exn : int32 -> t (** Alias to {!Int64.of_int32} - Convert the given 32-bit integer (type [int32]) - to a 64-bit integer (type [int64]). - @raise Failure in case of failure. *) + @deprecated since NEXT_RELEASE *) val to_nativeint : t -> nativeint (** Convert the given 64-bit integer (type [int64]) to a @@ -119,28 +117,28 @@ val to_nativeint : t -> nativeint is taken modulo 2{^32}. On 64-bit platforms, the conversion is exact. *) -val of_nativeint : nativeint -> t option -(** Safe version of {!of_nativeint_exn}. *) +val of_nativeint : nativeint -> t +(** Alias to {!Int64.of_nativeint}. + NOTE: use to return an option, but the function actually never fails. *) val of_nativeint_exn : nativeint -> t -(** Alias to {!Int64.of_nativeint}. - Convert the given native integer (type [nativeint]) - to a 64-bit integer (type [int64]). - @raise Failure in case of failure. *) +(** Alias to {!Int64.of_nativeint} + @deprecated since NEXT_RELEASE *) val to_float : t -> float (** Convert the given 64-bit integer to a floating-point number. *) -val of_float : float -> t option -(** Safe version of {!of_float_exn}. *) - -val of_float_exn : float -> t +val of_float : float -> t (** Alias to {!Int64.of_float}. Convert the given floating-point number to a 64-bit integer, discarding the fractional part (truncate towards 0). The result of the conversion is undefined if, after truncation, the number is outside the range \[{!CCInt64.min_int}, {!CCInt64.max_int}\]. - @raise Failure in case of failure. *) + NOTE: used to return an option, but the function never fails *) + +val of_float_exn : float -> t +(** Alias to {!Int64.of_float}. + @deprecated since NEXT_RELEASE *) val to_string : t -> string (** Return the string representation of its argument, in decimal. *) @@ -148,10 +146,14 @@ val to_string : t -> string val of_string : string -> t option (** Safe version of {!of_string_exn}. *) +val of_string_opt : string -> t option +(** Alias to {!of_string} + @since NEXT_RELEASE *) + val of_string_exn : string -> t (** Alias to {!Int64.of_string}. Convert the given string to a 64-bit integer. - The string is read in decimal (by default, or if the string + The string is read in decimal (by default, or if the string begins with [0u]) or in hexadecimal, octal or binary if the string begins with [0x], [0o] or [0b] respectively. From dabb7de24a7751c7cc77ec14b66adeff072bb109 Mon Sep 17 00:00:00 2001 From: Christopher Zimmermann Date: Sat, 17 Feb 2018 19:14:06 +0100 Subject: [PATCH 12/60] Add infix operators to CCFloat --- AUTHORS.adoc | 1 + src/core/CCFloat.ml | 7 +++++++ src/core/CCFloat.mli | 15 +++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/AUTHORS.adoc b/AUTHORS.adoc index 93b85e2b..895cb10e 100644 --- a/AUTHORS.adoc +++ b/AUTHORS.adoc @@ -28,3 +28,4 @@ - Rand (@rand00) - Dave Aitken (@actionshrimp) - Etienne Millon (@emillon) +- Christopher Zimmermann (@madroach) diff --git a/src/core/CCFloat.ml b/src/core/CCFloat.ml index cb4062b9..0d6b53d0 100644 --- a/src/core/CCFloat.ml +++ b/src/core/CCFloat.ml @@ -16,6 +16,11 @@ module Infix = struct let (>) = Pervasives.(>) let (<=) = Pervasives.(<=) let (>=) = Pervasives.(>=) + let (~-) = Pervasives.(~-.) + let (+) = Pervasives.(+.) + let (-) = Pervasives.(-.) + let ( * ) = Pervasives.( *. ) + let (/) = Pervasives.(/.) end include Infix @@ -35,6 +40,8 @@ let is_nan x = (x : t) <> x let add = (+.) let sub = (-.) +let mul = ( *. ) +let div = (/.) let neg = (~-.) let abs = Pervasives.abs_float let scale = ( *. ) diff --git a/src/core/CCFloat.mli b/src/core/CCFloat.mli index 8b0e7241..77b3c46a 100644 --- a/src/core/CCFloat.mli +++ b/src/core/CCFloat.mli @@ -130,6 +130,21 @@ module Infix : sig val (>=) : t -> t -> bool (** @since 0.17 *) + + val (+) : t -> t -> t + (** Addition. @since NEXT_RELEASE *) + + val (-) : t -> t -> t + (** Subtraction. @since NEXT_RELEASE *) + + val (~-) : t -> t + (** Unary negation. @since NEXT_RELEASE *) + + val ( * ) : t -> t -> t + (** Multiplication. @since NEXT_RELEASE *) + + val (/) : t -> t -> t + (** Division. @since NEXT_RELEASE *) end include module type of Infix From 3c8869dd5b8fd326ed0132bee27023921fce74ec Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 19 Feb 2018 12:14:44 -0600 Subject: [PATCH 13/60] udpate readme to explain a bit more how to live with monomorphic ops --- README.adoc | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/README.adoc b/README.adoc index 1debf137..9fc081b5 100644 --- a/README.adoc +++ b/README.adoc @@ -65,6 +65,50 @@ Some of the modules have been moved to their own repository (e.g. `sequence`, However, during migration and until you use proper combinators for equality (`CCEqual`), comparison (`CCOrd`), and hashing (`CCHash`), you might want to add `open Pervasives` just after the `open Containers`. + See <> for more details. + +[[mono-ops]] +== Monomorphic operators: why, and how? + +=== Why shadow polymorphic operators by default? + +To quote @bluddy in #196: + +The main problem with polymorphic comparison is that many data structures will +give one result for structural comparison, and a different result for semantic +comparison. The classic example is comparing maps. If you have a list of maps +and try to use comparison to sort them, you'll get the wrong result: multiple +map structures can represent the same semantic mapping from key to value, and +comparing them in terms of structure is simply wrong. A far more pernicious bug +occurs with hashtables. Identical hashtables will seem to be identical for a +while, as before they've had a key clash, the outer array is likely to be the +same. Once you get a key clash though, you start getting lists inside the +arrays (or maps inside the arrays if you try to make a smarter hashtable) and +that will cause comparison errors ie. identical hashtables will be seen as +different or vice versa. + +Every time you use a polymorphic comparison where you're using a data type +where structural comparison != semantic comparison, it's a bug. And ever time +you use polymorphic comparison where the type of data being compared may vary +(e.g. it's an int now, but it may be a map later), you're planting a bug for +the future. + +=== Sometimes polymorphic operators still make sense! + +If you just want to use polymorphic operators, it's fine! You can access them +easily by using `Pervasives.(=)`, `Pervasives.max`, etc. + +When migrating a module, you can add `open Pervasives` on top of it to restore +the default behavior. It is, however, recommended to export an `equal` function +(and `compare`, and `hash`) for all the public types, even if their internal +definition is just the corresponding polymorphic operator. +This way, other modules can refer to `Foo.equal` and will not have to be +updated the day `Foo.equal` is no longer just polymorphic equality. +Another bonus is that `Hashtbl.Make(Foo)` or `Map.Make(Foo)` will just work™. + +=== Further discussions + +See issues #196, #197 == Change Log From ab378a98bbd5e18d898b6ef451e5921375b2f71f Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 19 Feb 2018 12:18:00 -0600 Subject: [PATCH 14/60] proper urls --- README.adoc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index 9fc081b5..cec38ef8 100644 --- a/README.adoc +++ b/README.adoc @@ -72,7 +72,7 @@ Some of the modules have been moved to their own repository (e.g. `sequence`, === Why shadow polymorphic operators by default? -To quote @bluddy in #196: +To quote @bluddy in https://github.com/c-cube/ocaml-containers/issues/196[#196]: The main problem with polymorphic comparison is that many data structures will give one result for structural comparison, and a different result for semantic @@ -108,7 +108,9 @@ Another bonus is that `Hashtbl.Make(Foo)` or `Map.Make(Foo)` will just work™. === Further discussions -See issues #196, #197 +See issues +https://github.com/c-cube/ocaml-containers/issues/196[#196], +https://github.com/c-cube/ocaml-containers/issues/197[#197] == Change Log From 22fce8e16f850de1c79ee8bae591cf5b520b5e48 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 19 Feb 2018 14:16:23 -0600 Subject: [PATCH 15/60] point to JST's blog post on poly compare --- README.adoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.adoc b/README.adoc index cec38ef8..47c4158f 100644 --- a/README.adoc +++ b/README.adoc @@ -93,6 +93,11 @@ you use polymorphic comparison where the type of data being compared may vary (e.g. it's an int now, but it may be a map later), you're planting a bug for the future. +See also: + +- https://blog.janestreet.com/the-perils-of-polymorphic-compare/ +- https://blog.janestreet.com/building-a-better-compare/ + === Sometimes polymorphic operators still make sense! If you just want to use polymorphic operators, it's fine! You can access them From fe1660852491a2726a6cfaaa39e1be730e312b4c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 21 Feb 2018 10:21:36 -0600 Subject: [PATCH 16/60] add `CCHash.combine{5,6}` --- src/core/CCHash.ml | 6 ++++++ src/core/CCHash.mli | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/src/core/CCHash.ml b/src/core/CCHash.ml index 8b0b9ac7..3958da4f 100644 --- a/src/core/CCHash.ml +++ b/src/core/CCHash.ml @@ -21,6 +21,12 @@ let combine3 a b c = let combine4 a b c d = combine2 (combine2 a b) (combine2 c d) +let combine5 a b c d e = + combine2 (combine2 a b) (combine2 (combine2 c d) e) + +let combine6 a b c d e f = + combine2 (combine2 a b) (combine2 (combine2 c d) (combine2 e f)) + (** {2 Combinators} *) let const h _ = h diff --git a/src/core/CCHash.mli b/src/core/CCHash.mli index 1a687ead..fe86eac8 100644 --- a/src/core/CCHash.mli +++ b/src/core/CCHash.mli @@ -64,6 +64,12 @@ val combine2 : hash -> hash -> hash val combine3 : hash -> hash -> hash -> hash val combine4 : hash -> hash -> hash -> hash -> hash +val combine5 : hash -> hash -> hash -> hash -> hash -> hash +(** @since NEXT_RELEASE *) + +val combine6 : hash -> hash -> hash -> hash -> hash -> hash -> hash +(** @since NEXT_RELEASE *) + (** {2 Iterators} *) type 'a sequence = ('a -> unit) -> unit From 5ebebf4fd76aa37052ed1b37dc14eea317b33c95 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 22 Feb 2018 14:18:00 -0600 Subject: [PATCH 17/60] test deps are required when we run tests --- containers.opam | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/containers.opam b/containers.opam index 0fc9832b..9f62d0c4 100644 --- a/containers.opam +++ b/containers.opam @@ -11,10 +11,6 @@ build-test: [ "jbuilder" "runtest" "-p" name "-j" jobs] depends: [ "jbuilder" {build & >= "1.0+beta12"} "result" -] -depopts: [ - "base-unix" - "base-threads" "qtest" { test } "qcheck" { test } "oUnit" { test } @@ -22,6 +18,10 @@ depopts: [ "gen" { test } "odoc" { doc } ] +depopts: [ + "base-unix" + "base-threads" +] conflicts: [ "sequence" { < "0.5" } ] From 0fb25fac2684fe05e1e8f2780465d04dcf0c8c79 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sat, 24 Feb 2018 18:38:13 -0600 Subject: [PATCH 18/60] note that ccfun_vec is really experimental --- src/data/CCFun_vec.ml | 4 ++++ src/data/CCFun_vec.mli | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/data/CCFun_vec.ml b/src/data/CCFun_vec.ml index 220fce8d..539b1cd4 100644 --- a/src/data/CCFun_vec.ml +++ b/src/data/CCFun_vec.ml @@ -40,6 +40,10 @@ module Transient = struct exception Frozen end +(* TODO: move transient from A.t to 'a t, as nodes can be owned by a transient, + not arrays. + Then do mutable push, and use it for append/filter/flatten/flat_map… *) + (* function array *) module A = struct type 'a t = { diff --git a/src/data/CCFun_vec.mli b/src/data/CCFun_vec.mli index ad2a3d88..04c804fe 100644 --- a/src/data/CCFun_vec.mli +++ b/src/data/CCFun_vec.mli @@ -5,7 +5,7 @@ (** Tree with a large branching factor for logarithmic operations with a low multiplicative factor. - {b status: unstable} + {b status: experimental. DO NOT USE (yet)} @since NEXT_RELEASE *) From 001d330bb9456334236e0ddc768e75410b82fa2c Mon Sep 17 00:00:00 2001 From: Jacques-Pascal Deplaix Date: Sun, 25 Feb 2018 09:51:31 +0000 Subject: [PATCH 19/60] Fix ounit package name --- containers.opam | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/containers.opam b/containers.opam index 9f62d0c4..4b6b83ef 100644 --- a/containers.opam +++ b/containers.opam @@ -13,7 +13,7 @@ depends: [ "result" "qtest" { test } "qcheck" { test } - "oUnit" { test } + "ounit" { test } "sequence" { test } "gen" { test } "odoc" { doc } From a0d0cf9d88e0857853606761557d2ea617055ecc Mon Sep 17 00:00:00 2001 From: Jacques-Pascal Deplaix Date: Thu, 22 Feb 2018 16:05:29 +0000 Subject: [PATCH 20/60] Enable support for Travis CI and Appveyor --- .travis.yml | 44 +++++++++++++------------------------------- appveyor.yml | 23 +++++++++++++++++++++++ 2 files changed, 36 insertions(+), 31 deletions(-) create mode 100644 appveyor.yml diff --git a/.travis.yml b/.travis.yml index ab69f2e0..57996d11 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,33 +1,15 @@ language: c +install: wget https://raw.githubusercontent.com/ocaml/ocaml-ci-scripts/master/.travis-docker.sh +script: bash -ex .travis-docker.sh +services: +- docker env: - - OCAML_VERSION=4.02.3 - - OCAML_VERSION=4.04.2 - - OCAML_VERSION=4.05.0 - - OCAML_VERSION=4.05.0+flambda - - OCAML_VERSION=4.06.0 -addons: - apt: - sources: - - avsm - packages: - - opam -# Caching may take a lot of space with so many ocaml versions -#cache: -# directories: -# - $HOME/.opam -before_install: - # Some opam boilerplate - - export OPAMYES=1 - - export OPAMVERBOSE=1 - - opam init - - opam switch ${OCAML_VERSION} - - eval `opam config env` -install: - # Install dependencies - - opam pin add --no-action containers . - - opam install jbuilder base-bytes result - - opam install --deps-only containers -script: - - make build - - opam install sequence qcheck qtest gen - - make test + global: + - PINS="containers:." + - DISTRO="ubuntu-16.04" + matrix: + - PACKAGE="containers" OCAML_VERSION="4.02.3" DEPOPTS="base-threads base-unix" + - PACKAGE="containers" OCAML_VERSION="4.03.0" DEPOPTS="base-threads base-unix" + - PACKAGE="containers" OCAML_VERSION="4.04.2" DEPOPTS="base-threads base-unix" + - PACKAGE="containers" OCAML_VERSION="4.05.0" DEPOPTS="base-threads base-unix" + - PACKAGE="containers" OCAML_VERSION="4.06.0" DEPOPTS="base-threads base-unix" diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..4bd43c36 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,23 @@ +platform: +- x86 +environment: + global: + FORK_USER: ocaml + FORK_BRANCH: master + CYG_ROOT: C:\cygwin64 + PINS: containers:. + matrix: + - OPAM_SWITCH: 4.02.3+mingw64c + PACKAGE: containers + - OPAM_SWITCH: 4.03.0+mingw64c + PACKAGE: containers + - OPAM_SWITCH: 4.04.2+mingw64c + PACKAGE: containers + - OPAM_SWITCH: 4.05.0+mingw64c + PACKAGE: containers + - OPAM_SWITCH: 4.06.0+mingw64c + PACKAGE: containers +install: +- ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/$env:FORK_USER/ocaml-ci-scripts/$env:FORK_BRANCH/appveyor-install.ps1")) +build_script: +- call %CYG_ROOT%\bin\bash.exe -l %APPVEYOR_BUILD_FOLDER%\appveyor-opam.sh From 5f0b6488453a9ea41a050f643a5f41d2f37b8ffd Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 26 Feb 2018 19:47:24 -0600 Subject: [PATCH 21/60] update printers names in containers.top (closes #201) --- src/top/containers_top.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/top/containers_top.ml b/src/top/containers_top.ml index 4eeca3ab..ba1f8502 100644 --- a/src/top/containers_top.ml +++ b/src/top/containers_top.ml @@ -17,11 +17,11 @@ let install_printers = List.iter install_printer let () = install_printers - [ "CCHashtbl.print" - ; "CCBV.print" - ; "CCDeque.print" - ; "CCFQueue.print" - ; "CCIntMap.print" - ; "CCPersistentArray.print" + [ "CCHashtbl.pp" + ; "CCBV.pp" + ; "CCDeque.pp" + ; "CCFQueue.pp" + ; "CCIntMap.pp" + ; "CCPersistentArray.pp" ; "CCSexp.pp" ] From a4dda4284c113719ef5258d681c2e27c7144c9d2 Mon Sep 17 00:00:00 2001 From: Jacques-Pascal Deplaix Date: Tue, 27 Feb 2018 12:59:29 +0000 Subject: [PATCH 22/60] Add an appveyor test for 32bits windows --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 4bd43c36..4524fbe9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,6 +17,9 @@ environment: PACKAGE: containers - OPAM_SWITCH: 4.06.0+mingw64c PACKAGE: containers + - CYG_ROOT: C:\cygwin32 + OPAM_SWITCH: 4.06.0+mingw32c + PACKAGE: containers install: - ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/$env:FORK_USER/ocaml-ci-scripts/$env:FORK_BRANCH/appveyor-install.ps1")) build_script: From f5f98c5e11bb5058db625e7feecd814e07624708 Mon Sep 17 00:00:00 2001 From: Jacques-Pascal Deplaix Date: Tue, 27 Feb 2018 14:37:48 +0000 Subject: [PATCH 23/60] Fix appveyor 32bits --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 4524fbe9..ee0f6e0a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,7 +17,7 @@ environment: PACKAGE: containers - OPAM_SWITCH: 4.06.0+mingw64c PACKAGE: containers - - CYG_ROOT: C:\cygwin32 + - CYG_ROOT: C:\cygwin OPAM_SWITCH: 4.06.0+mingw32c PACKAGE: containers install: From 87779968171a4d06c9ddac2119637144afabb83b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 27 Feb 2018 10:39:45 -0600 Subject: [PATCH 24/60] point to appveyor build in readme --- README.adoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.adoc b/README.adoc index 47c4158f..4ce39456 100644 --- a/README.adoc +++ b/README.adoc @@ -14,7 +14,8 @@ map/fold_right/append, and additional functions on lists). Alternatively, `open Containers` will bring enhanced versions of the standard modules into scope. -image::https://travis-ci.org/c-cube/ocaml-containers.svg?branch=master[alt="Build Status", link="https://travis-ci.org/c-cube/ocaml-containers"] +image::https://travis-ci.org/c-cube/ocaml-containers.svg?branch=master[alt="Build Status on Travis", link="https://travis-ci.org/c-cube/ocaml-containers"] +image::https://ci.appveyor.com/api/projects/status/tftx9q8auil4cv4c?svg=true[alt="Build Status on AppVeyor", link="https://ci.appveyor.com/project/c-cube/ocaml-containers"] toc::[] From d8c16ec95bc03d26d50a7bd3e1ed26cd08dad047 Mon Sep 17 00:00:00 2001 From: JPR Date: Tue, 27 Feb 2018 20:41:56 +0100 Subject: [PATCH 25/60] Adding CCInt32 module --- src/core/CCInt32.ml | 40 ++++++++++++++ src/core/CCInt32.mli | 125 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 src/core/CCInt32.ml create mode 100644 src/core/CCInt32.mli diff --git a/src/core/CCInt32.ml b/src/core/CCInt32.ml new file mode 100644 index 00000000..4aaf73d9 --- /dev/null +++ b/src/core/CCInt32.ml @@ -0,0 +1,40 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +include Int32 + +let (+) = add + +let (-) = sub + +let (~-) = neg + +let ( * ) = mul + +let (/) = div + +let (mod) = rem + +let (land) = logand + +let (lor) = logor + +let (lxor) = logxor + +let lnot = lognot + +let (lsl) = shift_left + +let (lsr) = shift_right_logical + +let (asr) = shift_right + +let equal (x:t) y = Pervasives.(=) x y + +let hash x = Pervasives.abs (to_int x) + +(** {2 Conversion} *) + +let of_string_exn = of_string + +let of_string x = try Some (of_string_exn x) with Failure _ -> None +let of_string_opt = of_string diff --git a/src/core/CCInt32.mli b/src/core/CCInt32.mli new file mode 100644 index 00000000..60b4fb3f --- /dev/null +++ b/src/core/CCInt32.mli @@ -0,0 +1,125 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 Int32} + + Helpers for 32-bit integers. + + This module provides operations on the type int32 of signed 32-bit integers. + Unlike the built-in int type, the type int32 is guaranteed to be exactly + 32-bit wide on all platforms. All arithmetic operations over int32 are taken + modulo 2{^32}. + + Performance notice: values of type int32 occupy more memory space than values + of type int, and arithmetic operations on int32 are generally slower than + those on int. Use int32 only when the application requires exact 32-bit arithmetic. + + @since NEXT_RELEASE *) + +include module type of struct include Int32 end + +val ( + ) : t -> t -> t +(** Addition. *) + +val ( - ) : t -> t -> t +(** Subtraction. *) + +val ( ~- ) : t -> t +(** Unary negation. *) + +val ( * ) : t -> t -> t +(** Multiplication. *) + +val ( / ) : t -> t -> t +(** Integer division. Raise [Division_by_zero] if the second + argument is zero. This division rounds the real quotient of + its arguments towards zero, as specified for {!Pervasives.(/)}. *) + +val ( mod ) : t -> t -> t +(** [x mod y ] is the integer remainder. + If [y <> zero], the result of [x mod y] satisfies the following property: + [x = ((x / y) * y) + (x mod y)]. + If [y = 0], [x mod y] raises [Division_by_zero]. *) + +val ( land ) : t -> t -> t +(** Bitwise logical and. *) + +val ( lor ) : t -> t -> t +(** Bitwise logical or. *) + +val ( lxor ) : t -> t -> t +(** Bitwise logical exclusive or. *) + +val lnot : t -> t +(** Bitwise logical negation. *) + +val ( lsl ) : t -> int -> t +(** [ x lsl y] shifts [x] to the left by [y] bits, filling in with zeroes. + The result is unspecified if [y < 0] or [y >= 32]. *) + +val ( lsr ) : t -> int -> t +(** [x lsr y] shifts [x] to the right by [y] bits. + This is a logical shift: zeroes are inserted in the vacated bits + regardless of the sign of [x]. + The result is unspecified if [y < 0] or [y >= 32]. *) + +val ( asr ) : t -> int -> t +(** [x asr y] shifts [x] to the right by [y] bits. + This is an arithmetic shift: the sign bit of [x] is replicated + and inserted in the vacated bits. + The result is unspecified if [y < 0] or [y >= 32]. *) + +val equal : t -> t -> bool +(** The equal function for 32-bit integers. + Like {!Pervasives.(=) x y)}. *) + +val hash : t -> int +(** Like {!Pervasives.abs (to_int x)}. *) + +(** {2 Conversion} *) + +val to_int : t -> int +(** Convert the given 32-bit integer (type [int32]) to an + integer (type [int]). On 32-bit platforms, the 32-bit integer + is taken modulo 2{^31}, i.e. the high-order bit is lost + during the conversion. On 64-bit platforms, the conversion is exact. *) + +val of_int : int -> t +(** Alias to {!Int32.of_int}. *) + +val to_float : t -> float +(** Convert the given 32-bit integer to a floating-point number. *) + +val of_float : float -> t +(** Alias to {!Int32.of_float}. + Convert the given floating-point number to a 32-bit integer, + discarding the fractional part (truncate towards 0). + The result of the conversion is undefined if, after truncation, the number + is outside the range \[{!CCInt32.min_int}, {!CCInt32.max_int}\]. *) + +val to_string : t -> string +(** Return the string representation of its argument, in signed decimal. *) + +val of_string_exn : string -> t +(** Alias to {!Int32.of_string}. + Convert the given string to a 32-bit integer. + The string is read in decimal (by default, or if the string + begins with [0u]) or in hexadecimal, octal or binary if the + string begins with [0x], [0o] or [0b] respectively. + + The [0u] prefix reads the input as an unsigned integer in the range + [[0, 2*CCInt32.max_int+1]]. If the input exceeds {!CCInt32.max_int} + it is converted to the signed integer + [CCInt32.min_int + input - CCInt32.max_int - 1]. + + The [_] (underscore) character can appear anywhere in the string + and is ignored. + Raise [Failure "Int32.of_string"] if the given string is not + a valid representation of an integer, or if the integer represented + exceeds the range of integers representable in type [int32]. *) + +val of_string : string -> t option +(** Safe version of {!of_string_exn}. + Like {!of_string_exn}, but return [None] instead of raising. *) + +val of_string_opt : string -> t option +(** Alias to {!of_string}. *) From 4a317e57c1768355916e7b9530386b88b296a0e7 Mon Sep 17 00:00:00 2001 From: JPR Date: Tue, 27 Feb 2018 21:15:01 +0100 Subject: [PATCH 26/60] Correction containers.ml file --- src/core/containers.ml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/containers.ml b/src/core/containers.ml index 6db033eb..7a1a1574 100644 --- a/src/core/containers.ml +++ b/src/core/containers.ml @@ -27,6 +27,7 @@ module Hashtbl = struct end module Heap = CCHeap module Int = CCInt +module Int32 = CCInt32 module Int64 = CCInt64 module IO = CCIO module List = CCList From 9f2ef2f461e9f9506e30cf5c56ee026640fcee18 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 27 Feb 2018 21:07:01 -0600 Subject: [PATCH 27/60] add `CCInt{32,64}.Infix` --- src/core/CCInt32.ml | 63 +++++++++++++++++++++++++------------------ src/core/CCInt32.mli | 26 ++++++++++++++++-- src/core/CCInt64.ml | 64 ++++++++++++++++++++++++++------------------ src/core/CCInt64.mli | 24 +++++++++++++++++ 4 files changed, 123 insertions(+), 54 deletions(-) diff --git a/src/core/CCInt32.ml b/src/core/CCInt32.ml index 4aaf73d9..74aaab63 100644 --- a/src/core/CCInt32.ml +++ b/src/core/CCInt32.ml @@ -2,34 +2,45 @@ include Int32 -let (+) = add - -let (-) = sub - -let (~-) = neg - -let ( * ) = mul - -let (/) = div - -let (mod) = rem - -let (land) = logand - -let (lor) = logor - -let (lxor) = logxor - -let lnot = lognot - -let (lsl) = shift_left - -let (lsr) = shift_right_logical - -let (asr) = shift_right - let equal (x:t) y = Pervasives.(=) x y +module Infix = struct + let (+) = add + + let (-) = sub + + let (~-) = neg + + let ( * ) = mul + + let (/) = div + + let (mod) = rem + + let (land) = logand + + let (lor) = logor + + let (lxor) = logxor + + let lnot = lognot + + let (lsl) = shift_left + + let (lsr) = shift_right_logical + + let (asr) = shift_right + + let (=) = equal + + let (<>) = Pervasives.(<>) + let (<) = Pervasives.(<) + let (<=) = Pervasives.(<=) + let (>) = Pervasives.(>) + let (>=) = Pervasives.(>=) +end +include Infix + let hash x = Pervasives.abs (to_int x) (** {2 Conversion} *) diff --git a/src/core/CCInt32.mli b/src/core/CCInt32.mli index 60b4fb3f..1386abf1 100644 --- a/src/core/CCInt32.mli +++ b/src/core/CCInt32.mli @@ -9,8 +9,8 @@ 32-bit wide on all platforms. All arithmetic operations over int32 are taken modulo 2{^32}. - Performance notice: values of type int32 occupy more memory space than values - of type int, and arithmetic operations on int32 are generally slower than + Performance notice: values of type int32 occupy more memory space than values + of type int, and arithmetic operations on int32 are generally slower than those on int. Use int32 only when the application requires exact 32-bit arithmetic. @since NEXT_RELEASE *) @@ -68,6 +68,28 @@ val ( asr ) : t -> int -> t and inserted in the vacated bits. The result is unspecified if [y < 0] or [y >= 32]. *) +module Infix : sig + val (+) : t -> t -> t + val (-) : t -> t -> t + val (~-) : t -> t + val ( * ) : t -> t -> t + val (/) : t -> t -> t + val (mod) : t -> t -> t + val (land) : t -> t -> t + val (lor) : t -> t -> t + val (lxor) : t -> t -> t + val lnot : t -> t + val (lsl) : t -> int -> t + val (lsr) : t -> int -> t + val (asr) : t -> int -> t + val (=) : t -> t -> bool + val (<>) : t -> t -> bool + val (>) : t -> t -> bool + val (>=) : t -> t -> bool + val (<=) : t -> t -> bool + val (<) : t -> t -> bool +end + val equal : t -> t -> bool (** The equal function for 32-bit integers. Like {!Pervasives.(=) x y)}. *) diff --git a/src/core/CCInt64.ml b/src/core/CCInt64.ml index 025142c2..bfaed6f9 100644 --- a/src/core/CCInt64.ml +++ b/src/core/CCInt64.ml @@ -2,34 +2,46 @@ include Int64 -let (+) = add - -let (-) = sub - -let (~-) = neg - -let ( * ) = mul - -let (/) = div - -let (mod) = rem - -let (land) = logand - -let (lor) = logor - -let (lxor) = logxor - -let lnot = lognot - -let (lsl) = shift_left - -let (lsr) = shift_right_logical - -let (asr) = shift_right - let equal (x:t) y = Pervasives.(=) x y +module Infix = struct + let (+) = add + + let (-) = sub + + let (~-) = neg + + let ( * ) = mul + + let (/) = div + + let (mod) = rem + + let (land) = logand + + let (lor) = logor + + let (lxor) = logxor + + let lnot = lognot + + let (lsl) = shift_left + + let (lsr) = shift_right_logical + + let (asr) = shift_right + + let (=) = equal + + let (<>) = Pervasives.(<>) + let (<) = Pervasives.(<) + let (<=) = Pervasives.(<=) + let (>) = Pervasives.(>) + let (>=) = Pervasives.(>=) +end + +include Infix + let hash x = Pervasives.abs (to_int x) (** {2 Conversion} *) diff --git a/src/core/CCInt64.mli b/src/core/CCInt64.mli index 2f830c45..5ab65c3f 100644 --- a/src/core/CCInt64.mli +++ b/src/core/CCInt64.mli @@ -66,6 +66,30 @@ val (asr) : t -> int -> t and inserted in the vacated bits. The result is unspecified if [y < 0] or [y >= 64]. *) +(** Infix operators + @since NEXT_RELEASE *) +module Infix : sig + val (+) : t -> t -> t + val (-) : t -> t -> t + val (~-) : t -> t + val ( * ) : t -> t -> t + val (/) : t -> t -> t + val (mod) : t -> t -> t + val (land) : t -> t -> t + val (lor) : t -> t -> t + val (lxor) : t -> t -> t + val lnot : t -> t + val (lsl) : t -> int -> t + val (lsr) : t -> int -> t + val (asr) : t -> int -> t + val (=) : t -> t -> bool + val (<>) : t -> t -> bool + val (>) : t -> t -> bool + val (>=) : t -> t -> bool + val (<=) : t -> t -> bool + val (<) : t -> t -> bool +end + val equal : t -> t -> bool (** The equal function for 64-bit integers. Like {!Pervasives.(=) x y)}. *) From da6d4a72fa92029fcb8a06299c590412e656298c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 27 Feb 2018 21:12:04 -0600 Subject: [PATCH 28/60] enrich `CCInt.Infix` to get a uniform interface with `CCInt{32,64}` --- src/core/CCInt.ml | 23 ++++++++--------------- src/core/CCInt.mli | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/core/CCInt.ml b/src/core/CCInt.ml index cc87f080..909833e4 100644 --- a/src/core/CCInt.ml +++ b/src/core/CCInt.ml @@ -1,9 +1,16 @@ (* This file is free software, part of containers. See file "license" for more details. *) +module Infix = struct + include Pervasives + let (--) = range + let (--^) = range' +end +include Infix + type t = int -let equal (a:int) b = a=b +let equal (a:int) b = Pervasives.(=) a b let compare a b = compare a b @@ -245,17 +252,3 @@ let range' i j yield = [5;4;3] (range' 5 2 |> Sequence.to_list) *) - -module Infix = struct - let (=) = (=) - let (<>) = (<>) - let (<) = (<) - let (>) = (>) - let (<=) = (<=) - let (>=) = (>=) - let (--) = range - let (--^) = range' -end -include Infix -let min = min -let max = max diff --git a/src/core/CCInt.mli b/src/core/CCInt.mli index 76295bcb..2d0389ac 100644 --- a/src/core/CCInt.mli +++ b/src/core/CCInt.mli @@ -115,6 +115,20 @@ module Infix : sig val (--^) : t -> t -> t sequence (** Alias to {!range'}. @since 1.2 *) + + val (+) : t -> t -> t (** @since NEXT_RELEASE *) + val (-) : t -> t -> t (** @since NEXT_RELEASE *) + val (~-) : t -> t (** @since NEXT_RELEASE *) + val ( * ) : t -> t -> t (** @since NEXT_RELEASE *) + val (/) : t -> t -> t (** @since NEXT_RELEASE *) + val (mod) : t -> t -> t (** @since NEXT_RELEASE *) + val (land) : t -> t -> t (** @since NEXT_RELEASE *) + val (lor) : t -> t -> t (** @since NEXT_RELEASE *) + val (lxor) : t -> t -> t (** @since NEXT_RELEASE *) + val lnot : t -> t (** @since NEXT_RELEASE *) + val (lsl) : t -> int -> t (** @since NEXT_RELEASE *) + val (lsr) : t -> int -> t (** @since NEXT_RELEASE *) + val (asr) : t -> int -> t (** @since NEXT_RELEASE *) end include module type of Infix From 98bb766de6d4bcdf14e6ad247b791bc42e65ceeb Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 27 Feb 2018 21:37:28 -0600 Subject: [PATCH 29/60] fix ccint --- src/core/CCInt.ml | 81 +++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/src/core/CCInt.ml b/src/core/CCInt.ml index 909833e4..f260c719 100644 --- a/src/core/CCInt.ml +++ b/src/core/CCInt.ml @@ -1,13 +1,6 @@ (* This file is free software, part of containers. See file "license" for more details. *) -module Infix = struct - include Pervasives - let (--) = range - let (--^) = range' -end -include Infix - type t = int let equal (a:int) b = Pervasives.(=) a b @@ -16,6 +9,46 @@ let compare a b = compare a b let hash i = i land max_int +let range i j yield = + let rec up i j yield = + if i=j then yield i + else ( + yield i; + up (i+1) j yield + ) + and down i j yield = + if i=j then yield i + else ( + yield i; + down (i-1) j yield + ) + in + if i<=j then up i j yield else down i j yield + +(*$= & ~printer:Q.Print.(list int) + [0;1;2;3;4;5] (range 0 5 |> Sequence.to_list) + [0] (range 0 0 |> Sequence.to_list) + [5;4;3;2] (range 5 2 |> Sequence.to_list) +*) + +let range' i j yield = + if i Sequence.to_list) + [0;1;2;3;4] (range' 0 5 |> Sequence.to_list) + [5;4;3] (range' 5 2 |> Sequence.to_list) +*) + +module Infix = struct + include Pervasives + let (--) = range + let (--^) = range' +end +include Infix + let sign i = if i < 0 then -1 else if i>0 then 1 @@ -218,37 +251,3 @@ let range_by ~step i j yield = (CCInt.range_by ~step:1 i j |> Sequence.to_list) \ (CCInt.range i j |> Sequence.to_list) ) *) - -let range i j yield = - let rec up i j yield = - if i=j then yield i - else ( - yield i; - up (i+1) j yield - ) - and down i j yield = - if i=j then yield i - else ( - yield i; - down (i-1) j yield - ) - in - if i<=j then up i j yield else down i j yield - -(*$= & ~printer:Q.Print.(list int) - [0;1;2;3;4;5] (range 0 5 |> Sequence.to_list) - [0] (range 0 0 |> Sequence.to_list) - [5;4;3;2] (range 5 2 |> Sequence.to_list) -*) - -let range' i j yield = - if i Sequence.to_list) - [0;1;2;3;4] (range' 0 5 |> Sequence.to_list) - [5;4;3] (range' 5 2 |> Sequence.to_list) -*) - From 8d3981d983f871ab735da3108fcdc24c58749f20 Mon Sep 17 00:00:00 2001 From: JPR Date: Wed, 28 Feb 2018 07:42:13 +0100 Subject: [PATCH 30/60] Adding CCNativeint --- src/core/CCNativeint.ml | 51 ++++++++++++++ src/core/CCNativeint.mli | 148 +++++++++++++++++++++++++++++++++++++++ src/core/containers.ml | 3 +- 3 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 src/core/CCNativeint.ml create mode 100644 src/core/CCNativeint.mli diff --git a/src/core/CCNativeint.ml b/src/core/CCNativeint.ml new file mode 100644 index 00000000..b9692c38 --- /dev/null +++ b/src/core/CCNativeint.ml @@ -0,0 +1,51 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +include Nativeint + +let equal (x:t) y = Pervasives.(=) x y + +module Infix = struct + let (+) = add + + let (-) = sub + + let (~-) = neg + + let ( * ) = mul + + let (/) = div + + let (mod) = rem + + let (land) = logand + + let (lor) = logor + + let (lxor) = logxor + + let lnot = lognot + + let (lsl) = shift_left + + let (lsr) = shift_right_logical + + let (asr) = shift_right + + let (=) = equal + + let (<>) = Pervasives.(<>) + let (<) = Pervasives.(<) + let (<=) = Pervasives.(<=) + let (>) = Pervasives.(>) + let (>=) = Pervasives.(>=) +end +include Infix + +let hash x = Pervasives.abs (to_int x) + +(** {2 Conversion} *) + +let of_string_exn = of_string + +let of_string x = try Some (of_string_exn x) with Failure _ -> None +let of_string_opt = of_string diff --git a/src/core/CCNativeint.mli b/src/core/CCNativeint.mli new file mode 100644 index 00000000..e1e72f8d --- /dev/null +++ b/src/core/CCNativeint.mli @@ -0,0 +1,148 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 Nativeint} + + Helpers for processor-native integers. + + This module provides operations on the type [nativeint] of signed 32-bit integers + (on 32-bit platforms) or signed 64-bit integers (on 64-bit platforms). + This integer type has exactly the same width as that of a pointer type in the C compiler. + All arithmetic operations over nativeint are taken modulo 2{^32} or 2{^64} depending + on the word size of the architecture. + + Performance notice: values of type [nativeint] occupy more memory space than values of type [int], + and arithmetic operations on [nativeint] are generally slower than those on [int]. + Use [nativeint] only when the application requires the extra bit of precision over the [int] type. + + @since NEXT_RELEASE *) + +include module type of struct include Nativeint end + +val ( + ) : t -> t -> t +(** Addition. *) + +val ( - ) : t -> t -> t +(** Subtraction. *) + +val ( ~- ) : t -> t +(** Unary negation. *) + +val ( * ) : t -> t -> t +(** Multiplication. *) + +val ( / ) : t -> t -> t +(** Integer division. Raise [Division_by_zero] if the second + argument is zero. This division rounds the real quotient of + its arguments towards zero, as specified for {!Pervasives.(/)}. *) + +val ( mod ) : t -> t -> t +(** [x mod y ] is the integer remainder. + If [y <> zero], the result of [x mod y] satisfies the following properties: + [zero <= x mod y < abs y] and + [x = ((x / y) * y) + (x mod y)]. + If [y = 0], [x mod y] raises [Division_by_zero]. *) + +val ( land ) : t -> t -> t +(** Bitwise logical and. *) + +val ( lor ) : t -> t -> t +(** Bitwise logical or. *) + +val ( lxor ) : t -> t -> t +(** Bitwise logical exclusive or. *) + +val lnot : t -> t +(** Bitwise logical negation. *) + +val ( lsl ) : t -> int -> t +(** [ x lsl y] shifts [x] to the left by [y] bits. + The result is unspecified if [y < 0] or [y >= bitsize], where [bitsize] is [32] on a 32-bit platform + and [64] on a 64-bit platform. *) + +val ( lsr ) : t -> int -> t +(** [x lsr y] shifts [x] to the right by [y] bits. + This is a logical shift: zeroes are inserted in the vacated bits + regardless of the sign of [x]. + The result is unspecified if [y < 0] or [y >= bitsize]. *) + +val ( asr ) : t -> int -> t +(** [x asr y] shifts [x] to the right by [y] bits. + This is an arithmetic shift: the sign bit of [x] is replicated + and inserted in the vacated bits. + The result is unspecified if [y < 0] or [y >= bitsize]. *) + +module Infix : sig + val (+) : t -> t -> t + val (-) : t -> t -> t + val (~-) : t -> t + val ( * ) : t -> t -> t + val (/) : t -> t -> t + val (mod) : t -> t -> t + val (land) : t -> t -> t + val (lor) : t -> t -> t + val (lxor) : t -> t -> t + val lnot : t -> t + val (lsl) : t -> int -> t + val (lsr) : t -> int -> t + val (asr) : t -> int -> t + val (=) : t -> t -> bool + val (<>) : t -> t -> bool + val (>) : t -> t -> bool + val (>=) : t -> t -> bool + val (<=) : t -> t -> bool + val (<) : t -> t -> bool +end + +val equal : t -> t -> bool +(** The equal function for native integers. + Like {!Pervasives.(=) x y)}. *) + +val hash : t -> int +(** Like {!Pervasives.abs (to_int x)}. *) + +(** {2 Conversion} *) + +val to_int : t -> int +(** Convert the given native integer (type [nativeint]) to an + integer (type [int]). The high-order bit is lost + during the conversion. *) + +val of_int : int -> t +(** Alias to {!Nativeint.of_int}. + Convert the given integer (type [int]) to a native integer (type [nativeint]). *) + +val to_float : t -> float +(** Convert the given native integer to a floating-point number. *) + +val of_float : float -> t +(** Alias to {!Nativeint.of_float}. + Convert the given floating-point number to a native integer, + discarding the fractional part (truncate towards 0). + The result of the conversion is undefined if, after truncation, the number + is outside the range \[{!CCNativeint.min_int}, {!CCNativeint.max_int}\]. *) + +val to_string : t -> string +(** Return the string representation of its argument, in decimal. *) + +val of_string_exn : string -> t +(** Alias to {!Nativeint.of_string}. + Convert the given string to a native integer. + The string is read in decimal (by default, or if the string + begins with [0u]) or in hexadecimal, octal or binary if the + string begins with [0x], [0o] or [0b] respectively. + + The [0u] prefix reads the input as an unsigned integer in the range + [[0, 2*CCNativeint.max_int+1]]. If the input exceeds {!CCNativeint.max_int} + it is converted to the signed integer + [CCInt64.min_int + input - CCNativeint.max_int - 1]. + + Raise [Failure "Nativeint.of_string"] if the given string is not + a valid representation of an integer, or if the integer represented + exceeds the range of integers representable in type [nativeint]. *) + +val of_string : string -> t option +(** Safe version of {!of_string_exn}. + Like {!of_string_exn}, but return [None] instead of raising. *) + +val of_string_opt : string -> t option +(** Alias to {!of_string}. *) diff --git a/src/core/containers.ml b/src/core/containers.ml index 7a1a1574..15945033 100644 --- a/src/core/containers.ml +++ b/src/core/containers.ml @@ -27,12 +27,13 @@ module Hashtbl = struct end module Heap = CCHeap module Int = CCInt -module Int32 = CCInt32 +module Int32 = CCInt32 module Int64 = CCInt64 module IO = CCIO module List = CCList module ListLabels = CCListLabels module Map = CCMap +module Nativeint = CCNativeint module Option = CCOpt module Ord = CCOrd module Pair = CCPair From 68ad3d74082c7a55c77b072915706874391c2648 Mon Sep 17 00:00:00 2001 From: Nicola Mometto Date: Tue, 6 Mar 2018 10:09:56 +0000 Subject: [PATCH 31/60] Add <|> to CCLazy_list --- src/iter/CCLazy_list.ml | 12 ++++++++++++ src/iter/CCLazy_list.mli | 5 +++++ 2 files changed, 17 insertions(+) diff --git a/src/iter/CCLazy_list.ml b/src/iter/CCLazy_list.ml index d4855bd9..3fc011ea 100644 --- a/src/iter/CCLazy_list.ml +++ b/src/iter/CCLazy_list.ml @@ -82,9 +82,21 @@ let rec flat_map ~f l = Lazy.force res ) +let default b a = + lazy ( + match a with + | lazy Nil -> Lazy.force b + | lazy a -> a + ) + +(*$= + [1] (default (return 1) empty |> to_list) +*) + module Infix = struct let (>|=) x f = map ~f x let (>>=) x f = flat_map ~f x + let (<|>) = default end include Infix diff --git a/src/iter/CCLazy_list.mli b/src/iter/CCLazy_list.mli index b6111661..06983522 100644 --- a/src/iter/CCLazy_list.mli +++ b/src/iter/CCLazy_list.mli @@ -45,9 +45,14 @@ val append : 'a t -> 'a t -> 'a t val flat_map : f:('a -> 'b t) -> 'a t -> 'b t (** Monadic flatten + map. *) +val default : 'a t -> 'a t -> 'a t +(** Choice operator. + @since NEXT_RELEASE *) + module Infix : sig val (>|=) : 'a t -> ('a -> 'b) -> 'b t val (>>=) : 'a t -> ('a -> 'b t) -> 'b t + val (<|>) : 'a t -> 'a t -> 'a t end include module type of Infix From 8f4c1a24b7a83bb96bd18072b2fa20b544f56706 Mon Sep 17 00:00:00 2001 From: Nicola Mometto Date: Tue, 6 Mar 2018 15:37:57 +0000 Subject: [PATCH 32/60] default uses labelled argument --- src/iter/CCLazy_list.ml | 10 +++++----- src/iter/CCLazy_list.mli | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/iter/CCLazy_list.ml b/src/iter/CCLazy_list.ml index 3fc011ea..40a416d9 100644 --- a/src/iter/CCLazy_list.ml +++ b/src/iter/CCLazy_list.ml @@ -82,11 +82,11 @@ let rec flat_map ~f l = Lazy.force res ) -let default b a = +let default ~default l = lazy ( - match a with - | lazy Nil -> Lazy.force b - | lazy a -> a + match l with + | lazy Nil -> Lazy.force default + | lazy l -> l ) (*$= @@ -96,7 +96,7 @@ let default b a = module Infix = struct let (>|=) x f = map ~f x let (>>=) x f = flat_map ~f x - let (<|>) = default + let (<|>) a b = default ~default:b a end include Infix diff --git a/src/iter/CCLazy_list.mli b/src/iter/CCLazy_list.mli index 06983522..5f850518 100644 --- a/src/iter/CCLazy_list.mli +++ b/src/iter/CCLazy_list.mli @@ -45,7 +45,7 @@ val append : 'a t -> 'a t -> 'a t val flat_map : f:('a -> 'b t) -> 'a t -> 'b t (** Monadic flatten + map. *) -val default : 'a t -> 'a t -> 'a t +val default : default:'a t -> 'a t -> 'a t (** Choice operator. @since NEXT_RELEASE *) From 524658fb0f2debf94cc0024515fbb7476ccca7e9 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Mar 2018 10:15:33 -0600 Subject: [PATCH 33/60] add some doc --- src/iter/CCLazy_list.mli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/iter/CCLazy_list.mli b/src/iter/CCLazy_list.mli index 5f850518..bada7437 100644 --- a/src/iter/CCLazy_list.mli +++ b/src/iter/CCLazy_list.mli @@ -52,7 +52,7 @@ val default : default:'a t -> 'a t -> 'a t module Infix : sig val (>|=) : 'a t -> ('a -> 'b) -> 'b t val (>>=) : 'a t -> ('a -> 'b t) -> 'b t - val (<|>) : 'a t -> 'a t -> 'a t + val (<|>) : 'a t -> 'a t -> 'a t (** Alias to {!default}. @since NEXT_RELEASE *) end include module type of Infix From 30fca7ae9ee7dfefc08954c77ae42986cf623acd Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Mar 2018 22:15:28 -0600 Subject: [PATCH 34/60] add `CCUtf8_string` with basic encoding and decoding functionalities --- containers.opam | 2 + qtest/jbuild | 2 +- src/core/CCUtf8_string.ml | 288 +++++++++++++++++++++++++++++++++++++ src/core/CCUtf8_string.mli | 75 ++++++++++ src/core/containers.ml | 1 + src/core/jbuild | 2 +- 6 files changed, 368 insertions(+), 2 deletions(-) create mode 100644 src/core/CCUtf8_string.ml create mode 100644 src/core/CCUtf8_string.mli diff --git a/containers.opam b/containers.opam index 4b6b83ef..612c6187 100644 --- a/containers.opam +++ b/containers.opam @@ -11,11 +11,13 @@ build-test: [ "jbuilder" "runtest" "-p" name "-j" jobs] depends: [ "jbuilder" {build & >= "1.0+beta12"} "result" + "uchar" "qtest" { test } "qcheck" { test } "ounit" { test } "sequence" { test } "gen" { test } + "uutf" { test } "odoc" { doc } ] depopts: [ diff --git a/qtest/jbuild b/qtest/jbuild index 8f48e686..05351c29 100644 --- a/qtest/jbuild +++ b/qtest/jbuild @@ -18,7 +18,7 @@ (modules (run_qtest)) (libraries (sequence gen qcheck containers containers.unix containers.data containers.thread containers.iter - containers.sexp)) + containers.sexp uutf)) )) (alias diff --git a/src/core/CCUtf8_string.ml b/src/core/CCUtf8_string.ml new file mode 100644 index 00000000..b21524ef --- /dev/null +++ b/src/core/CCUtf8_string.ml @@ -0,0 +1,288 @@ + +(** {1 UTF8 strings} *) + +(** Ref {{: https://en.wikipedia.org/wiki/UTF-8} Wikipedia} + + We only deal with UTF8 strings as they naturally map to OCaml bytestrings *) + +type uchar = Uchar.t +type 'a gen = unit -> 'a option +type 'a sequence = ('a -> unit) -> unit + +type t = string + +let to_string x = x + +let pp = Format.pp_print_string +let equal = String.equal +let compare = String.compare +let hash : t -> int = Hashtbl.hash + +(** State for decoding *) +module Dec = struct + type t = { + s: string; + len: int; (* max offset *) + mutable i: int; (* offset *) + } + + let make ?(idx=0) (s:string) : t = + { s=s; i=idx; len=String.length s; } +end + +exception Malformed of string * int +(** Malformed string at given offset *) + +(* decode next char. Mutate state, calls [yield c] if a char [c] is + read, [stop ()] otherwise. + @raise Malformed if an invalid substring is met *) +let next_ (type a) (st : Dec.t) ~(yield:uchar -> a) ~(stop:unit -> a) () : a = + let open Dec in + (* read a multi-byte character. + @param acc the accumulator (containing the first byte of the char) + @param n_bytes number of bytes to read (i.e. [width char - 1]) *) + let read_multi n_bytes acc = + (* inner loop j = 1..jmax *) + let rec aux j acc = + let c = Char.code st.s.[ st.i + j] in + (* check that c is in 0b10xxxxxx *) + if c lsr 6 <> 0b10 then raise (Malformed (st.s,st.i)); + (* except for first, each char gives 6 bits *) + let next = (acc lsl 6) lor (c land 0b111111) in + if j = n_bytes then ( + (* done reading the codepoint *) + if Uchar.is_valid next then ( + st.i <- st.i + j + 1; (* +1 for first char *) + yield (Uchar.unsafe_of_int next) + ) else ( + raise (Malformed (st.s,st.i)) + ) + ) else ( + aux (j+1) next + ) + in + assert (n_bytes >= 1); + (* is the string long enough to contain the whole codepoint? *) + if st.i + n_bytes < st.len then ( + aux 1 acc (* start with j=1, first char is already proccessed! *) + ) else ( + (* char is truncated *) + raise (Malformed (st.s,st.i)) + ) + in + if st.i >= st.len then ( + stop () + ) else ( + let c = st.s.[ st.i ] in + match c with + | '\000' .. '\127' -> + st.i <- 1 + st.i; + yield (Uchar.of_int @@ Char.code c) (* 0xxxxxxx *) + | '\192' .. '\223' -> read_multi 1 ((Char.code c) land 0b11111) (* 110yyyyy *) + | '\224' .. '\239' -> read_multi 2 ((Char.code c) land 0b1111) (* 1110zzzz *) + | '\240' .. '\247' -> read_multi 3 ((Char.code c) land 0b111) (* 11110uuu *) + | '\128' .. '\191' + | '\248' .. '\255' -> + raise (Malformed (st.s,st.i)) + ) + +let to_gen ?(idx=0) str : uchar gen = + let st = Dec.make ~idx str in + fun () -> + next_ st + ~yield:(fun c -> Some c) + ~stop:(fun () -> None) + () + +exception Stop + +let to_seq ?(idx=0) s : uchar sequence = + fun yield -> + let st = Dec.make ~idx s in + try + while true do + next_ st ~yield + ~stop:(fun () -> raise Stop) + () + done + with Stop -> () + +let iter ?idx f s = to_seq ?idx s f + +let fold ?idx f acc s = + let st = Dec.make ?idx s in + let rec aux acc = + next_ st + ~yield:(fun x -> + let acc = f acc x in + aux acc) + ~stop:(fun () -> acc) + () + in + aux acc + +let to_list ?(idx=0) s : uchar list = + fold ~idx (fun acc x -> x :: acc) [] s |> List.rev + +(* Convert a code point (int) into a string; + There are various equally trivial versions of this around. +*) + +let code_to_string buf (c:uchar) : unit = + let c = Uchar.to_int c in + let mask = 0b111111 in + assert (Uchar.is_valid c); + if c <= 0x7f then ( + Buffer.add_char buf (Char.unsafe_chr c) + ) else if c <= 0x7ff then ( + Buffer.add_char buf (Char.unsafe_chr (0xc0 lor (c lsr 6))); + Buffer.add_char buf (Char.unsafe_chr (0x80 lor (c land mask))); + ) else if c <= 0xffff then ( + Buffer.add_char buf (Char.unsafe_chr (0xe0 lor (c lsr 12))); + Buffer.add_char buf (Char.unsafe_chr (0x80 lor ((c lsr 6) land mask))); + Buffer.add_char buf (Char.unsafe_chr (0x80 lor (c land mask))); + ) else if c <= 0x1fffff then ( + Buffer.add_char buf (Char.unsafe_chr (0xf0 lor (c lsr 18))); + Buffer.add_char buf (Char.unsafe_chr (0x80 lor ((c lsr 12) land mask))); + Buffer.add_char buf (Char.unsafe_chr (0x80 lor ((c lsr 6) land mask))); + Buffer.add_char buf (Char.unsafe_chr (0x80 lor (c land mask))); + ) else ( + Buffer.add_char buf (Char.unsafe_chr (0xf8 lor (c lsr 24))); + Buffer.add_char buf (Char.unsafe_chr (0x80 lor ((c lsr 18) land mask))); + Buffer.add_char buf (Char.unsafe_chr (0x80 lor ((c lsr 12) land mask))); + Buffer.add_char buf (Char.unsafe_chr (0x80 lor ((c lsr 6) land mask))); + Buffer.add_char buf (Char.unsafe_chr (0x80 lor (c land mask))); + ) + +let of_gen g : t = + let buf = Buffer.create 32 in + let rec aux () = match g() with + | None -> Buffer.contents buf + | Some c -> code_to_string buf c; aux () + in + aux () + +let of_seq seq : t = + let buf = Buffer.create 32 in + seq (code_to_string buf); + Buffer.contents buf + +let of_list l : t = + let buf = Buffer.create 32 in + List.iter (code_to_string buf) l; + Buffer.contents buf + +let unsafe_of_string s = s + +let is_valid (s:string) : bool = + try + let st = Dec.make s in + while true do + next_ st + ~yield:(fun _ -> ()) + ~stop:(fun () -> raise Stop) + () + done; + assert false + with + | Malformed _ -> false + | Stop -> true + +let of_string_exn s = + if is_valid s then s + else invalid_arg "CCUtf8_string.of_string_exn" + +let of_string s = if is_valid s then Some s else None + +(*$inject + + let printer s = String.escaped (to_string s) + let pp_uchar (c:Uchar.t) = Printf.sprintf "0x%x" (Uchar.to_int c) + +let uutf_is_valid s = + try + Uutf.String.fold_utf_8 + (fun () _ -> function + | `Malformed _ -> raise Exit + | `Uchar _ -> ()) + () s; + true + with Exit -> + false + +let uutf_to_seq s f = + Uutf.String.fold_utf_8 + (fun () _ -> function + | `Malformed _ -> f (Uchar.of_int 0xfffd) + | `Uchar c -> f c) + () s +*) + +(*$R + let s = of_string_exn "このため、" in + let s' = to_seq s |> of_seq in + assert_equal ~cmp:equal ~printer s s' +*) + +(*$QR + Q.string (fun s -> + Q.assume (CCString.for_all (fun c -> Char.code c < 128) s); + is_valid s) +*) + +(*$QR + Q.string (fun s -> + Q.assume (CCString.for_all (fun c -> Char.code c < 128) s); + s = (of_string_exn s |> to_seq |> of_seq |> to_string) + ) +*) + +(*$QR + Q.string (fun s -> + Q.assume (CCString.for_all (fun c -> Char.code c < 128) s); + String.length s = List.length (of_string_exn s |> to_list) + ) +*) + +(*$QR + Q.string (fun s -> + Q.assume (is_valid s); + let s = of_string_exn s in + let s2 = s |> to_seq |> of_seq in + if s=s2 then true + else Q.Test.fail_reportf "s=%S, s2=%S" (to_string s)(to_string s2) + ) +*) + +(*$QR + Q.string (fun s -> + Q.assume (is_valid s); + let s = of_string_exn s in + let s2 = s |> to_gen |> of_gen in + if s=s2 then true + else Q.Test.fail_reportf "s=%S, s2=%S" (to_string s)(to_string s2) + ) +*) + +(* compare with uutf *) + +(*$QR + Q.string (fun s -> + let v1 = is_valid s in + let v2 = uutf_is_valid s in + if v1=v2 then true + else Q.Test.fail_reportf "s:%S, valid: %B, uutf_valid: %B" s v1 v2 + ) +*) + +(*$QR + Q.string (fun s -> + Q.assume (is_valid s && uutf_is_valid s); + let pp s = Q.Print.(list pp_uchar) s in + let l_uutf = uutf_to_seq s |> Sequence.to_list in + let l_co = of_string_exn s |> to_seq |> Sequence.to_list in + if l_uutf = l_co then true + else Q.Test.fail_reportf "uutf: '%s', containers: '%s', is_valid %B, uutf_is_valid %B" + (pp l_uutf) (pp l_co) (is_valid s) (uutf_is_valid s) + ) + *) diff --git a/src/core/CCUtf8_string.mli b/src/core/CCUtf8_string.mli new file mode 100644 index 00000000..64843c18 --- /dev/null +++ b/src/core/CCUtf8_string.mli @@ -0,0 +1,75 @@ + +(** {1 Unicode String, in UTF8} *) + +(** A unicode string represented by a utf8 bytestring. This representation + is convenient for manipulating normal OCaml strings that are encoded + in UTF8. + + We perform only basic decoding and encoding between codepoints and + bytestrings. + For more elaborate operations, + please use the excellent {{: http://erratique.ch/software/uutf} Uutf}. + + @since NEXT_RELEASE + + {b status}: experimental +*) + + +type uchar = Uchar.t +type 'a gen = unit -> 'a option +type 'a sequence = ('a -> unit) -> unit + +type t = private string +(** A UTF8 string *) + +val equal : t -> t -> bool + +val hash : t -> int + +val compare : t -> t -> int + +val pp : Format.formatter -> t -> unit + +val to_string : t -> string +(** Identity *) + +exception Malformed of string * int +(** Malformed string at given offset *) + +val to_gen : ?idx:int -> t -> uchar gen +(** Generator of unicode codepoints. + @param idx offset where to start the decoding *) + +val to_seq : ?idx:int -> t -> uchar sequence +(** Sequence of unicode codepoints. + @param idx offset where to start the decoding *) + +val to_list : ?idx:int -> t -> uchar list +(** List of unicode codepoints. + @param idx offset where to start the decoding *) + +val fold : ?idx:int -> ('a -> uchar -> 'a) -> 'a -> t -> 'a + +val iter : ?idx:int -> (uchar -> unit) -> t -> unit + +val of_seq : uchar sequence -> t + +val of_gen : uchar gen -> t + +val of_list : uchar list -> t + +val of_string_exn : string -> t +(** Validate string by checking it is valid UTF8 + @raise Invalid_argument if the string is not valid UTF8 *) + +val of_string : string -> t option +(** Safe version of {!of_string_exn} *) + +val is_valid : string -> bool +(** Valid UTF8? *) + +val unsafe_of_string : string -> t +(** Conversion from a string without validating. + Upon iteration, if an invalid substring is met, Malformed will be raised *) + diff --git a/src/core/containers.ml b/src/core/containers.ml index 15945033..b12a4a2c 100644 --- a/src/core/containers.ml +++ b/src/core/containers.ml @@ -45,5 +45,6 @@ module Set = CCSet module String = CCString module Vector = CCVector module Monomorphic = CCMonomorphic +module Utf8_string = CCUtf8_string include Monomorphic diff --git a/src/core/jbuild b/src/core/jbuild index 172c8daf..2c848845 100644 --- a/src/core/jbuild +++ b/src/core/jbuild @@ -5,5 +5,5 @@ (wrapped false) (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -safe-string -nolabels -open CCMonomorphic)) (ocamlopt_flags (:standard (:include ../flambda.flags))) - (libraries (bytes result containers.monomorphic)) + (libraries (bytes result uchar containers.monomorphic)) )) From 6b5735a3188d034452cace1e5fcd16cfca3ba36b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Mar 2018 22:27:52 -0600 Subject: [PATCH 35/60] fix compat with 4.02 --- src/core/CCUtf8_string.ml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/core/CCUtf8_string.ml b/src/core/CCUtf8_string.ml index b21524ef..9c647cca 100644 --- a/src/core/CCUtf8_string.ml +++ b/src/core/CCUtf8_string.ml @@ -9,15 +9,14 @@ type uchar = Uchar.t type 'a gen = unit -> 'a option type 'a sequence = ('a -> unit) -> unit -type t = string +let equal (a:string) b = Pervasives.(=) a b +let hash : string -> int = Hashtbl.hash +let pp = Format.pp_print_string + +include String let to_string x = x -let pp = Format.pp_print_string -let equal = String.equal -let compare = String.compare -let hash : t -> int = Hashtbl.hash - (** State for decoding *) module Dec = struct type t = { From 640ab72bb20243ae28e7b4a0db79e25bff41b04c Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Mar 2018 22:28:11 -0600 Subject: [PATCH 36/60] bugfix in `CCVector.slice_seq` --- src/core/CCVector.ml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/core/CCVector.ml b/src/core/CCVector.ml index 32d4bb33..fcb86cc1 100644 --- a/src/core/CCVector.ml +++ b/src/core/CCVector.ml @@ -653,7 +653,7 @@ let to_seq_rev v k = let slice_seq v start len = assert (start >= 0 && len >= 0); fun k -> - assert (start+len < v.size); + assert (start+len <= v.size); for i = start to start+len-1 do let x = Array.unsafe_get v.vec i in k x @@ -661,6 +661,8 @@ let slice_seq v start len = (*$T slice_seq (of_list [0;1;2;3;4]) 1 3 |> CCList.of_seq = [1;2;3] + slice_seq (of_list [0;1;2;3;4]) 1 4 |> CCList.of_seq = [1;2;3;4] + slice_seq (of_list [0;1;2;3;4]) 0 5 |> CCList.of_seq = [0;1;2;3;4] *) let slice v = (v.vec, 0, v.size) From be76d6bf918351ee69bd7031c32f35423c5ca3ad Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Mar 2018 23:07:59 -0600 Subject: [PATCH 37/60] fixes in utf8_string: remove some forbidden cases --- src/core/CCUtf8_string.ml | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/core/CCUtf8_string.ml b/src/core/CCUtf8_string.ml index 9c647cca..c3b82db0 100644 --- a/src/core/CCUtf8_string.ml +++ b/src/core/CCUtf8_string.ml @@ -73,15 +73,17 @@ let next_ (type a) (st : Dec.t) ~(yield:uchar -> a) ~(stop:unit -> a) () : a = stop () ) else ( let c = st.s.[ st.i ] in + (* find leading byte, and detect some impossible cases + according to https://en.wikipedia.org/wiki/Utf8#Codepage_layout *) match c with | '\000' .. '\127' -> st.i <- 1 + st.i; yield (Uchar.of_int @@ Char.code c) (* 0xxxxxxx *) - | '\192' .. '\223' -> read_multi 1 ((Char.code c) land 0b11111) (* 110yyyyy *) + | '\194' .. '\223' -> read_multi 1 ((Char.code c) land 0b11111) (* 110yyyyy *) | '\224' .. '\239' -> read_multi 2 ((Char.code c) land 0b1111) (* 1110zzzz *) - | '\240' .. '\247' -> read_multi 3 ((Char.code c) land 0b111) (* 11110uuu *) - | '\128' .. '\191' - | '\248' .. '\255' -> + | '\240' .. '\244' -> read_multi 3 ((Char.code c) land 0b111) (* 11110uuu *) + | '\128' .. '\193' (* 192,193 are forbidden *) + | '\245' .. '\255' -> raise (Malformed (st.s,st.i)) ) @@ -229,21 +231,26 @@ let uutf_to_seq s f = is_valid s) *) -(*$QR +(*$QR & ~long_factor:10 Q.string (fun s -> Q.assume (CCString.for_all (fun c -> Char.code c < 128) s); s = (of_string_exn s |> to_seq |> of_seq |> to_string) ) *) -(*$QR +(*$QR & ~long_factor:10 Q.string (fun s -> Q.assume (CCString.for_all (fun c -> Char.code c < 128) s); String.length s = List.length (of_string_exn s |> to_list) ) *) -(*$QR +(*$T + not (is_valid "\192\181") + not (is_valid "\193\143") +*) + +(*$QR & ~long_factor:40 Q.string (fun s -> Q.assume (is_valid s); let s = of_string_exn s in @@ -253,7 +260,7 @@ let uutf_to_seq s f = ) *) -(*$QR +(*$QR & ~long_factor:40 Q.string (fun s -> Q.assume (is_valid s); let s = of_string_exn s in @@ -265,7 +272,7 @@ let uutf_to_seq s f = (* compare with uutf *) -(*$QR +(*$QR & ~long_factor:40 ~count:10_000 Q.string (fun s -> let v1 = is_valid s in let v2 = uutf_is_valid s in @@ -274,7 +281,7 @@ let uutf_to_seq s f = ) *) -(*$QR +(*$QR & ~long_factor:40 ~count:10_000 Q.string (fun s -> Q.assume (is_valid s && uutf_is_valid s); let pp s = Q.Print.(list pp_uchar) s in From fe88bafe77be21a8fc7d1380d75e5d77ec480ee3 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Mar 2018 23:08:02 -0600 Subject: [PATCH 38/60] simplify `CCFun_vec` by removing transients, for now --- src/data/CCFun_vec.ml | 55 +++++++++++++++++------------------------- src/data/CCFun_vec.mli | 2 ++ 2 files changed, 24 insertions(+), 33 deletions(-) diff --git a/src/data/CCFun_vec.ml b/src/data/CCFun_vec.ml index 539b1cd4..b16fdb9b 100644 --- a/src/data/CCFun_vec.ml +++ b/src/data/CCFun_vec.ml @@ -18,6 +18,7 @@ type 'a gen = unit -> 'a option type 'a printer = Format.formatter -> 'a -> unit type 'a ktree = unit -> [`Nil | `Node of 'a * 'a ktree list] +(* TODO (** {2 Transient IDs} *) module Transient = struct type state = { mutable frozen: bool } @@ -39,17 +40,11 @@ module Transient = struct raise e exception Frozen end - -(* TODO: move transient from A.t to 'a t, as nodes can be owned by a transient, - not arrays. - Then do mutable push, and use it for append/filter/flatten/flat_map… *) + *) (* function array *) module A = struct - type 'a t = { - arr: 'a array; - id: Transient.t; - } + type 'a t = 'a array let length_log = 5 let max_length = 32 @@ -57,35 +52,33 @@ module A = struct let () = assert (max_length = 1 lsl length_log) - let length a = Array.length a.arr + let length = Array.length + let iteri = Array.iteri + let fold = Array.fold_left - let create ~id = { arr= [| |]; id; } + let create () = [| |] - let empty = {arr=[| |]; id=Transient.empty} + let empty = [| |] let is_empty a = length a = 0 - let return x = { arr=[| x |]; id=Transient.empty} - - let owns ~id a = - Transient.active id && Transient.equal id a.id + let return x = [| x |] let get a i = if i<0 || i >= length a then invalid_arg "A.get"; - Array.unsafe_get a.arr i + Array.unsafe_get a i (* push at the back *) let push x a = let n = length a in if n = max_length then invalid_arg "A.push"; let arr = Array.make (n+1) x in - Array.blit a.arr 0 arr 0 n; - {a with arr;} + Array.blit a 0 arr 0 n; + arr let pop a = let n = length a in if n=0 then invalid_arg "A.pop"; - let arr = Array.sub a.arr 0 (n-1) in - {a with arr} + Array.sub a 0 (n-1) let append a b = let n_a = length a in @@ -94,10 +87,10 @@ module A = struct if n_a = 0 then b else if n_b = 0 then a else ( - let arr = Array.make (n_a+n_b) (a.arr.(0)) in - Array.blit a.arr 0 arr 0 n_a; - Array.blit b.arr 0 arr n_a n_b; - {id=Transient.empty; arr} + let arr = Array.make (n_a+n_b) (a.(0)) in + Array.blit a 0 arr 0 n_a; + Array.blit b 0 arr n_a n_b; + arr ) let set ~mut a i x = @@ -105,22 +98,18 @@ module A = struct if i=length a then ( (* insert in a longer copy *) let arr = Array.make (i+1) x in - Array.blit a.arr 0 arr 0 i; - {a with arr} + Array.blit a 0 arr 0 i; + arr ) else if mut then ( (* replace element at [i] in place *) - a.arr.(i) <- x; + a.(i) <- x; a ) else ( (* replace element at [i] in copy *) - let arr = Array.copy a.arr in + let arr = Array.copy a in arr.(i) <- x; - {a with arr} + arr ) - - let iteri f a = Array.iteri f a.arr - - let fold f acc a = Array.fold_left f acc a.arr end (** {2 Functors} *) diff --git a/src/data/CCFun_vec.mli b/src/data/CCFun_vec.mli index 04c804fe..4e89df1b 100644 --- a/src/data/CCFun_vec.mli +++ b/src/data/CCFun_vec.mli @@ -15,6 +15,7 @@ type 'a gen = unit -> 'a option type 'a printer = Format.formatter -> 'a -> unit type 'a ktree = unit -> [`Nil | `Node of 'a * 'a ktree list] +(* TODO: restore this (** {2 Transient Identifiers} *) module Transient : sig type t @@ -46,6 +47,7 @@ module Transient : sig exception Frozen (** Raised when a frozen ID is used. *) end +*) (** {2 Signature} *) From ea4a4e4ffba019ac798d62eb4335f120676a41ed Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Mar 2018 23:50:49 -0600 Subject: [PATCH 39/60] utf8string: detect overlong encodings - also, stronger tests for utf8string --- src/core/CCUtf8_string.ml | 39 +++++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/core/CCUtf8_string.ml b/src/core/CCUtf8_string.ml index c3b82db0..5cac7bb8 100644 --- a/src/core/CCUtf8_string.ml +++ b/src/core/CCUtf8_string.ml @@ -37,15 +37,20 @@ exception Malformed of string * int @raise Malformed if an invalid substring is met *) let next_ (type a) (st : Dec.t) ~(yield:uchar -> a) ~(stop:unit -> a) () : a = let open Dec in + let malformed st = raise (Malformed (st.s,st.i)) in (* read a multi-byte character. - @param acc the accumulator (containing the first byte of the char) - @param n_bytes number of bytes to read (i.e. [width char - 1]) *) - let read_multi n_bytes acc = + @param acc the accumulator (containing the first byte of the char) + @param n_bytes number of bytes to read (i.e. [width char - 1]) + @param overlong minimal bound on second byte (to detect overlong encoding) + *) + let read_multi ?(overlong=0) n_bytes acc = (* inner loop j = 1..jmax *) let rec aux j acc = let c = Char.code st.s.[ st.i + j] in (* check that c is in 0b10xxxxxx *) - if c lsr 6 <> 0b10 then raise (Malformed (st.s,st.i)); + if c lsr 6 <> 0b10 then malformed st; + (* overlong encoding? *) + if j=1 && overlong<>0 && (c land 0b111111) < overlong then malformed st; (* except for first, each char gives 6 bits *) let next = (acc lsl 6) lor (c land 0b111111) in if j = n_bytes then ( @@ -54,7 +59,7 @@ let next_ (type a) (st : Dec.t) ~(yield:uchar -> a) ~(stop:unit -> a) () : a = st.i <- st.i + j + 1; (* +1 for first char *) yield (Uchar.unsafe_of_int next) ) else ( - raise (Malformed (st.s,st.i)) + malformed st; ) ) else ( aux (j+1) next @@ -66,7 +71,7 @@ let next_ (type a) (st : Dec.t) ~(yield:uchar -> a) ~(stop:unit -> a) () : a = aux 1 acc (* start with j=1, first char is already proccessed! *) ) else ( (* char is truncated *) - raise (Malformed (st.s,st.i)) + malformed st; ) in if st.i >= st.len then ( @@ -80,11 +85,18 @@ let next_ (type a) (st : Dec.t) ~(yield:uchar -> a) ~(stop:unit -> a) () : a = st.i <- 1 + st.i; yield (Uchar.of_int @@ Char.code c) (* 0xxxxxxx *) | '\194' .. '\223' -> read_multi 1 ((Char.code c) land 0b11111) (* 110yyyyy *) - | '\224' .. '\239' -> read_multi 2 ((Char.code c) land 0b1111) (* 1110zzzz *) - | '\240' .. '\244' -> read_multi 3 ((Char.code c) land 0b111) (* 11110uuu *) + | '\225' .. '\239' -> read_multi 2 ((Char.code c) land 0b1111) (* 1110zzzz *) + | '\241' .. '\244' -> read_multi 3 ((Char.code c) land 0b111) (* 11110uuu *) + | '\224' -> + (* overlong: if next byte is < than [0b001000000] then the char + would fit in 1 byte *) + read_multi ~overlong:0b00100000 2 ((Char.code c) land 0b1111) (* 1110zzzz *) + | '\240' -> + (* overlong: if next byte is < than [0b000100000] then the char + would fit in 2 bytes *) + read_multi ~overlong:0b00010000 3 ((Char.code c) land 0b111) (* 11110uuu *) | '\128' .. '\193' (* 192,193 are forbidden *) - | '\245' .. '\255' -> - raise (Malformed (st.s,st.i)) + | '\245' .. '\255' -> malformed st; ) let to_gen ?(idx=0) str : uchar gen = @@ -248,6 +260,9 @@ let uutf_to_seq s f = (*$T not (is_valid "\192\181") not (is_valid "\193\143") + not (is_valid "\224\151\167") + not (is_valid "\224\137\165") + is_valid "\240\151\189\163" *) (*$QR & ~long_factor:40 @@ -272,7 +287,7 @@ let uutf_to_seq s f = (* compare with uutf *) -(*$QR & ~long_factor:40 ~count:10_000 +(*$QR & ~long_factor:40 ~count:100_000 Q.string (fun s -> let v1 = is_valid s in let v2 = uutf_is_valid s in @@ -281,7 +296,7 @@ let uutf_to_seq s f = ) *) -(*$QR & ~long_factor:40 ~count:10_000 +(*$QR & ~long_factor:40 ~count:100_000 Q.string (fun s -> Q.assume (is_valid s && uutf_is_valid s); let pp s = Q.Print.(list pp_uchar) s in From 79089677af30f2f9e893e4391196a01c0b068116 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 6 Mar 2018 23:50:59 -0600 Subject: [PATCH 40/60] `CCFun_vec`: implement missing functions --- src/data/CCFun_vec.ml | 363 ++++++++--------------------------------- src/data/CCFun_vec.mli | 45 +++-- 2 files changed, 88 insertions(+), 320 deletions(-) diff --git a/src/data/CCFun_vec.ml b/src/data/CCFun_vec.ml index b16fdb9b..e49a08bb 100644 --- a/src/data/CCFun_vec.ml +++ b/src/data/CCFun_vec.ml @@ -54,7 +54,12 @@ module A = struct let length = Array.length let iteri = Array.iteri + let iter = Array.iter let fold = Array.fold_left + let map = Array.map + + let iteri_rev f a = + for i = length a-1 downto 0 do f i a.(i) done let create () = [| |] @@ -247,270 +252,75 @@ let iteri ~f (m : 'a t) : unit = A.iteri (fun i sub -> Queue.push (combine_idx i high, sub) q) m.subs; done +let iteri_rev ~f (m : 'a t) : unit = + (* like {!iteri} but last element comes first *) + let rec aux high m = + A.iteri_rev (fun i sub -> aux (combine_idx i high) sub) m.subs; + (* only now, explore current leaves *) + A.iteri_rev (fun i x -> f (combine_idx high i) x) m.leaves; + in + aux 0 m + let foldi ~f ~x m = let acc = ref x in iteri m ~f:(fun i x -> acc := f !acc i x); !acc +let foldi_rev ~f ~x m = + let acc = ref x in + iteri_rev m + ~f:(fun i x -> acc := f !acc i x); + !acc + let iter ~f m = iteri ~f:(fun _ x -> f x) m let fold ~f ~x m = foldi ~f:(fun acc _ x -> f acc x) ~x m -let of_list l = List.fold_left (fun v x -> push x v) empty l +let fold_rev ~f ~x m = foldi_rev ~f:(fun acc _ x -> f acc x) ~x m -let to_list m = fold m ~f:(fun acc x -> x::acc) ~x:[] |> List.rev - -(* TODO - -(* add [k,v] to the list [l], removing old binding if any *) -let rec add_list_ k v l = match l with - | Nil -> One (k,v) - | One (k1, v1) -> - if Key.equal k k1 then One (k, v) else Two (k,v,k1,v1) - | Two (k1, v1, k2, v2) -> - if Key.equal k k1 then Two (k, v, k2, v2) - else if Key.equal k k2 then Two (k, v, k1, v1) - else Cons (k, v, l) - | Cons (k', v', tail) -> - if Key.equal k k' - then Cons (k, v, tail) (* replace *) - else Cons (k', v', add_list_ k v tail) - -let node_ leaf a = N (leaf, a) - - -(* [h]: hash, with the part required to reach this leaf removed - [id] is the transient ID used for mutability *) -let rec add_ ~id k v ~h m = match m with - | E -> S (h, k, v) - | S (h', k', v') -> - if Hash.equal h h' - then if Key.equal k k' - then S (h, k, v) (* replace *) - else L (h, Cons (k, v, Cons (k', v', Nil))) - else - make_array_ ~id ~leaf:(Cons (k', v', Nil)) ~h_leaf:h' k v ~h - | L (h', l) -> - if Hash.equal h h' - then L (h, add_list_ k v l) - else (* split into N *) - make_array_ ~id ~leaf:l ~h_leaf:h' k v ~h - | N (leaf, a) -> - if Hash.is_0 h - then node_ (add_list_ k v leaf) a - else - let mut = A.owns ~id a in (* can we modify [a] in place? *) - node_ leaf (add_to_array_ ~id ~mut k v ~h a) - -(* make an array containing a leaf, and insert (k,v) in it *) -and make_array_ ~id ~leaf ~h_leaf:h' k v ~h = - let a = A.create ~id in - let a, leaf = - if Hash.is_0 h' then a, leaf - else - (* put leaf in the right bucket *) - let i = Hash.rem h' in - let h'' = Hash.quotient h' in - A.set ~mut:true a i (L (h'', leaf)), Nil - in - (* then add new node *) - let a, leaf = - if Hash.is_0 h then a, add_list_ k v leaf - else add_to_array_ ~id ~mut:true k v ~h a, leaf - in - N (leaf, a) - -(* add k->v to [a] *) -and add_to_array_ ~id ~mut k v ~h a = - (* insert in a bucket *) - let i = Hash.rem h in - let h' = Hash.quotient h in - A.update ~default:E ~mut a i (fun x -> add_ ~id k v ~h:h' x) - -let add k v m = add_ ~id:Transient.empty k v ~h:(hash_ k) m - -(*$Q - _listuniq (fun l -> \ - let m = List.fold_left (fun m (x,y) -> add x y m) empty l in \ - List.for_all (fun (x,y) -> get_exn x m = y) l) -*) - -let add_mut ~id k v m = - if Transient.frozen id then raise Transient.Frozen; - add_ ~id k v ~h:(hash_ k) m - -(*$R - let lsort = List.sort Pervasives.compare in - let m = of_list [1, 1; 2, 2] in - let id = Transient.create() in - let m' = add_mut ~id 3 3 m in - let m' = add_mut ~id 4 4 m' in - assert_equal [1, 1; 2, 2] (to_list m |> lsort); - assert_equal [1, 1; 2, 2; 3,3; 4,4] (to_list m' |> lsort); - Transient.freeze id; - assert_bool "must raise" - (try ignore(add_mut ~id 5 5 m'); false with Transient.Frozen -> true) -*) - - -exception LocalExit - -let is_empty_arr_ a = - try - A.iter (fun t -> if not (is_empty t) then raise LocalExit) a; - true - with LocalExit -> false - -let is_empty_list_ = function - | Nil -> true - | One _ - | Two _ - | Cons _ -> false - -let rec remove_list_ k l = match l with - | Nil -> Nil - | One (k', _) -> - if Key.equal k k' then Nil else l - | Two (k1, v1, k2, v2) -> - if Key.equal k k1 then One (k2, v2) - else if Key.equal k k2 then One (k1, v1) - else l - | Cons (k', v', tail) -> - if Key.equal k k' - then tail - else Cons (k', v', remove_list_ k tail) - -let rec remove_rec_ ~id k ~h m = match m with - | E -> E - | S (_, k', _) -> - if Key.equal k k' then E else m - | L (h, l) -> - let l = remove_list_ k l in - if is_empty_list_ l then E else L (h, l) - | N (leaf, a) -> - let leaf, a = - if Hash.is_0 h - then remove_list_ k leaf, a - else - let i = Hash.rem h in - let h' = Hash.quotient h in - let new_t = remove_rec_ ~id k ~h:h' (A.get ~default:E a i) in - if is_empty new_t - then leaf, A.remove a i (* remove sub-tree *) - else - let mut = A.owns ~id a in - leaf, A.set ~mut a i new_t - in - if is_empty_list_ leaf && is_empty_arr_ a - then E - else N (leaf, a) - -let remove k m = remove_rec_ ~id:Transient.empty k ~h:(hash_ k) m - -let remove_mut ~id k m = - if Transient.frozen id then raise Transient.Frozen; - remove_rec_ ~id k ~h:(hash_ k) m +let rec map f m : _ t = + { subs=A.map (map f) m.subs; + leaves=A.map f m.leaves; + size=m.size; + } (*$QR - _listuniq (fun l -> - let m = of_list l in - List.for_all - (fun (x,_) -> - let m' = remove x m in - not (mem x m') && - cardinal m' = cardinal m - 1 && - List.for_all - (fun (y,v) -> y = x || get_exn y m' = v) - l - ) l + Q.(pair (fun1 Observable.int bool)(small_list int)) (fun (f,l) -> + let f = Q.Fn.apply f in + (List.map f l) = (of_list l |> map f |> to_list) ) -*) + *) -let update_ ~id k f m = - let h = hash_ k in - let opt_v = try Some (get_ k ~h m) with Not_found -> None in - match opt_v, f opt_v with - | None, None -> m - | Some _, Some v - | None, Some v -> add_ ~id k v ~h m - | Some _, None -> remove_rec_ ~id k ~h m +let append a b = + if is_empty b then a + else fold ~f:(fun v x -> push x v) ~x:a b -let update k ~f m = update_ ~id:Transient.empty k f m +(*$QR + Q.(pair (small_list int)(small_list int)) (fun (l1,l2) -> + (l1 @ l2) = (append (of_list l1)(of_list l2) |> to_list) + ) + *) -let update_mut ~id k ~f m = - if Transient.frozen id then raise Transient.Frozen; - update_ ~id k f m - -(*$R - let m = of_list [1, 1; 2, 2; 5, 5] in - let m' = update 4 - (function - | None -> Some 4 - | Some _ -> Some 0 - ) m - in - assert_equal [1,1; 2,2; 4,4; 5,5] (to_list m' |> List.sort Pervasives.compare); -*) - -let iter ~f t = - let rec aux = function - | E -> () - | S (_, k, v) -> f k v - | L (_,l) -> aux_list l - | N (l,a) -> aux_list l; A.iter aux a - and aux_list = function - | Nil -> () - | One (k,v) -> f k v - | Two (k1,v1,k2,v2) -> f k1 v1; f k2 v2 - | Cons (k, v, tl) -> f k v; aux_list tl - in - aux t - -let fold ~f ~x:acc t = - let rec aux acc t = match t with - | E -> acc - | S (_,k,v) -> f acc k v - | L (_,l) -> aux_list acc l - | N (l,a) -> let acc = aux_list acc l in A.fold aux acc a - and aux_list acc l = match l with - | Nil -> acc - | One (k,v) -> f acc k v - | Two (k1,v1,k2,v2) -> f (f acc k1 v1) k2 v2 - | Cons (k, v, tl) -> let acc = f acc k v in aux_list acc tl - in - aux acc t - -(*$T - let l = CCList.(1 -- 10 |> map (fun x->x,x)) in \ - of_list l \ - |> fold ~f:(fun acc x y -> (x,y)::acc) ~x:[] \ - |> List.sort Pervasives.compare = l -*) - -let cardinal m = fold ~f:(fun n _ _ -> n+1) ~x:0 m - -let to_list m = fold ~f:(fun acc k v -> (k,v)::acc) ~x:[] m - -let add_list_mut ~id m l = - List.fold_left (fun acc (k,v) -> add_mut ~id k v acc) m l - -let add_list m l = - Transient.with_ (fun id -> add_list_mut ~id m l) +let add_list v l = List.fold_left (fun v x -> push x v) v l let of_list l = add_list empty l -let add_seq_mut ~id m seq = - let m = ref m in - seq (fun (k,v) -> m := add_mut ~id k v !m); - !m +let to_list m = fold_rev m ~f:(fun acc x -> x::acc) ~x:[] -let add_seq m seq = - Transient.with_ (fun id -> add_seq_mut ~id m seq) +(*$QR + Q.(small_list int) (fun l -> + l = to_list (of_list l)) +*) + +let add_seq v seq = + let v = ref v in + seq (fun x -> v := push x !v); + !v let of_seq s = add_seq empty s -let to_seq m yield = iter ~f:(fun k v -> yield (k,v)) m +let to_seq m yield = iteri ~f:(fun _ v -> yield v) m (*$Q _listuniq (fun l -> \ @@ -519,12 +329,9 @@ let to_seq m yield = iter ~f:(fun k v -> yield (k,v)) m |> List.sort Pervasives.compare) ) *) -let rec add_gen_mut ~id m g = match g() with +let rec add_gen m g = match g() with | None -> m - | Some (k,v) -> add_gen_mut ~id (add_mut ~id k v m) g - -let add_gen m g = - Transient.with_ (fun id -> add_gen_mut ~id m g) + | Some x -> add_gen (push x m) g let of_gen g = add_gen empty g @@ -532,29 +339,19 @@ let of_gen g = add_gen empty g hashes lexicographically by A.length_log-wide chunks of bits, least-significant chunks first *) let to_gen m = - let st = Stack.create() in - Stack.push m st; + let q_cur : 'a Queue.t = Queue.create() in + let q_sub : 'a t Queue.t = Queue.create() in + Queue.push m q_sub; let rec next() = - if Stack.is_empty st then None - else match Stack.pop st with - | E -> next () - | S (_,k,v) -> Some (k,v) - | L (_, Nil) -> next() - | L (_, One (k,v)) -> Some (k,v) - | L (h, Two (k1,v1,k2,v2)) -> - Stack.push (L (h, One (k2,v2))) st; - Some (k1,v1) - | L (h, Cons(k,v,tl)) -> - Stack.push (L (h, tl)) st; (* tail *) - Some (k,v) - | N (l, a) -> - A.iter - (fun sub -> Stack.push sub st) - a; - Stack.push (L (Hash.zero, l)) st; (* leaf *) - next() - in - next + if not (Queue.is_empty q_cur) then ( + Some (Queue.pop q_cur) + ) else if not (Queue.is_empty q_sub) then ( + let m = Queue.pop q_sub in + A.iter (fun x -> Queue.push x q_cur) m.leaves; + A.iter (fun sub -> Queue.push sub q_sub) m.subs; + next() + ) else None + in next (*$Q _listuniq (fun l -> \ @@ -574,38 +371,10 @@ let choose_exn m = match choose m with | None -> raise Not_found | Some (k,v) -> k, v -let pp ppk ppv out m = +let pp ppv out m = let first = ref true in iter m - ~f:(fun k v -> + ~f:(fun v -> if !first then first := false else Format.fprintf out ";@ "; - ppk out k; - Format.pp_print_string out " -> "; ppv out v ) - -let rec as_tree m () = match m with - | E -> `Nil - | S (h,k,v) -> `Node (`L ((h:>int), [k,v]), []) - | L (h,l) -> `Node (`L ((h:>int), list_as_tree_ l), []) - | N (l,a) -> `Node (`N, as_tree (L (Hash.zero, l)) :: array_as_tree_ a) -and list_as_tree_ l = match l with - | Nil -> [] - | One (k,v) -> [k,v] - | Two (k1,v1,k2,v2) -> [k1,v1; k2,v2] - | Cons (k, v, tail) -> (k,v) :: list_as_tree_ tail -and array_as_tree_ a = A.fold (fun acc t -> as_tree t :: acc) [] a - *) - -(* TODO: $R again? - let m = of_list CCList.( (501 -- 1000) @ (500 -- 1) |> map (fun i->i,i)) in - assert_equal ~printer:CCInt.to_string 1000 (length m); - assert_bool "check all get" - (Sequence.for_all (fun i -> i = get_exn i m) Sequence.(1 -- 1000)); - let m = Sequence.(501 -- 1000 |> fold (fun m i -> remove i m) m) in - assert_equal ~printer:CCInt.to_string 500 (length m); - assert_bool "check all get after remove" - (Sequence.for_all (fun i -> i = get_exn i m) Sequence.(1 -- 500)); - assert_bool "check all get after remove" - (Sequence.for_all (fun i -> None = get i m) Sequence.(501 -- 1000)); -*) diff --git a/src/data/CCFun_vec.mli b/src/data/CCFun_vec.mli index 4e89df1b..06c252e5 100644 --- a/src/data/CCFun_vec.mli +++ b/src/data/CCFun_vec.mli @@ -75,11 +75,21 @@ val pop_exn : 'a t -> 'a * 'a t val iter : f:('a -> unit) -> 'a t -> unit val iteri : f:(int -> 'a -> unit) -> 'a t -> unit +(** Iterate on elements with their index, in increasing order *) + +val iteri_rev : f:(int -> 'a -> unit) -> 'a t -> unit +(** Iterate on elements with their index, but starting from the end *) val fold : f:('b -> 'a -> 'b) -> x:'b -> 'a t -> 'b val foldi : f:('b -> int -> 'a -> 'b) -> x:'b -> 'a t -> 'b +val append : 'a t -> 'a t -> 'a t + +val map : ('a -> 'b) -> 'a t -> 'b t + +val choose : 'a t -> 'a option + (* TODO val push_mut : id:Transient.t -> 'a -> 'a t -> 'a t @@ -93,11 +103,7 @@ val pop_mut : id:Transient.t -> 'a t -> 'a * 'a t (** Same as {!remove}, but modifies in place whenever possible. @raise Transient.Frozen if [id] is frozen. *) -val append : 'a t -> 'a t -> 'a t - val append_mut : id:Transient.t -> into:'a t -> 'a t -> 'a t - -val map : ('a -> 'b) -> 'a t -> 'b t *) (** {6 Conversions} *) @@ -106,39 +112,32 @@ val to_list : 'a t -> 'a list val of_list : 'a list -> 'a t -(* TODO - val add_list : 'a t -> 'a list -> 'a t -val add_list_mut : id:Transient.t -> 'a t -> 'a list -> 'a t -(** @raise Frozen if the ID is frozen. *) - val add_seq : 'a t -> 'a sequence -> 'a t -val add_seq_mut : id:Transient.t -> 'a t -> 'a sequence -> 'a t -(** @raise Frozen if the ID is frozen. *) - val of_seq : 'a sequence -> 'a t val to_seq : 'a t -> 'a sequence val add_gen : 'a t -> 'a gen -> 'a t -val add_gen_mut : id:Transient.t -> 'a t -> 'a gen -> 'a t -(** @raise Frozen if the ID is frozen. *) - val of_gen : 'a gen -> 'a t val to_gen : 'a t -> 'a gen +(* TODO + +val add_list_mut : id:Transient.t -> 'a t -> 'a list -> 'a t +(** @raise Frozen if the ID is frozen. *) + +val add_seq_mut : id:Transient.t -> 'a t -> 'a sequence -> 'a t +(** @raise Frozen if the ID is frozen. *) + +val add_gen_mut : id:Transient.t -> 'a t -> 'a gen -> 'a t +(** @raise Frozen if the ID is frozen. *) + *) + (** {6 IO} *) val pp : 'a printer -> 'a t printer - -val as_tree : 'a t -> [`L of int * 'a list | `N ] ktree -(** For debugging purpose: explore the structure of the tree, - with [`L (h,l)] being a leaf (with shared hash [h]) - and [`N] an inner node. *) - - *) - From 4a9b41c3cd5b7e48b66c086013b7ec43568167b3 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 7 Mar 2018 00:17:48 -0600 Subject: [PATCH 41/60] add various functions on `CCUtf8_string` --- src/core/CCUtf8_string.ml | 25 +++++++++++++++++++++++++ src/core/CCUtf8_string.mli | 16 ++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/core/CCUtf8_string.ml b/src/core/CCUtf8_string.ml index 5cac7bb8..27e13898 100644 --- a/src/core/CCUtf8_string.ml +++ b/src/core/CCUtf8_string.ml @@ -29,6 +29,8 @@ module Dec = struct { s=s; i=idx; len=String.length s; } end +let n_bytes = length + exception Malformed of string * int (** Malformed string at given offset *) @@ -134,6 +136,8 @@ let fold ?idx f acc s = in aux acc +let n_chars = fold (fun x _ -> x+1) 0 + let to_list ?(idx=0) s : uchar list = fold ~idx (fun acc x -> x :: acc) [] s |> List.rev @@ -185,6 +189,27 @@ let of_list l : t = List.iter (code_to_string buf) l; Buffer.contents buf +let map f s : t = + let buf = Buffer.create (n_bytes s) in + iter (fun c -> code_to_string buf (f c)) s; + Buffer.contents buf + +let filter_map f s : t = + let buf = Buffer.create (n_bytes s) in + iter + (fun c -> match f c with + | None -> () + | Some c -> code_to_string buf c) + s; + Buffer.contents buf + +let flat_map f s : t = + let buf = Buffer.create (n_bytes s) in + iter (fun c -> iter (code_to_string buf) (f c)) s; + Buffer.contents buf + +let append = Pervasives.(^) + let unsafe_of_string s = s let is_valid (s:string) : bool = diff --git a/src/core/CCUtf8_string.mli b/src/core/CCUtf8_string.mli index 64843c18..9b90a990 100644 --- a/src/core/CCUtf8_string.mli +++ b/src/core/CCUtf8_string.mli @@ -53,6 +53,22 @@ val fold : ?idx:int -> ('a -> uchar -> 'a) -> 'a -> t -> 'a val iter : ?idx:int -> (uchar -> unit) -> t -> unit +val n_chars : t -> int +(** Number of characters *) + +val n_bytes : t -> int +(** Number of bytes *) + +val map : (uchar -> uchar) -> t -> t + +val filter_map : (uchar -> uchar option) -> t -> t + +val flat_map : (uchar -> t) -> t -> t + +val append : t -> t -> t + +val concat : t -> t list -> t + val of_seq : uchar sequence -> t val of_gen : uchar gen -> t From c578dd958388859d3387a3cb02bf78d2562fd098 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Thu, 8 Mar 2018 17:53:43 -0600 Subject: [PATCH 42/60] minor fixes in doc --- src/data/CCDeque.mli | 4 ++-- src/data/CCMixmap.mli | 5 +++-- src/data/CCRingBuffer.mli | 5 ++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/data/CCDeque.mli b/src/data/CCDeque.mli index c0b6b8c3..ea8acd33 100644 --- a/src/data/CCDeque.mli +++ b/src/data/CCDeque.mli @@ -79,8 +79,8 @@ type 'a sequence = ('a -> unit) -> unit val of_seq : 'a sequence -> 'a t (** Create a deque from the sequence. - @since 0.13 optional argument [deque] disappears, use - {!add_seq_back} instead. *) + optional argument [deque] disappears, use {!add_seq_back} instead since + 0.13 *) val to_seq : 'a t -> 'a sequence (** Iterate on the elements. *) diff --git a/src/data/CCMixmap.mli b/src/data/CCMixmap.mli index 829330f9..7eb488d7 100644 --- a/src/data/CCMixmap.mli +++ b/src/data/CCMixmap.mli @@ -26,9 +26,10 @@ assert (M.get ~inj:inj_str 3 m = None) ]} + change of API, the map is last argument to make piping with [|>] easier since 0.16. + @since 0.9 - @since 0.16 change of API, the map is last argument to - make piping with [|>] easier. *) +*) type 'a injection (** An accessor for values of type 'a in any map. Values put diff --git a/src/data/CCRingBuffer.mli b/src/data/CCRingBuffer.mli index ffec50fd..e5f77378 100644 --- a/src/data/CCRingBuffer.mli +++ b/src/data/CCRingBuffer.mli @@ -10,10 +10,9 @@ {b status: experimental} - @since 0.9 + Change in the API to provide only a bounded buffer since 1.3 - Change in the API to provide only a bounded buffer - @since 1.3 + @since 0.9 *) (** {2 Underlying Array} *) From 2c5cda7e3dd858e6a228b3921a648c067a16bed5 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 9 Mar 2018 11:27:11 -0600 Subject: [PATCH 43/60] add `?margin` parameter to `CCFormat.ksprintf` --- src/core/CCFormat.ml | 3 ++- src/core/CCFormat.mli | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/CCFormat.ml b/src/core/CCFormat.ml index d01f3ad9..a9359cb6 100644 --- a/src/core/CCFormat.ml +++ b/src/core/CCFormat.ml @@ -383,10 +383,11 @@ let fprintf_dyn_color ~colors out fmt = assert_equal "yolo" (sprintf_no_color "@{yolo@}"); *) -let ksprintf ~f fmt = +let ksprintf ?margin ~f fmt = let buf = Buffer.create 32 in let out = Format.formatter_of_buffer buf in if !color_enabled then set_color_tag_handling out; + begin match margin with None -> () | Some m -> pp_set_margin out m end; Format.kfprintf (fun _ -> Format.pp_print_flush out (); f (Buffer.contents buf)) out fmt diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index 8b2e5ffd..f54c46d0 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -273,11 +273,13 @@ val fprintf_dyn_color : colors:bool -> t -> ('a, t, unit ) format -> 'a @since 0.21 *) val ksprintf : + ?margin:int -> f:(string -> 'b) -> ('a, Format.formatter, unit, 'b) format4 -> 'a (** [ksprintf fmt ~f] formats using [fmt], in a way similar to {!sprintf}, and then calls [f] on the resulting string. + @param margin set margin (since NEXT_RELEASE) @since 0.14 *) val to_file : string -> ('a, t, unit, unit) format4 -> 'a From 09d5b146f21f3dda964b7f66473b834896cb029a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 12 Mar 2018 22:04:34 -0500 Subject: [PATCH 44/60] remove explicit dep on `bytes` in jbuild files --- src/core/jbuild | 2 +- src/data/jbuild | 2 +- src/iter/jbuild | 2 +- src/sexp/jbuild | 2 +- src/unix/jbuild | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/core/jbuild b/src/core/jbuild index 2c848845..1f988b34 100644 --- a/src/core/jbuild +++ b/src/core/jbuild @@ -5,5 +5,5 @@ (wrapped false) (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -safe-string -nolabels -open CCMonomorphic)) (ocamlopt_flags (:standard (:include ../flambda.flags))) - (libraries (bytes result uchar containers.monomorphic)) + (libraries (result uchar containers.monomorphic)) )) diff --git a/src/data/jbuild b/src/data/jbuild index 9fcf9861..3f5bf259 100644 --- a/src/data/jbuild +++ b/src/data/jbuild @@ -5,5 +5,5 @@ (wrapped false) (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -safe-string)) (ocamlopt_flags (:standard (:include ../flambda.flags))) - (libraries (bytes result)) + (libraries (result)) )) diff --git a/src/iter/jbuild b/src/iter/jbuild index bade997f..b2cab584 100644 --- a/src/iter/jbuild +++ b/src/iter/jbuild @@ -5,5 +5,5 @@ (wrapped false) (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -safe-string)) (ocamlopt_flags (:standard (:include ../flambda.flags))) - (libraries (bytes result)) + (libraries (result)) )) diff --git a/src/sexp/jbuild b/src/sexp/jbuild index b3c35302..743e3b30 100644 --- a/src/sexp/jbuild +++ b/src/sexp/jbuild @@ -5,7 +5,7 @@ (wrapped false) (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -safe-string)) (ocamlopt_flags (:standard (:include ../flambda.flags))) - (libraries (bytes result)) + (libraries (result)) )) (ocamllex (CCSexp_lex)) diff --git a/src/unix/jbuild b/src/unix/jbuild index 6502a9d3..ef81a336 100644 --- a/src/unix/jbuild +++ b/src/unix/jbuild @@ -6,5 +6,5 @@ (optional) (flags (:standard -w +a-4-42-44-48-50-58-32-60@8 -safe-string)) (ocamlopt_flags (:standard (:include ../flambda.flags))) - (libraries (bytes result unix)) + (libraries (result unix)) )) From 7a22286ca1224a075de826dbfd8b821affb32541 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Tue, 13 Mar 2018 00:34:49 -0500 Subject: [PATCH 45/60] use standard `List.sort_uniq` --- src/core/CCList.ml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/core/CCList.ml b/src/core/CCList.ml index 82291ba3..8a4dbfb0 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -580,13 +580,7 @@ let sorted_merge ~cmp l1 l2 = List.length (sorted_merge ~cmp:CCInt.compare l1 l2) = List.length l1 + List.length l2) *) -let sort_uniq (type elt) ~cmp l = - let module S = Set.Make(struct - type t = elt - let compare = cmp - end) in - let set = fold_right S.add l S.empty in - S.elements set +let sort_uniq (type elt) ~cmp l = List.sort_uniq cmp l (*$T sort_uniq ~cmp:CCInt.compare [1;2;5;3;6;1;4;2;3] = [1;2;3;4;5;6] From 9b804b46a5ec4298e646456eb99c5203336f5530 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 14 Mar 2018 18:20:59 -0500 Subject: [PATCH 46/60] update readme --- README.adoc | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.adoc b/README.adoc index 4ce39456..9050155f 100644 --- a/README.adoc +++ b/README.adoc @@ -185,8 +185,7 @@ The prefered way to install is through http://opam.ocaml.org/[opam]. === From Sources -On the branch `master` you will need `oasis` to build the library. On the -branch `stable` it is not necessary. +You need dune (formerly jbuilder). $ make @@ -547,8 +546,6 @@ printer:: `'a printer = Format.formatter -> 'a -> unit` is a pretty-printer See link:doc/containers.adoc[the extended documentation] for more examples. -Powered by image:http://oasis.forge.ocamlcore.org/oasis-badge.png[alt="OASIS", style="border: none;", link="http://oasis.forge.ocamlcore.org/"] - == HOWTO (for contributors) === Make a release From d18d9fb6361ffb8443ebe4dfa682c4f784260a8f Mon Sep 17 00:00:00 2001 From: JPR Date: Tue, 13 Mar 2018 22:24:38 +0100 Subject: [PATCH 47/60] Comments - Style & typos fixing --- src/core/CCChar.mli | 4 ++-- src/core/CCFormat.mli | 31 +++++++++++++++++++------------ src/core/CCHashtbl.ml | 2 +- src/core/CCHashtbl.mli | 10 ++++------ src/core/CCHeap.ml | 2 +- src/core/CCHeap.mli | 6 +++--- src/core/CCIO.mli | 4 ++-- src/core/CCInt64.mli | 16 ++++++++-------- src/core/CCList.mli | 2 +- src/core/CCNativeint.mli | 2 +- src/core/CCString.ml | 2 +- src/core/CCString.mli | 27 +++++++++++++-------------- src/core/CCUtf8_string.ml | 2 +- src/core/CCUtf8_string.mli | 20 ++++++++++---------- src/data/CCBV.mli | 6 +++--- src/data/CCCache.mli | 2 +- src/data/CCDeque.mli | 6 +++--- src/data/CCFun_vec.mli | 8 ++++---- src/data/CCGraph.mli | 4 ++-- src/data/CCHet.mli | 2 +- src/data/CCMixset.mli | 2 +- src/data/CCMultiMap.mli | 4 ++-- src/data/CCPersistentArray.mli | 4 ++-- src/data/CCRAL.mli | 2 +- src/data/CCRingBuffer.mli | 12 ++++++------ src/data/CCWBTree.ml | 2 +- src/data/CCWBTree.mli | 2 +- src/data/CCZipper.mli | 4 ++-- src/iter/CCKList.mli | 14 +++++++------- src/iter/CCKTree.mli | 4 ++-- src/iter/CCLazy_list.mli | 2 +- src/threads/CCLock.mli | 2 +- src/threads/CCPool.mli | 10 +++++----- src/threads/CCTimer.mli | 2 +- 34 files changed, 114 insertions(+), 110 deletions(-) diff --git a/src/core/CCChar.mli b/src/core/CCChar.mli index 56414df3..ab1a1b5f 100644 --- a/src/core/CCChar.mli +++ b/src/core/CCChar.mli @@ -41,7 +41,7 @@ val to_int : t -> int @since 1.0 *) val pp_buf : Buffer.t -> t -> unit -(** Used to be {!pp}, changed name @since 2.0 *) +(** Renamed from [pp] since 2.0. *) val pp : Format.formatter -> t -> unit -(** Used to be {!print}, changed name @since 2.0 *) +(** Renamed from [print] since 2.0. *) diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index f54c46d0..449fa8c7 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -17,7 +17,8 @@ type 'a printer = t -> 'a -> unit (** {2 Combinators} *) -val silent : 'a printer (** Prints nothing *) +val silent : 'a printer +(** Prints nothing. *) val unit : unit printer (** Prints "()". *) @@ -55,7 +56,7 @@ val flush : unit printer @since 1.2 *) val string_quoted : string printer -(** Similar to {!CCString.print}. +(** Like {!CCString.print}. @since 0.14 *) val list : ?sep:unit printer -> 'a printer -> 'a list printer @@ -65,8 +66,8 @@ val seq : ?sep:unit printer -> 'a printer -> 'a sequence printer val opt : 'a printer -> 'a option printer (** [opt pp] prints options as follows: - [Some x] will become "some foo" if [pp x ---> "foo"]. - [None] will become "none". *) + - [Some x] will become "some foo" if [pp x ---> "foo"]. + - [None] will become "none". *) (** In the tuple printers, the [sep] argument is only available. @since 0.17 *) @@ -124,18 +125,18 @@ val const : 'a printer -> 'a -> unit printer val some : 'a printer -> 'a option printer (** [some pp] will print options as follows: - - [Some x] is printed using [pp] on [x]. - - [None] is not printed at all. + - [Some x] is printed using [pp] on [x] + - [None] is not printed at all @since 1.0 *) val lazy_force : 'a printer -> 'a lazy_t printer -(** [lazy_force pp out x] forces [x] and prints the result with [pp] +(** [lazy_force pp out x] forces [x] and prints the result with [pp]. @since 2.0 *) val lazy_or : ?default:unit printer -> 'a printer -> 'a lazy_t printer (** [lazy_or ?default pp out x] prints [default] if [x] is not - evaluated yet, or uses [pp] otherwise + evaluated yet, or uses [pp] otherwise. @since 2.0 *) (** {2 ANSI codes} @@ -191,28 +192,33 @@ 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: 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: unstable} @since 0.16 *) val with_color_sf : string -> ('a, t, unit, string) format4 -> 'a (** [with_color_sf "Blue" out "%s %d" "yolo" 42] will behave like {!sprintf}, but wrapping the content with the given style. + Example: {[ CCFormat.with_color_sf "red" "%a" CCFormat.Dump.(list int) [1;2;3] |> print_endline;; ]} + {b status: unstable} @since 0.21 *) val with_color_ksf : f:(string -> 'b) -> string -> ('a, t, unit, 'b) format4 -> 'a (** [with_color_ksf "Blue" ~f "%s %d" "yolo" 42] will behave like {!ksprintf}, but wrapping the content with the given style. + Example: the following with raise [Failure] with a colored message {[ @@ -244,14 +250,15 @@ val tee : t -> t -> t val sprintf : ('a, t, unit, string) format4 -> 'a (** Print into a string any format string that would usually be compatible - with {!fprintf}. Similar to {!Format.asprintf}. *) + with {!fprintf}. Like {!Format.asprintf}. *) val sprintf_no_color : ('a, t, unit, string) format4 -> 'a -(** Similar to {!sprintf} but never prints colors. +(** Like {!sprintf} but never prints colors. @since 0.16 *) val sprintf_dyn_color : colors:bool -> ('a, t, unit, string) format4 -> 'a -(** Similar to {!sprintf} but enable/disable colors depending on [colors]. +(** Like {!sprintf} but enable/disable colors depending on [colors]. + Example: {[ (* with colors *) @@ -269,7 +276,7 @@ val fprintf : t -> ('a, t, unit ) format -> 'a @since 0.14 *) val fprintf_dyn_color : colors:bool -> t -> ('a, t, unit ) format -> 'a -(** Similar to {!fprintf} but enable/disable colors depending on [colors]. +(** Like {!fprintf} but enable/disable colors depending on [colors]. @since 0.21 *) val ksprintf : diff --git a/src/core/CCHashtbl.ml b/src/core/CCHashtbl.ml index 41a33c5f..e5c67d62 100644 --- a/src/core/CCHashtbl.ml +++ b/src/core/CCHashtbl.ml @@ -205,7 +205,7 @@ module type S = sig @since 0.16 *) val of_seq_count : key sequence -> int t - (** Similar to {!add_seq_count}, but allocates a new table and returns it + (** Like {!add_seq_count}, but allocates a new table and returns it @since 0.16 *) val to_list : 'a t -> (key * 'a) list diff --git a/src/core/CCHashtbl.mli b/src/core/CCHashtbl.mli index c4f712f4..975d2cc1 100644 --- a/src/core/CCHashtbl.mli +++ b/src/core/CCHashtbl.mli @@ -76,7 +76,7 @@ module Poly : sig @since 0.16 *) val of_seq_count : 'a sequence -> ('a, int) Hashtbl.t - (** Similar to {!add_seq_count}, but allocates a new table and returns it. + (** Like {!add_seq_count}, but allocates a new table and returns it. @since 0.16 *) val to_list : ('a,'b) Hashtbl.t -> ('a * 'b) list @@ -106,8 +106,7 @@ module Poly : sig val pp : 'a printer -> 'b printer -> ('a, 'b) Hashtbl.t printer (** Printer for table. @since 0.13 - Renamed from [print]. - @since 2.0 *) + Renamed from [print] since 2.0. *) end include module type of Poly @@ -178,7 +177,7 @@ module type S = sig @since 0.16 *) val of_seq_count : key sequence -> int t - (** Similar to {!add_seq_count}, but allocates a new table and returns it. + (** Like {!add_seq_count}, but allocates a new table and returns it. @since 0.16 *) val to_list : 'a t -> (key * 'a) list @@ -208,8 +207,7 @@ module type S = sig val pp : key printer -> 'a printer -> 'a t printer (** Printer for tables. @since 0.13 - Renamed from {!print}. - @since 2.0 *) + Renamed from [print] since 2.0. *) end module Make(X : Hashtbl.HashedType) : diff --git a/src/core/CCHeap.ml b/src/core/CCHeap.ml index 4b5699d6..57d080f2 100644 --- a/src/core/CCHeap.ml +++ b/src/core/CCHeap.ml @@ -161,7 +161,7 @@ module type S = sig (** [of_list l] is [add_list empty l]. *) val add_seq : t -> elt sequence -> t - (** Similar to {!add_list}. + (** Like {!add_list}. @since 0.16 *) val of_seq : elt sequence -> t diff --git a/src/core/CCHeap.mli b/src/core/CCHeap.mli index cb53ddd8..c0801a73 100644 --- a/src/core/CCHeap.mli +++ b/src/core/CCHeap.mli @@ -81,7 +81,7 @@ module type S = sig (** {2 Conversions} The interface of [of_gen], [of_seq], [of_klist] - has changed @since 0.16 (the old signatures + has changed since 0.16 (the old signatures are now [add_seq], [add_gen], [add_klist]). *) val to_list : t -> elt list @@ -100,7 +100,7 @@ module type S = sig (** [of_list l] is [add_list empty l]. Complexity: [O(n log n)]. *) val add_seq : t -> elt sequence -> t (** @since 0.16 *) - (** Similar to {!add_list}. *) + (** Like {!add_list}. *) val of_seq : elt sequence -> t (** Build a heap from a given [sequence]. Complexity: [O(n log n)]. *) @@ -133,7 +133,7 @@ module type S = sig val pp : ?sep:string -> elt printer -> t printer (** @since 0.16 - Renamed from {!print} @since 2.0 *) + Renamed from {!print} since 2.0 *) end module Make(E : PARTIAL_ORD) : S with type elt = E.t diff --git a/src/core/CCIO.mli b/src/core/CCIO.mli index e28a6dc2..3a848f54 100644 --- a/src/core/CCIO.mli +++ b/src/core/CCIO.mli @@ -83,7 +83,7 @@ val with_out : ?mode:int -> ?flags:open_flag list -> val with_out_a : ?mode:int -> ?flags:open_flag list -> string -> (out_channel -> 'a) -> 'a -(** Similar to {!with_out} but with the [[Open_append; Open_creat; Open_wronly]] +(** Like {!with_out} but with the [[Open_append; Open_creat; Open_wronly]] flags activated, to append to the file. @raise Sys_error in case of error (same as {!open_out} and {!close_out}). *) @@ -196,7 +196,7 @@ module File : sig type walk_item = [`File | `Dir] * t val walk : t -> walk_item gen - (** Similar to {!read_dir} (with [recurse=true]), this function walks + (** Like {!read_dir} (with [recurse=true]), this function walks a directory recursively and yields either files or directories. Is a file anything that doesn't satisfy {!is_directory} (including symlinks, etc.) diff --git a/src/core/CCInt64.mli b/src/core/CCInt64.mli index 5ab65c3f..faf58cd8 100644 --- a/src/core/CCInt64.mli +++ b/src/core/CCInt64.mli @@ -2,7 +2,7 @@ (** {1 Int64} - Helpers for 64-bit integers. + Helpers for 64-bit integers @since 0.13 *) @@ -114,8 +114,8 @@ val to_int : t -> int during the conversion. *) val of_int : int -> t -(** Alias to {!Int64.of_int} - NOTE: used to return an option, but the function actually never fails *) +(** Alias to {!Int64.of_int}. + NOTE: used to return an option, but the function actually never fails. *) val of_int_exn : int -> t (** Alias to {!Int64.of_int}. @@ -128,11 +128,11 @@ val to_int32 : t -> int32 during the conversion. *) val of_int32 : int32 -> t -(** Alias to {!Int64.of_int32} +(** Alias to {!Int64.of_int32}. NOTE: use to return an option, but the function actually never fails. *) val of_int32_exn : int32 -> t -(** Alias to {!Int64.of_int32} +(** Alias to {!Int64.of_int32}. @deprecated since NEXT_RELEASE *) val to_nativeint : t -> nativeint @@ -146,7 +146,7 @@ val of_nativeint : nativeint -> t NOTE: use to return an option, but the function actually never fails. *) val of_nativeint_exn : nativeint -> t -(** Alias to {!Int64.of_nativeint} +(** Alias to {!Int64.of_nativeint}. @deprecated since NEXT_RELEASE *) val to_float : t -> float @@ -158,7 +158,7 @@ val of_float : float -> t discarding the fractional part (truncate towards 0). The result of the conversion is undefined if, after truncation, the number is outside the range \[{!CCInt64.min_int}, {!CCInt64.max_int}\]. - NOTE: used to return an option, but the function never fails *) + NOTE: used to return an option, but the function never fails. *) val of_float_exn : float -> t (** Alias to {!Int64.of_float}. @@ -171,7 +171,7 @@ val of_string : string -> t option (** Safe version of {!of_string_exn}. *) val of_string_opt : string -> t option -(** Alias to {!of_string} +(** Alias to {!of_string}. @since NEXT_RELEASE *) val of_string_exn : string -> t diff --git a/src/core/CCList.mli b/src/core/CCList.mli index b80bf76a..a9ed88d2 100644 --- a/src/core/CCList.mli +++ b/src/core/CCList.mli @@ -95,7 +95,7 @@ val init : int -> (int -> 'a) -> 'a t @since 0.6 *) val combine : 'a list -> 'b list -> ('a * 'b) list -(** Similar to {!List.combine} but tail-recursive. +(** Like {!List.combine} but tail-recursive. Transform a pair of lists into a list of pairs: [combine [a1; ...; an] [b1; ...; bn]] is [[(a1,b1); ...; (an,bn)]]. diff --git a/src/core/CCNativeint.mli b/src/core/CCNativeint.mli index e1e72f8d..f1754e30 100644 --- a/src/core/CCNativeint.mli +++ b/src/core/CCNativeint.mli @@ -2,7 +2,7 @@ (** {1 Nativeint} - Helpers for processor-native integers. + Helpers for processor-native integers This module provides operations on the type [nativeint] of signed 32-bit integers (on 32-bit platforms) or signed 64-bit integers (on 64-bit platforms). diff --git a/src/core/CCString.ml b/src/core/CCString.ml index 11291efe..f5004529 100644 --- a/src/core/CCString.ml +++ b/src/core/CCString.ml @@ -44,7 +44,7 @@ module type S = sig val length : t -> int val blit : t -> int -> Bytes.t -> int -> int -> unit - (** Similar to {!String.blit}. + (** Like {!String.blit}. Compatible with the [-safe-string] option. @raise Invalid_argument if indices are not valid *) diff --git a/src/core/CCString.mli b/src/core/CCString.mli index 60de9493..a7da7a5f 100644 --- a/src/core/CCString.mli +++ b/src/core/CCString.mli @@ -19,7 +19,7 @@ module type S = sig (** Return the length (number of characters) of the given string. *) val blit : t -> int -> Bytes.t -> int -> int -> unit - (** Similar to {!String.blit}. + (** Like {!String.blit}. Compatible with the [-safe-string] option. @raise Invalid_argument if indices are not valid. *) @@ -38,25 +38,24 @@ module type S = sig (** {2 Conversions} *) val to_gen : t -> char gen - (** Return the [gen] of characters contained in the string *) + (** Return the [gen] of characters contained in the string. *) val to_seq : t -> char sequence - (** Return the [sequence] of characters contained in the string *) + (** Return the [sequence] of characters contained in the string. *) val to_klist : t -> char klist - (** Return the [klist] of characters contained in the string *) + (** Return the [klist] of characters contained in the string. *) val to_list : t -> char list (** Return the list of characters contained in the string. *) val pp_buf : Buffer.t -> t -> unit - (** Renamed from [pp]. - @since 2.0 *) + (** Renamed from [pp] since 2.0. *) val pp : Format.formatter -> t -> unit (** Print the string within quotes. - Renamed from [print]. - @since 2.0 *) + + Renamed from [print] since 2.0. *) end (** {2 Strings} *) @@ -75,7 +74,7 @@ val is_empty : string -> bool val hash : string -> int val init : int -> (int -> char) -> string -(** Analog to [Array.init]. +(** Like [Array.init]. @since 0.3.3 *) val rev : string -> string @@ -127,7 +126,7 @@ val find_all_l : ?start:int -> sub:string -> string -> int list @since 0.17 *) val mem : ?start:int -> sub:string -> string -> bool -(** [mem ~sub s] is true iff [sub] is a substring of [s]. +(** [mem ~sub s] is [true] iff [sub] is a substring of [s]. @since 0.12 *) val rfind : sub:string -> string -> int @@ -339,10 +338,10 @@ end module Split : sig (** Specification of what to do with empty blocks, as in [split ~by:"-" "-a-b-"]. - - [{first=false; last=false}] will return [""; "a"; "b"; ""]. - - [{first=true; last=false}] will return ["a"; "b" ""]. - - [{first=false; last=true}] will return [""; "a"; "b"]. - - [{first=true; last=true}] will return ["a"; "b"]. + - [{first=false; last=false}] will return [""; "a"; "b"; ""] + - [{first=true; last=false}] will return ["a"; "b" ""] + - [{first=false; last=true}] will return [""; "a"; "b"] + - [{first=true; last=true}] will return ["a"; "b"] The default value of all remaining functions is [Drop_none]. @since 1.5 diff --git a/src/core/CCUtf8_string.ml b/src/core/CCUtf8_string.ml index 27e13898..372271e2 100644 --- a/src/core/CCUtf8_string.ml +++ b/src/core/CCUtf8_string.ml @@ -70,7 +70,7 @@ let next_ (type a) (st : Dec.t) ~(yield:uchar -> a) ~(stop:unit -> a) () : a = assert (n_bytes >= 1); (* is the string long enough to contain the whole codepoint? *) if st.i + n_bytes < st.len then ( - aux 1 acc (* start with j=1, first char is already proccessed! *) + aux 1 acc (* start with j=1, first char is already processed! *) ) else ( (* char is truncated *) malformed st; diff --git a/src/core/CCUtf8_string.mli b/src/core/CCUtf8_string.mli index 9b90a990..4db382be 100644 --- a/src/core/CCUtf8_string.mli +++ b/src/core/CCUtf8_string.mli @@ -32,32 +32,32 @@ val compare : t -> t -> int val pp : Format.formatter -> t -> unit val to_string : t -> string -(** Identity *) +(** Identity. *) exception Malformed of string * int (** Malformed string at given offset *) val to_gen : ?idx:int -> t -> uchar gen (** Generator of unicode codepoints. - @param idx offset where to start the decoding *) + @param idx offset where to start the decoding. *) val to_seq : ?idx:int -> t -> uchar sequence (** Sequence of unicode codepoints. - @param idx offset where to start the decoding *) + @param idx offset where to start the decoding. *) val to_list : ?idx:int -> t -> uchar list (** List of unicode codepoints. - @param idx offset where to start the decoding *) + @param idx offset where to start the decoding. *) val fold : ?idx:int -> ('a -> uchar -> 'a) -> 'a -> t -> 'a val iter : ?idx:int -> (uchar -> unit) -> t -> unit val n_chars : t -> int -(** Number of characters *) +(** Number of characters. *) val n_bytes : t -> int -(** Number of bytes *) +(** Number of bytes. *) val map : (uchar -> uchar) -> t -> t @@ -76,16 +76,16 @@ val of_gen : uchar gen -> t val of_list : uchar list -> t val of_string_exn : string -> t -(** Validate string by checking it is valid UTF8 - @raise Invalid_argument if the string is not valid UTF8 *) +(** Validate string by checking it is valid UTF8. + @raise Invalid_argument if the string is not valid UTF8. *) val of_string : string -> t option -(** Safe version of {!of_string_exn} *) +(** Safe version of {!of_string_exn}. *) val is_valid : string -> bool (** Valid UTF8? *) val unsafe_of_string : string -> t (** Conversion from a string without validating. - Upon iteration, if an invalid substring is met, Malformed will be raised *) + Upon iteration, if an invalid substring is met, Malformed will be raised. *) diff --git a/src/data/CCBV.mli b/src/data/CCBV.mli index be0b3053..d6c60609 100644 --- a/src/data/CCBV.mli +++ b/src/data/CCBV.mli @@ -51,7 +51,7 @@ val set : t -> int -> unit (** Set i-th bit, extending the bitvector if needed. *) val get : t -> int -> bool -(** Is the i-th bit true? Returns false if the index is too high. *) +(** Is the i-th bit true? Return false if the index is too high. *) val reset : t -> int -> unit (** Set i-th bit to 0, extending the bitvector if needed. *) @@ -82,8 +82,8 @@ val of_list : int list -> t bitvector will have [length t] equal to 1 more than max of list indices. *) val first : t -> int option -(** First set bit, or return None. - changed type at 1.2 *) +(** First set bit, or return [None]. + Changed type at 1.2 *) val first_exn : t -> int (** First set bit, or diff --git a/src/data/CCCache.mli b/src/data/CCCache.mli index b2e7b590..e4caee2d 100644 --- a/src/data/CCCache.mli +++ b/src/data/CCCache.mli @@ -72,7 +72,7 @@ val iter : ('a,'b) t -> ('a -> 'b -> unit) -> unit (** Iterate on cached values. Should yield [size cache] pairs. *) val add : ('a, 'b) t -> 'a -> 'b -> bool -(** Manually add a cached value. Returns [true] if the value has successfully +(** Manually add a cached value. Return [true] if the value has successfully been added, and [false] if the value was already bound. @since 1.5 *) diff --git a/src/data/CCDeque.mli b/src/data/CCDeque.mli index ea8acd33..a214dd9a 100644 --- a/src/data/CCDeque.mli +++ b/src/data/CCDeque.mli @@ -4,7 +4,7 @@ (** {1 Imperative deque} This structure provides fast access to its front and back elements, - with O(1) operations *) + with O(1) operations. *) type 'a t (** Contains 'a elements, queue in both ways *) @@ -79,7 +79,7 @@ type 'a sequence = ('a -> unit) -> unit val of_seq : 'a sequence -> 'a t (** Create a deque from the sequence. - optional argument [deque] disappears, use {!add_seq_back} instead since + Optional argument [deque] disappears, use {!add_seq_back} instead since 0.13 *) val to_seq : 'a t -> 'a sequence @@ -90,7 +90,7 @@ val of_gen : 'a gen -> 'a t @since 0.13 *) val to_gen : 'a t -> 'a gen -(** Iterate on elements of the deque. +(** Iterate on the elements of the deque. @since 0.13 *) val add_seq_front : 'a t -> 'a sequence -> unit diff --git a/src/data/CCFun_vec.mli b/src/data/CCFun_vec.mli index 06c252e5..a536c101 100644 --- a/src/data/CCFun_vec.mli +++ b/src/data/CCFun_vec.mli @@ -62,7 +62,7 @@ val return : 'a -> 'a t val length : _ t -> int val push : 'a -> 'a t -> 'a t -(** Add element at the end *) +(** Add element at the end. *) val get : int -> 'a t -> 'a option @@ -70,15 +70,15 @@ val get_exn : int -> 'a t -> 'a (** @raise Not_found if key not present. *) val pop_exn : 'a t -> 'a * 'a t -(** Pop last element *) +(** Pop last element. *) val iter : f:('a -> unit) -> 'a t -> unit val iteri : f:(int -> 'a -> unit) -> 'a t -> unit -(** Iterate on elements with their index, in increasing order *) +(** Iterate on elements with their index, in increasing order. *) val iteri_rev : f:(int -> 'a -> unit) -> 'a t -> unit -(** Iterate on elements with their index, but starting from the end *) +(** Iterate on elements with their index, but starting from the end. *) val fold : f:('b -> 'a -> 'b) -> x:'b -> 'a t -> 'b diff --git a/src/data/CCGraph.mli b/src/data/CCGraph.mli index 95f6a9d6..469067ec 100644 --- a/src/data/CCGraph.mli +++ b/src/data/CCGraph.mli @@ -118,7 +118,7 @@ module Traverse : sig graph:('v, 'e) t -> 'v sequence -> 'v sequence_once - (** One-shot traversal of the graph using a tag set and the given bag *) + (** One-shot traversal of the graph using a tag set and the given bag. *) val dfs: tbl:'v set -> graph:('v, 'e) t -> @@ -147,7 +147,7 @@ module Traverse : sig ('v * int * ('v,'e) path) sequence_once (** Dijkstra algorithm, traverses a graph in increasing distance order. Yields each vertex paired with its distance to the set of initial vertices - (the smallest distance needed to reach the node from the initial vertices) + (the smallest distance needed to reach the node from the initial vertices). @param dist distance from origin of the edge to destination, must be strictly positive. Default is 1 for every edge. *) diff --git a/src/data/CCHet.mli b/src/data/CCHet.mli index 2bb400c1..f6a88f7b 100644 --- a/src/data/CCHet.mli +++ b/src/data/CCHet.mli @@ -1,7 +1,7 @@ (* This file is free software, part of containers. See file "license" for more details. *) -(** {1 Associative containers with Heterogenerous Values} +(** {1 Associative containers with Heterogeneous Values} This is similar to {!CCMixtbl}, but the injection is directly used as a key. diff --git a/src/data/CCMixset.mli b/src/data/CCMixset.mli index e51eee5c..620b6abb 100644 --- a/src/data/CCMixset.mli +++ b/src/data/CCMixset.mli @@ -35,7 +35,7 @@ val newkey : unit -> 'a key Not thread-safe. *) val empty : t -(** Empty set *) +(** Empty set. *) val set : key:'a key -> 'a -> t -> t (** [set ~key v set] maps [key] to [v] in [set]. It means that diff --git a/src/data/CCMultiMap.mli b/src/data/CCMultiMap.mli index 86ea1788..556e9794 100644 --- a/src/data/CCMultiMap.mli +++ b/src/data/CCMultiMap.mli @@ -52,7 +52,7 @@ module type S = sig (** Intersection of multimaps. *) val diff : t -> t -> t - (** Difference of maps, ie bindings of the first that are not + (** Difference of maps, i.e. bindings of the first that are not in the second. *) val equal : t -> t -> bool @@ -62,7 +62,7 @@ module type S = sig (** Total order on multimaps. *) val submap : t -> t -> bool - (** [submap m1 m2] is true iff all bindings of [m1] are also in [m2]. *) + (** [submap m1 m2] is [true] iff all bindings of [m1] are also in [m2]. *) val to_seq : t -> (key * value) sequence diff --git a/src/data/CCPersistentArray.mli b/src/data/CCPersistentArray.mli index 7ed20503..a325dff1 100644 --- a/src/data/CCPersistentArray.mli +++ b/src/data/CCPersistentArray.mli @@ -63,14 +63,14 @@ val set : 'a t -> int -> 'a -> 'a t range [0] to [Array.length a - 1]. *) val length : 'a t -> int -(** Returns the length of the persistent array. *) +(** Return the length of the persistent array. *) val copy : 'a t -> 'a t (** [copy a] returns a fresh copy of [a]. Both copies are independent. *) val map : ('a -> 'b) -> 'a t -> 'b t val mapi : (int -> 'a -> 'b) -> 'a t -> 'b t -(** Applies the given function to all elements of the array, and returns +(** Apply the given function to all elements of the array, and return a persistent array initialized by the results of f. In the case of [mapi], the function is also given the index of the element. It is equivalent to [fun f t -> init (fun i -> f (get t i))]. *) diff --git a/src/data/CCRAL.mli b/src/data/CCRAL.mli index 36c48fdc..3d9721d3 100644 --- a/src/data/CCRAL.mli +++ b/src/data/CCRAL.mli @@ -171,7 +171,7 @@ module Infix : sig (** Alias to {!map}. *) val (<*>) : ('a -> 'b) t -> 'a t -> 'b t - (** Alias to {!app} *) + (** Alias to {!app}. *) val (--) : int -> int -> int t (** Alias to {!range}. *) diff --git a/src/data/CCRingBuffer.mli b/src/data/CCRingBuffer.mli index e5f77378..a8384e37 100644 --- a/src/data/CCRingBuffer.mli +++ b/src/data/CCRingBuffer.mli @@ -81,7 +81,7 @@ module type S = sig (** [create size] creates a new bounded buffer with given size. The underlying array is allocated immediately and no further (large) allocation will happen from now on. - @raise Invalid_argument if the arguments is [< 1]. *) + @raise Invalid_argument if the argument is [< 1]. *) val copy : t -> t (** Make a fresh copy of the buffer. *) @@ -93,12 +93,12 @@ module type S = sig (** Number of elements currently stored in the buffer. *) val is_full : t -> bool - (** true if pushing an element would erase another element. + (** [true] if pushing an element would erase another element. @since 1.3 *) val blit_from : t -> Array.t -> int -> int -> unit (** [blit_from buf from_buf o len] copies the slice [o, ... o + len - 1] from - a input buffer [from_buf] to the end of the buffer. + an input buffer [from_buf] to the end of the buffer. If the slice is too large for the buffer, only the last part of the array will be copied. @raise Invalid_argument if [o,len] is not a valid slice of [s]. *) @@ -142,12 +142,12 @@ module type S = sig being its relative index within [buf]. *) val get_front : t -> int -> Array.elt - (** [get_front buf i] returns the [i]-th element of [buf] from the front, ie + (** [get_front buf i] returns the [i]-th element of [buf] from the front, i.e. the one returned by [take_front buf] after [i-1] calls to [junk_front buf]. @raise Invalid_argument if the index is invalid (> [length buf]). *) val get_back : t -> int -> Array.elt - (** [get_back buf i] returns the [i]-th element of [buf] from the back, ie + (** [get_back buf i] returns the [i]-th element of [buf] from the back, i.e. the one returned by [take_back buf] after [i-1] calls to [junk_back buf]. @raise Invalid_argument if the index is invalid (> [length buf]). *) @@ -199,7 +199,7 @@ end (** An efficient byte based ring buffer *) module Byte : S with module Array = Array.Byte -(** Makes a ring buffer module with the given array type. *) +(** Makes a ring buffer module with the given array type *) module MakeFromArray(A : Array.S) : S with module Array = A (** Buffer using regular arrays *) diff --git a/src/data/CCWBTree.ml b/src/data/CCWBTree.ml index 2979b362..a64930c9 100644 --- a/src/data/CCWBTree.ml +++ b/src/data/CCWBTree.ml @@ -121,7 +121,7 @@ module type S = sig and [o = Some v] if [k, v] belonged to the map *) val merge : f:(key -> 'a option -> 'b option -> 'c option) -> 'a t -> 'b t -> 'c t - (** Similar to {!Map.S.merge} *) + (** Like {!Map.S.merge} *) val extract_min : 'a t -> key * 'a * 'a t (** [extract_min m] returns [k, v, m'] where [k,v] is the pair with the diff --git a/src/data/CCWBTree.mli b/src/data/CCWBTree.mli index 58e6b188..c62ce15e 100644 --- a/src/data/CCWBTree.mli +++ b/src/data/CCWBTree.mli @@ -86,7 +86,7 @@ module type S = sig and [o = Some v] if [k, v] belonged to the map. *) val merge : f:(key -> 'a option -> 'b option -> 'c option) -> 'a t -> 'b t -> 'c t - (** Similar to {!Map.S.merge}. *) + (** Like {!Map.S.merge}. *) val extract_min : 'a t -> key * 'a * 'a t (** [extract_min m] returns [k, v, m'] where [k,v] is the pair with the diff --git a/src/data/CCZipper.mli b/src/data/CCZipper.mli index 9d3ea4ae..411eb302 100644 --- a/src/data/CCZipper.mli +++ b/src/data/CCZipper.mli @@ -7,13 +7,13 @@ type 'a t = 'a list * 'a list (** The pair [l, r] represents the list [List.rev_append l r], but - with the focus on [r]. *) + with the focus on [r] *) val empty : 'a t (** Empty zipper. *) val is_empty : _ t -> bool -(** Empty zipper? Returns true iff the two lists are empty. *) +(** Empty zipper? Returns [true] iff the two lists are empty. *) val to_list : 'a t -> 'a list (** Convert the zipper back to a list. diff --git a/src/iter/CCKList.mli b/src/iter/CCKList.mli index a7061d86..1e95cd03 100644 --- a/src/iter/CCKList.mli +++ b/src/iter/CCKList.mli @@ -155,22 +155,22 @@ val merge : 'a ord -> 'a t -> 'a t -> 'a t (** Merge two sorted iterators into a sorted iterator. *) val zip : 'a t -> 'b t -> ('a * 'b) t -(** Combine elements pairwise. Stops as soon as one of the lists stops. +(** Combine elements pairwise. Stop as soon as one of the lists stops. @since 0.13 *) val unzip : ('a * 'b) t -> 'a t * 'b t -(** Splits each tuple in the list. +(** Split each tuple in the list. @since 0.13 *) (** {2 Misc} *) val sort : cmp:'a ord -> 'a t -> 'a t -(** Eager sort. Requires the iterator to be finite. [O(n ln(n))] time +(** Eager sort. Require the iterator to be finite. [O(n ln(n))] time and space. @since 0.3.3 *) val sort_uniq : cmp:'a ord -> 'a t -> 'a t -(** Eager sort that removes duplicate values. Requires the iterator to be +(** Eager sort that removes duplicate values. Require the iterator to be finite. [O(n ln(n))] time and space. @since 0.3.3 *) @@ -243,14 +243,14 @@ end val of_list : 'a list -> 'a t val to_list : 'a t -> 'a list -(** Gather all values into a list *) +(** Gather all values into a list. *) val of_array : 'a array -> 'a t (** Iterate on the array. @since 0.13 *) val to_array : 'a t -> 'a array -(** Convert into array. Iterates twice. +(** Convert into array. Iterate twice. @since 0.13 *) val to_rev_list : 'a t -> 'a list @@ -268,4 +268,4 @@ val of_gen : 'a gen -> 'a t val pp : ?sep:string -> 'a printer -> 'a t printer (** Print the list with the given separator (default ","). - Does not print opening/closing delimiters. *) + Do not print opening/closing delimiters. *) diff --git a/src/iter/CCKTree.mli b/src/iter/CCKTree.mli index c732f6a0..3b161bae 100644 --- a/src/iter/CCKTree.mli +++ b/src/iter/CCKTree.mli @@ -91,7 +91,7 @@ val find : pset:'a pset -> ('a -> 'b option) -> 'a t -> 'b option | `Cst n -> Format.fprintf fmt "%d" n | `Plus n -> Format.fprintf fmt "%d" n;; - Format.printf "%a@." (CCKTree.print pp_node) (fib 8);; + Format.printf "%a@." (CCKTree.pp pp_node) (fib 8);; ]} *) @@ -114,7 +114,7 @@ module Dot : sig ] (** Dot attributes for nodes *) type graph = (string * attribute list t list) - (** A dot graph is a name, plus a list of trees labelled with attributes. *) + (** A dot graph is a name, plus a list of trees labelled with attributes *) val mk_id : ('a, Buffer.t, unit, attribute) format4 -> 'a (** Using a formatter string, build an ID. *) diff --git a/src/iter/CCLazy_list.mli b/src/iter/CCLazy_list.mli index bada7437..fae64d50 100644 --- a/src/iter/CCLazy_list.mli +++ b/src/iter/CCLazy_list.mli @@ -17,7 +17,7 @@ val return : 'a -> 'a t (** Return a computed value. *) val is_empty : _ t -> bool -(** Evaluates the head. *) +(** Evaluate the head. *) val length : _ t -> int (** [length l] returns the number of elements in [l], eagerly (linear time). diff --git a/src/threads/CCLock.mli b/src/threads/CCLock.mli index f06ec7c1..2b679e3d 100644 --- a/src/threads/CCLock.mli +++ b/src/threads/CCLock.mli @@ -24,7 +24,7 @@ val try_with_lock : 'a t -> ('a -> 'b) -> 'b option fails, [try_with_lock l f] fails too but the lock is released. @since 0.22 *) -(** Type allowing to manipulate the lock as a reference +(** Type allowing to manipulate the lock as a reference. @since 0.13 *) module LockRef : sig type 'a t diff --git a/src/threads/CCPool.mli b/src/threads/CCPool.mli index f915c7d6..69a174d6 100644 --- a/src/threads/CCPool.mli +++ b/src/threads/CCPool.mli @@ -3,7 +3,7 @@ (** {1 Thread Pool, and Futures} - Renamed and heavily updated from [CCFuture] + Renamed and heavily updated from [CCFuture]. @since 0.16 *) type +'a state = @@ -36,7 +36,7 @@ module Make(P : PARAM) : sig (** [active ()] is true as long as [stop()] has not been called yet. *) val stop : unit -> unit - (** After calling [stop ()], Most functions will raise Stopped. + (** After calling [stop ()], most functions will raise Stopped. This has the effect of preventing new tasks from being executed. *) (** {6 Futures} @@ -45,7 +45,7 @@ module Make(P : PARAM) : sig that are executed in the pool using {!run}. *) module Fut : sig type 'a t - (** A future value of type 'a *) + (** A future value of type ['a] *) type 'a future = 'a t @@ -70,7 +70,7 @@ module Make(P : PARAM) : sig val get : 'a t -> 'a (** Blocking get: wait for the future to be evaluated, and get the value, or the exception that failed the future is returned. - raise e if the future failed with e. *) + Raise e if the future failed with e. *) val state : 'a t -> 'a state (** State of the future. *) @@ -106,7 +106,7 @@ module Make(P : PARAM) : sig in the array fails, [sequence_a l] fails too. *) val map_a : ('a -> 'b t) -> 'a array -> 'b array t - (** [map_l f a] maps [f] on every element of [a], and will return + (** [map_a f a] maps [f] on every element of [a], and will return the array of every result if all calls succeed, or an error otherwise. *) val sequence_l : 'a t list -> 'a list t diff --git a/src/threads/CCTimer.mli b/src/threads/CCTimer.mli index 6a2db7e5..a8ad53cd 100644 --- a/src/threads/CCTimer.mli +++ b/src/threads/CCTimer.mli @@ -3,7 +3,7 @@ (** {1 Event timer} - Used to be part of [CCFuture] + Used to be part of [CCFuture]. @since 0.16 *) type t From 2c9ed9c5503691538a4e2357bbbff2ea6efbce81 Mon Sep 17 00:00:00 2001 From: JPR Date: Thu, 15 Mar 2018 19:33:18 +0100 Subject: [PATCH 48/60] Reverting CCFormat.mli comment --- src/core/CCFormat.mli | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index 449fa8c7..bdf2f394 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -56,7 +56,7 @@ val flush : unit printer @since 1.2 *) val string_quoted : string printer -(** Like {!CCString.print}. +(** Similar to {!CCString.print}. @since 0.14 *) val list : ?sep:unit printer -> 'a printer -> 'a list printer From f07cae6c82807ea1b415cfcecb2f634b3dc57e2f Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Sun, 18 Mar 2018 21:53:24 -0500 Subject: [PATCH 49/60] detail --- src/data/CCBitField.mli | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/data/CCBitField.mli b/src/data/CCBitField.mli index 9607e6f4..b5f29d2e 100644 --- a/src/data/CCBitField.mli +++ b/src/data/CCBitField.mli @@ -9,8 +9,6 @@ {[ module B = CCBitField.Make(struct end);; - #install_printer B.pp;; - let x = B.mk_field () let y = B.mk_field () let z = B.mk_field () From 323a6bb40afbc0a41a04dbf7d9efd7b12355f991 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 26 Mar 2018 11:35:21 -0500 Subject: [PATCH 50/60] test: regression test for #210 --- src/core/CCRandom.ml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/CCRandom.ml b/src/core/CCRandom.ml index 2879540e..ded871ff 100644 --- a/src/core/CCRandom.ml +++ b/src/core/CCRandom.ml @@ -228,3 +228,8 @@ let uniformity_test ?(size_hint=10) k rng st = (*$T split_list run ~st:(QCheck_runner.random_state()) ( uniformity_test 50_000 (split_list 10 ~len:3) ) *) + +(*$R + let open Containers in + ignore @@ List.random_choose [1;2;3] (Random.get_state()) +*) From fe23cb496cd2a8f3d730e7e31808739a0c2be3fd Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Mon, 26 Mar 2018 11:33:40 -0500 Subject: [PATCH 51/60] fix: strong type aliases in Random (closes #210) --- src/core/CCChar.mli | 2 +- src/core/CCRandom.mli | 2 +- src/core/CCResult.mli | 2 +- src/core/CCString.mli | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/CCChar.mli b/src/core/CCChar.mli index ab1a1b5f..974e9096 100644 --- a/src/core/CCChar.mli +++ b/src/core/CCChar.mli @@ -4,7 +4,7 @@ @since 0.14 *) -include module type of Char +include module type of struct include Char end val equal : t -> t -> bool (** The equal function for chars. *) diff --git a/src/core/CCRandom.mli b/src/core/CCRandom.mli index 7d173711..b9c55a5c 100644 --- a/src/core/CCRandom.mli +++ b/src/core/CCRandom.mli @@ -3,7 +3,7 @@ (** {1 Random Generators} *) -include module type of Random +include module type of struct include Random end type state = Random.State.t diff --git a/src/core/CCResult.mli b/src/core/CCResult.mli index 754a215b..4f402d4a 100644 --- a/src/core/CCResult.mli +++ b/src/core/CCResult.mli @@ -14,7 +14,7 @@ type 'a printer = Format.formatter -> 'a -> unit (** {2 Basics} *) -include module type of Result +include module type of struct include Result end (** @since 1.5 *) type (+'good, +'bad) t = ('good, 'bad) Result.result = diff --git a/src/core/CCString.mli b/src/core/CCString.mli index a7da7a5f..1a551de0 100644 --- a/src/core/CCString.mli +++ b/src/core/CCString.mli @@ -60,7 +60,7 @@ end (** {2 Strings} *) -include module type of String +include module type of struct include String end val equal : string -> string -> bool (** Equality function on strings. *) From 712b12d2f1ebdb78f9dbe3315d4a6ff31eb67900 Mon Sep 17 00:00:00 2001 From: Stavros Polymenis Date: Fri, 23 Feb 2018 00:21:57 +0000 Subject: [PATCH 52/60] Add Bijection. https://en.wikipedia.org/wiki/Bijection Discussion: http://lists.ocaml.org/pipermail/containers-users/2018-February/000042.html --- src/data/CCBijection.ml | 91 ++++++++++++++++++++++++++++++++++++++++ src/data/CCBijection.mli | 65 ++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 src/data/CCBijection.ml create mode 100644 src/data/CCBijection.mli diff --git a/src/data/CCBijection.ml b/src/data/CCBijection.ml new file mode 100644 index 00000000..e3f5dfba --- /dev/null +++ b/src/data/CCBijection.ml @@ -0,0 +1,91 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 Bijection} *) +module type OrderedType = sig + type t + val compare : t -> t -> int +end + +module type S = sig + type t + type left + type right + + val empty : t + val is_empty : t -> bool + val add : left -> right -> t -> t + val mem : left -> right -> t -> bool + val mem_left : left -> t -> bool + val mem_right : right -> t -> bool + val find_left : left -> t -> right + val find_right : right -> t -> left + val remove : left -> right -> t -> t + val remove_left : left -> t -> t + val remove_right : right -> t -> t + val list_left : t -> (left * right) list + val list_right : t -> (right * left) list +end + +module Make(L : OrderedType)(R : OrderedType) = struct + type left = L.t + type right = R.t + + module MapL = Map.Make(L) + module MapR = Map.Make(R) + + exception Incoherence of string + + type t = { + left : right MapL.t; + right : left MapR.t; + } + + let empty = { + left = MapL.empty; + right = MapR.empty; + } + + let is_empty m = match MapL.is_empty m.left, MapR.is_empty m.right with + | l, r when l = r -> l + | l, r -> raise (Incoherence ("is_empty left: " ^ string_of_bool l ^ ", right: " ^ string_of_bool r)) + + let add a b m = { + left = + (try let found = MapR.find b m.right in + if L.compare found a <> 0 then MapL.remove found m.left else m.left + with Not_found -> m.left) + |> MapL.add a b; + right = + (try let found = MapL.find a m.left in + if R.compare found b <> 0 then MapR.remove found m.right else m.right + with Not_found -> m.right) + |> MapR.add b a; + } + + let find_left key m = MapL.find key m.left + let find_right key m = MapR.find key m.right + + let mem left right m = try R.compare right (find_left left m) = 0 with Not_found -> false + let mem_left key m = MapL.mem key m.left + let mem_right key m = MapR.mem key m.right + + let remove a b m = + if mem a b m then + { + left = MapL.remove a m.left; + right = MapR.remove b m.right; + } + else m + + let remove_left a m = + let right = try MapR.remove (find_left a m) m.right with Not_found -> m.right in + { right; left = MapL.remove a m.left } + + let remove_right b m = + let left = try MapL.remove (find_right b m) m.left with Not_found -> m.left in + { left; right = MapR.remove b m.right } + + let list_left m = MapL.bindings m.left + let list_right m = MapR.bindings m.right + +end diff --git a/src/data/CCBijection.mli b/src/data/CCBijection.mli new file mode 100644 index 00000000..8aaf14cb --- /dev/null +++ b/src/data/CCBijection.mli @@ -0,0 +1,65 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +(** {1 Bijection} + Represents 1-to-1 mappings between two types. Each element from the "left" + is mapped to one "right" value, and conversely. + + @since NEXT_RELEASE *) + +module type OrderedType = sig + type t + val compare : t -> t -> int +end + +module type S = sig + type t + type left + type right + + val empty : t + + val is_empty : t -> bool + + val add : left -> right -> t -> t + (** Add [left] and [right] correspondence to bijection such that + [left] and [right] are unique in their respective sets and only + correspond to each other. *) + + val mem : left -> right -> t -> bool + (** Checks both sides for key membership. Can raise [Incoherence of + string] but should never happen *) + + val mem_left : left -> t -> bool + (** Checks for membership of correspondence using [left] key *) + + val mem_right : right -> t -> bool + (** Checks for membership of correspondence using [right] key *) + + val find_left : left -> t -> right + (** Raises [Not_found] if left is not found *) + + val find_right : right -> t -> left + (** Raises [Not_found] if right is not found *) + + val remove : left -> right -> t -> t + (** Removes the [left], [right] binding if it exists. Returns the + same bijection otherwise. *) + + val remove_left : left -> t -> t + (** Remove the binding with [left] key if it exists. Returns the + same bijection otheriwse *) + + val remove_right : right -> t -> t + (** Remove the binding with [right] key if it exists. Returns the + same bijection otheriwse *) + + val list_left : t -> (left * right) list + (** returns the bindings as a list of ([left], [right]) values *) + + val list_right : t -> (right * left) list + (** returns the bindings as a list of ([right, [left]) values *) + +end + +module Make(L : OrderedType)(R : OrderedType) : S + with type left = L.t and type right = R.t From 89ce86eec09e86285bee5ef24cc1f9e0335f2388 Mon Sep 17 00:00:00 2001 From: Stavros Polymenis Date: Tue, 27 Mar 2018 23:05:34 +0100 Subject: [PATCH 53/60] Assert emptiness on both maps instead of raising custom exception. https://github.com/c-cube/ocaml-containers/pull/211\#pullrequestreview-107483136 --- src/data/CCBijection.ml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/data/CCBijection.ml b/src/data/CCBijection.ml index e3f5dfba..306d28c7 100644 --- a/src/data/CCBijection.ml +++ b/src/data/CCBijection.ml @@ -45,9 +45,10 @@ module Make(L : OrderedType)(R : OrderedType) = struct right = MapR.empty; } - let is_empty m = match MapL.is_empty m.left, MapR.is_empty m.right with - | l, r when l = r -> l - | l, r -> raise (Incoherence ("is_empty left: " ^ string_of_bool l ^ ", right: " ^ string_of_bool r)) + let is_empty m = + let res = MapL.is_empty m.left in + assert (res = MapR.is_empty m.right); + res let add a b m = { left = From b874ff9bf9f122decf428284e4d1e5bdf59438c0 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 28 Mar 2018 19:46:43 -0500 Subject: [PATCH 54/60] feat(Bijection): add more functions, add basic tests --- src/data/CCBijection.ml | 45 ++++++++++++++++++++++++++++++++++++++-- src/data/CCBijection.mli | 31 +++++++++++++++++++++------ 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/src/data/CCBijection.ml b/src/data/CCBijection.ml index 306d28c7..cfb63537 100644 --- a/src/data/CCBijection.ml +++ b/src/data/CCBijection.ml @@ -1,6 +1,9 @@ (* This file is free software, part of containers. See file "license" for more details. *) (** {1 Bijection} *) + +type 'a sequence = ('a -> unit) -> unit + module type OrderedType = sig type t val compare : t -> t -> int @@ -13,7 +16,10 @@ module type S = sig val empty : t val is_empty : t -> bool + val equal : t -> t -> bool + val compare : t -> t -> int val add : left -> right -> t -> t + val cardinal : t -> int val mem : left -> right -> t -> bool val mem_left : left -> t -> bool val mem_right : right -> t -> bool @@ -24,6 +30,12 @@ module type S = sig val remove_right : right -> t -> t val list_left : t -> (left * right) list val list_right : t -> (right * left) list + val add_seq : (left * right) sequence -> t -> t + val of_seq : (left * right) sequence -> t + val to_seq : t -> (left * right) sequence + val add_list : (left * right) list -> t -> t + val of_list : (left * right) list -> t + val to_list : t -> (left * right) list end module Make(L : OrderedType)(R : OrderedType) = struct @@ -33,8 +45,6 @@ module Make(L : OrderedType)(R : OrderedType) = struct module MapL = Map.Make(L) module MapR = Map.Make(R) - exception Incoherence of string - type t = { left : right MapL.t; right : left MapR.t; @@ -45,11 +55,16 @@ module Make(L : OrderedType)(R : OrderedType) = struct right = MapR.empty; } + let cardinal m = MapL.cardinal m.left + let is_empty m = let res = MapL.is_empty m.left in assert (res = MapR.is_empty m.right); res + let equal a b = MapL.equal (fun a b -> R.compare a b = 0) a.left b.left + let compare a b = MapL.compare R.compare a.left b.left + let add a b m = { left = (try let found = MapR.find b m.right in @@ -89,4 +104,30 @@ module Make(L : OrderedType)(R : OrderedType) = struct let list_left m = MapL.bindings m.left let list_right m = MapR.bindings m.right + let add_list l m = List.fold_left (fun m (a,b) -> add a b m) m l + let of_list l = add_list l empty + let to_list = list_left + + let add_seq seq m = + let m = ref m in + seq (fun (k,v) -> m := add k v !m); + !m + + let of_seq l = add_seq l empty + + let to_seq m yield = MapL.iter (fun k v -> yield (k,v)) m.left end + +(*$inject + open Containers + module M = Make(Int)(String) + +*) + +(*$= + 2 (M.of_list [1,"1"; 2, "2"] |> M.cardinal) + "1" (M.of_list [1,"1"; 2, "2"] |> M.find_left 1) + "2" (M.of_list [1,"1"; 2, "2"] |> M.find_left 2) + 1 (M.of_list [1,"1"; 2, "2"] |> M.find_right "1") + 2 (M.of_list [1,"1"; 2, "2"] |> M.find_right "2") +*) diff --git a/src/data/CCBijection.mli b/src/data/CCBijection.mli index 8aaf14cb..9ee7388a 100644 --- a/src/data/CCBijection.mli +++ b/src/data/CCBijection.mli @@ -6,6 +6,8 @@ @since NEXT_RELEASE *) +type 'a sequence = ('a -> unit) -> unit + module type OrderedType = sig type t val compare : t -> t -> int @@ -20,14 +22,20 @@ module type S = sig val is_empty : t -> bool + val equal : t -> t -> bool + + val compare : t -> t -> int + val add : left -> right -> t -> t (** Add [left] and [right] correspondence to bijection such that [left] and [right] are unique in their respective sets and only correspond to each other. *) + val cardinal : t -> int + (** Number of bindings. O(n) time *) + val mem : left -> right -> t -> bool - (** Checks both sides for key membership. Can raise [Incoherence of - string] but should never happen *) + (** Checks both sides for key membership. *) val mem_left : left -> t -> bool (** Checks for membership of correspondence using [left] key *) @@ -36,10 +44,10 @@ module type S = sig (** Checks for membership of correspondence using [right] key *) val find_left : left -> t -> right - (** Raises [Not_found] if left is not found *) + (** @raise Not_found if left is not found *) val find_right : right -> t -> left - (** Raises [Not_found] if right is not found *) + (** @raise Not_found if right is not found *) val remove : left -> right -> t -> t (** Removes the [left], [right] binding if it exists. Returns the @@ -47,11 +55,11 @@ module type S = sig val remove_left : left -> t -> t (** Remove the binding with [left] key if it exists. Returns the - same bijection otheriwse *) + same bijection otherwise *) val remove_right : right -> t -> t (** Remove the binding with [right] key if it exists. Returns the - same bijection otheriwse *) + same bijection otherwise *) val list_left : t -> (left * right) list (** returns the bindings as a list of ([left], [right]) values *) @@ -59,6 +67,17 @@ module type S = sig val list_right : t -> (right * left) list (** returns the bindings as a list of ([right, [left]) values *) + val add_seq : (left * right) sequence -> t -> t + + val of_seq : (left * right) sequence -> t + + val to_seq : t -> (left * right) sequence + + val add_list : (left * right) list -> t -> t + + val of_list : (left * right) list -> t + + val to_list : t -> (left * right) list end module Make(L : OrderedType)(R : OrderedType) : S From 972a6f27203ee4608e8faeb2a00556347d441a75 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 28 Mar 2018 19:46:57 -0500 Subject: [PATCH 55/60] style: reindent --- src/core/CCFormat.mli | 2 +- src/core/CCString.ml | 2 +- src/core/CCUtf8_string.ml | 6 ++-- src/data/CCBijection.ml | 36 +++++++++++----------- src/data/CCBijection.mli | 6 ++-- src/data/CCFun_vec.ml | 34 ++++++++++----------- src/data/CCFun_vec.mli | 64 +++++++++++++++++++-------------------- src/iter/CCLazy_list.ml | 4 +-- 8 files changed, 77 insertions(+), 77 deletions(-) diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index bdf2f394..f7650483 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -258,7 +258,7 @@ val sprintf_no_color : ('a, t, unit, string) format4 -> 'a val sprintf_dyn_color : colors:bool -> ('a, t, unit, string) format4 -> 'a (** Like {!sprintf} but enable/disable colors depending on [colors]. - + Example: {[ (* with colors *) diff --git a/src/core/CCString.ml b/src/core/CCString.ml index f5004529..518ffd06 100644 --- a/src/core/CCString.ml +++ b/src/core/CCString.ml @@ -84,7 +84,7 @@ let rev s = (*$Q Q.printable_string (fun s -> \ rev s = (to_list s |> List.rev |> of_list)) - *) +*) (*$= diff --git a/src/core/CCUtf8_string.ml b/src/core/CCUtf8_string.ml index 372271e2..60ccf1b5 100644 --- a/src/core/CCUtf8_string.ml +++ b/src/core/CCUtf8_string.ml @@ -237,7 +237,7 @@ let of_string s = if is_valid s then Some s else None let printer s = String.escaped (to_string s) let pp_uchar (c:Uchar.t) = Printf.sprintf "0x%x" (Uchar.to_int c) -let uutf_is_valid s = + let uutf_is_valid s = try Uutf.String.fold_utf_8 (fun () _ -> function @@ -248,7 +248,7 @@ let uutf_is_valid s = with Exit -> false -let uutf_to_seq s f = + let uutf_to_seq s f = Uutf.String.fold_utf_8 (fun () _ -> function | `Malformed _ -> f (Uchar.of_int 0xfffd) @@ -331,4 +331,4 @@ let uutf_to_seq s f = else Q.Test.fail_reportf "uutf: '%s', containers: '%s', is_valid %B, uutf_is_valid %B" (pp l_uutf) (pp l_co) (is_valid s) (uutf_is_valid s) ) - *) +*) diff --git a/src/data/CCBijection.ml b/src/data/CCBijection.ml index cfb63537..3b2b1e3b 100644 --- a/src/data/CCBijection.ml +++ b/src/data/CCBijection.ml @@ -46,14 +46,14 @@ module Make(L : OrderedType)(R : OrderedType) = struct module MapR = Map.Make(R) type t = { - left : right MapL.t; - right : left MapR.t; - } + left : right MapL.t; + right : left MapR.t; + } let empty = { - left = MapL.empty; - right = MapR.empty; - } + left = MapL.empty; + right = MapR.empty; + } let cardinal m = MapL.cardinal m.left @@ -66,17 +66,17 @@ module Make(L : OrderedType)(R : OrderedType) = struct let compare a b = MapL.compare R.compare a.left b.left let add a b m = { - left = - (try let found = MapR.find b m.right in - if L.compare found a <> 0 then MapL.remove found m.left else m.left - with Not_found -> m.left) - |> MapL.add a b; - right = - (try let found = MapL.find a m.left in - if R.compare found b <> 0 then MapR.remove found m.right else m.right - with Not_found -> m.right) - |> MapR.add b a; - } + left = + (try let found = MapR.find b m.right in + if L.compare found a <> 0 then MapL.remove found m.left else m.left + with Not_found -> m.left) + |> MapL.add a b; + right = + (try let found = MapL.find a m.left in + if R.compare found b <> 0 then MapR.remove found m.right else m.right + with Not_found -> m.right) + |> MapR.add b a; + } let find_left key m = MapL.find key m.left let find_right key m = MapR.find key m.right @@ -84,7 +84,7 @@ module Make(L : OrderedType)(R : OrderedType) = struct let mem left right m = try R.compare right (find_left left m) = 0 with Not_found -> false let mem_left key m = MapL.mem key m.left let mem_right key m = MapR.mem key m.right - + let remove a b m = if mem a b m then { diff --git a/src/data/CCBijection.mli b/src/data/CCBijection.mli index 9ee7388a..996633c4 100644 --- a/src/data/CCBijection.mli +++ b/src/data/CCBijection.mli @@ -28,8 +28,8 @@ module type S = sig val add : left -> right -> t -> t (** Add [left] and [right] correspondence to bijection such that - [left] and [right] are unique in their respective sets and only - correspond to each other. *) + [left] and [right] are unique in their respective sets and only + correspond to each other. *) val cardinal : t -> int (** Number of bindings. O(n) time *) @@ -51,7 +51,7 @@ module type S = sig val remove : left -> right -> t -> t (** Removes the [left], [right] binding if it exists. Returns the - same bijection otherwise. *) + same bijection otherwise. *) val remove_left : left -> t -> t (** Remove the binding with [left] key if it exists. Returns the diff --git a/src/data/CCFun_vec.ml b/src/data/CCFun_vec.ml index e49a08bb..29af2859 100644 --- a/src/data/CCFun_vec.ml +++ b/src/data/CCFun_vec.ml @@ -19,17 +19,17 @@ type 'a printer = Format.formatter -> 'a -> unit type 'a ktree = unit -> [`Nil | `Node of 'a * 'a ktree list] (* TODO -(** {2 Transient IDs} *) -module Transient = struct - type state = { mutable frozen: bool } - type t = Nil | St of state - let empty = Nil - let equal a b = Pervasives.(==) a b - let create () = St {frozen=false} - let active = function Nil -> false | St st -> not st.frozen - let frozen = function Nil -> true | St st -> st.frozen - let freeze = function Nil -> () | St st -> st.frozen <- true - let with_ f = + (** {2 Transient IDs} *) + module Transient = struct + type state = { mutable frozen: bool } + type t = Nil | St of state + let empty = Nil + let equal a b = Pervasives.(==) a b + let create () = St {frozen=false} + let active = function Nil -> false | St st -> not st.frozen + let frozen = function Nil -> true | St st -> st.frozen + let freeze = function Nil -> () | St st -> st.frozen <- true + let with_ f = let r = create() in try let x = f r in @@ -38,9 +38,9 @@ module Transient = struct with e -> freeze r; raise e - exception Frozen -end - *) + exception Frozen + end +*) (* function array *) module A = struct @@ -133,7 +133,7 @@ type 'a t = { (forall j>=i, sub[j].size<32^{n+1}-1)] (prefix of subs has size of complete binary tree; suffix has smaller size (actually decreasing)) - *) +*) let empty = {size=0; leaves=A.empty; subs=A.empty} @@ -290,7 +290,7 @@ let rec map f m : _ t = let f = Q.Fn.apply f in (List.map f l) = (of_list l |> map f |> to_list) ) - *) +*) let append a b = if is_empty b then a @@ -300,7 +300,7 @@ let append a b = Q.(pair (small_list int)(small_list int)) (fun (l1,l2) -> (l1 @ l2) = (append (of_list l1)(of_list l2) |> to_list) ) - *) +*) let add_list v l = List.fold_left (fun v x -> push x v) v l diff --git a/src/data/CCFun_vec.mli b/src/data/CCFun_vec.mli index a536c101..b6389000 100644 --- a/src/data/CCFun_vec.mli +++ b/src/data/CCFun_vec.mli @@ -16,37 +16,37 @@ type 'a printer = Format.formatter -> 'a -> unit type 'a ktree = unit -> [`Nil | `Node of 'a * 'a ktree list] (* TODO: restore this -(** {2 Transient Identifiers} *) -module Transient : sig - type t - (** Identifiers for transient modifications. A transient modification + (** {2 Transient Identifiers} *) + module Transient : sig + type t + (** Identifiers for transient modifications. A transient modification is uniquely identified by a [Transient.t]. Once [Transient.freeze r] is called, [r] cannot be used to modify the structure again. *) - val create : unit -> t - (** Create a new, active ID. *) + val create : unit -> t + (** Create a new, active ID. *) - val equal : t -> t -> bool - (** Equality between IDs. *) + val equal : t -> t -> bool + (** Equality between IDs. *) - val frozen : t -> bool - (** [frozen i] returns [true] if [freeze i] was called before. In this case, + val frozen : t -> bool + (** [frozen i] returns [true] if [freeze i] was called before. In this case, the ID cannot be used for modifications again. *) - val active : t -> bool - (** [active i] is [not (frozen i)]. *) + val active : t -> bool + (** [active i] is [not (frozen i)]. *) - val freeze : t -> unit - (** [freeze i] makes [i] unusable for new modifications. The values + val freeze : t -> unit + (** [freeze i] makes [i] unusable for new modifications. The values created with [i] will now be immutable. *) - val with_ : (t -> 'a) -> 'a - (** [with_ f] creates a transient ID [i], calls [f i], + val with_ : (t -> 'a) -> 'a + (** [with_ f] creates a transient ID [i], calls [f i], freezes the ID [i] and returns the result of [f i]. *) - exception Frozen - (** Raised when a frozen ID is used. *) -end + exception Frozen + (** Raised when a frozen ID is used. *) + end *) (** {2 Signature} *) @@ -92,19 +92,19 @@ val choose : 'a t -> 'a option (* TODO -val push_mut : id:Transient.t -> 'a -> 'a t -> 'a t -(** [add_mut ~id k v m] behaves like [add k v m], except it will mutate + val push_mut : id:Transient.t -> 'a -> 'a t -> 'a t + (** [add_mut ~id k v m] behaves like [add k v m], except it will mutate in place whenever possible. Changes done with an [id] might affect all versions of the structure obtained with the same [id] (but not other versions). @raise Transient.Frozen if [id] is frozen. *) -val pop_mut : id:Transient.t -> 'a t -> 'a * 'a t -(** Same as {!remove}, but modifies in place whenever possible. + val pop_mut : id:Transient.t -> 'a t -> 'a * 'a t + (** Same as {!remove}, but modifies in place whenever possible. @raise Transient.Frozen if [id] is frozen. *) -val append_mut : id:Transient.t -> into:'a t -> 'a t -> 'a t - *) + val append_mut : id:Transient.t -> into:'a t -> 'a t -> 'a t +*) (** {6 Conversions} *) @@ -128,15 +128,15 @@ val to_gen : 'a t -> 'a gen (* TODO -val add_list_mut : id:Transient.t -> 'a t -> 'a list -> 'a t -(** @raise Frozen if the ID is frozen. *) + val add_list_mut : id:Transient.t -> 'a t -> 'a list -> 'a t + (** @raise Frozen if the ID is frozen. *) -val add_seq_mut : id:Transient.t -> 'a t -> 'a sequence -> 'a t -(** @raise Frozen if the ID is frozen. *) + val add_seq_mut : id:Transient.t -> 'a t -> 'a sequence -> 'a t + (** @raise Frozen if the ID is frozen. *) -val add_gen_mut : id:Transient.t -> 'a t -> 'a gen -> 'a t -(** @raise Frozen if the ID is frozen. *) - *) + val add_gen_mut : id:Transient.t -> 'a t -> 'a gen -> 'a t + (** @raise Frozen if the ID is frozen. *) +*) (** {6 IO} *) diff --git a/src/iter/CCLazy_list.ml b/src/iter/CCLazy_list.ml index 40a416d9..16c4e10f 100644 --- a/src/iter/CCLazy_list.ml +++ b/src/iter/CCLazy_list.ml @@ -84,10 +84,10 @@ let rec flat_map ~f l = let default ~default l = lazy ( - match l with + match l with | lazy Nil -> Lazy.force default | lazy l -> l - ) + ) (*$= [1] (default (return 1) empty |> to_list) From 55e92b4629b340fb59542ac9295fc64e227d3ddd Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 28 Mar 2018 19:53:02 -0500 Subject: [PATCH 56/60] feat(mono): add dotted comparison operators for floats closes #196 --- src/core/CCString.ml | 4 ++-- src/monomorphic/CCMonomorphic.ml | 8 ++++++++ src/monomorphic/CCMonomorphic.mli | 20 ++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/core/CCString.ml b/src/core/CCString.ml index 518ffd06..704eedd8 100644 --- a/src/core/CCString.ml +++ b/src/core/CCString.ml @@ -696,7 +696,7 @@ let prefix ~pre s = 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 if not (Char.equal (String.unsafe_get s i) (String.unsafe_get pre i)) then false else check (i+1) in check 0 @@ -719,7 +719,7 @@ let suffix ~suf s = 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 if not (Char.equal (String.unsafe_get s (off+i)) (String.unsafe_get suf i)) then false else check (i+1) in check 0 diff --git a/src/monomorphic/CCMonomorphic.ml b/src/monomorphic/CCMonomorphic.ml index 3817b80a..28e2ed2f 100644 --- a/src/monomorphic/CCMonomorphic.ml +++ b/src/monomorphic/CCMonomorphic.ml @@ -3,4 +3,12 @@ include Pervasives +let (=.) : float -> float -> bool = (=) +let (<>.) : float -> float -> bool = (<>) +let (<.) : float -> float -> bool = (<) +let (>.) : float -> float -> bool = (>) +let (<=.) : float -> float -> bool = (<=) +let (>=.) : float -> float -> bool = (>=) + let (==) = `Consider_using_CCEqual_physical +let (!=) = `Consider_using_CCEqual_physical diff --git a/src/monomorphic/CCMonomorphic.mli b/src/monomorphic/CCMonomorphic.mli index 382fd003..a188c82b 100644 --- a/src/monomorphic/CCMonomorphic.mli +++ b/src/monomorphic/CCMonomorphic.mli @@ -15,5 +15,25 @@ val compare : int -> int -> int val min : int -> int -> int val max : int -> int -> int +(** {2 Infix operators for Floats} *) + +val (=.) : float -> float -> bool (** @since NEXT_RELEASE *) + +val (<>.) : float -> float -> bool (** @since NEXT_RELEASE *) + +val (<.) : float -> float -> bool (** @since NEXT_RELEASE *) + +val (>.) : float -> float -> bool (** @since NEXT_RELEASE *) + +val (<=.) : float -> float -> bool (** @since NEXT_RELEASE *) + +val (>=.) : float -> float -> bool (** @since NEXT_RELEASE *) + +(** {2 Shadow Dangerous Operators} *) + val (==) : [`Consider_using_CCEqual_physical] [@@ocaml.deprecated "Please use CCEqual.physical or Pervasives.(==) instead."] + +(** @since NEXT_RELEASE *) +val (!=) : [`Consider_using_CCEqual_physical] +[@@ocaml.deprecated "Please use [not CCEqual.physical] or Pervasives.(!=) instead."] From 89fc7f9c77850dbf54e4aaa9488ec8f3293801e2 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 28 Mar 2018 20:09:05 -0500 Subject: [PATCH 57/60] fix(string): compat 4.02 --- src/core/CCString.ml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/CCString.ml b/src/core/CCString.ml index 704eedd8..47879c71 100644 --- a/src/core/CCString.ml +++ b/src/core/CCString.ml @@ -696,7 +696,7 @@ let prefix ~pre s = else ( let rec check i = if i=len then true - else if not (Char.equal (String.unsafe_get s i) (String.unsafe_get pre i)) then false + else if Pervasives.(<>) (String.unsafe_get s i) (String.unsafe_get pre i) then false else check (i+1) in check 0 @@ -719,7 +719,7 @@ let suffix ~suf s = let off = String.length s - len in let rec check i = if i=len then true - else if not (Char.equal (String.unsafe_get s (off+i)) (String.unsafe_get suf i)) then false + else if Pervasives.(<>) (String.unsafe_get s (off+i)) (String.unsafe_get suf i) then false else check (i+1) in check 0 From 5986955fb6ace1261619b0ad2a5201fe5d4cf420 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 28 Mar 2018 20:10:14 -0500 Subject: [PATCH 58/60] feat(list): add `{interleave,intersperse}` (closes #191) --- src/core/CCList.ml | 50 ++++++++++++++++++++++++++++++++++++++++++++- src/core/CCList.mli | 11 +++++++++- 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/src/core/CCList.ml b/src/core/CCList.ml index 8a4dbfb0..30ef12a1 100644 --- a/src/core/CCList.ml +++ b/src/core/CCList.ml @@ -580,7 +580,7 @@ let sorted_merge ~cmp l1 l2 = List.length (sorted_merge ~cmp:CCInt.compare l1 l2) = List.length l1 + List.length l2) *) -let sort_uniq (type elt) ~cmp l = List.sort_uniq cmp l +let sort_uniq ~cmp l = List.sort_uniq cmp l (*$T sort_uniq ~cmp:CCInt.compare [1;2;5;3;6;1;4;2;3] = [1;2;3;4;5;6] @@ -786,6 +786,54 @@ let sublists_of_len ?(last=fun _ -> None) ?offset n l = [[1;2]; [3;4]] (subs 2 [1;2;3;4;5]) *) +let intersperse x l = + let rec aux_direct i x l = match l with + | [] -> [] + | [_] -> l + | _ when i=0 -> aux_tailrec [] x l + | y :: tail -> y :: x :: aux_direct (i-1) x tail + and aux_tailrec acc x l = match l with + | [] -> List.rev acc + | [y] -> List.rev (y::acc) + | y :: tail -> aux_tailrec (x :: y :: acc) x tail + in + aux_direct 1_000 x l + +(*$= + [] (intersperse 0 []) + [1] (intersperse 0 [1]) + [1;0;2;0;3;0;4] (intersperse 0 [1;2;3;4]) +*) + +(*$Q + Q.(pair int (list int)) (fun (x,l) -> \ + length (intersperse x l) = (if length l <= 1 then length l else 2 * length l-1)) + Q.(pair int (list int)) (fun (x,l) -> \ + rev (intersperse x l) = intersperse x (rev l)) +*) + +let interleave l1 l2 : _ list = + let rec aux acc l1 l2 = match l1, l2 with + | [], [] -> List.rev acc + | [], _ -> List.rev (List.rev_append l2 acc) + | _, [] -> List.rev (List.rev_append l1 acc) + | x1 :: tl1, x2 :: tl2 -> + aux (x2 :: x1 :: acc) tl1 tl2 + in + aux [] l1 l2 + +(*$= + [1;2;3;4;5] (interleave [1;3] [2;4;5]) + [1;2;3] (interleave [1] [2;3]) +*) + +(*$Q + Q.(pair (small_list int)(small_list int)) (fun (l1,l2) -> \ + length (interleave l1 l2) = length l1 + length l2) + Q.(small_list int) (fun l -> l = interleave [] l) + Q.(small_list int) (fun l -> l = interleave l []) +*) + let take_while p l = let rec direct i p l = match l with | [] -> [] diff --git a/src/core/CCList.mli b/src/core/CCList.mli index a9ed88d2..2b8261c5 100644 --- a/src/core/CCList.mli +++ b/src/core/CCList.mli @@ -45,7 +45,7 @@ val (@) : 'a t -> 'a t -> 'a t Concatenate two lists. *) val filter : ('a -> bool) -> 'a t -> 'a t -(** Safe version of {!List.filter}. +(** Safe version of {!List.filter}. [filter p l] returns all the elements of the list [l] that satisfy the predicate [p]. The order of the elements in the input list is preserved. *) @@ -202,6 +202,15 @@ val sublists_of_len : @raise Invalid_argument if [offset <= 0] or [n <= 0]. @since 1.0 *) +val intersperse : 'a -> 'a list -> 'a list +(** Insert the first argument between every element of the list + @since NEXT_RELEASE *) + +val interleave : 'a list -> 'a list -> 'a list +(** [interleave [x1…xn] [y1…ym]] is [x1,y1,x2,y2,…] and finishes with + the suffix of the longest list + @since NEXT_RELEASE *) + val pure : 'a -> 'a t (** [pure] is [return]. *) From c04ee13d6e5d3b652be3d7e48050334e58c15d3b Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 28 Mar 2018 20:20:09 -0500 Subject: [PATCH 59/60] fix(array): small change in signatures --- src/core/CCArray.mli | 2 +- src/core/CCArrayLabels.mli | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/core/CCArray.mli b/src/core/CCArray.mli index 46784348..651394fd 100644 --- a/src/core/CCArray.mli +++ b/src/core/CCArray.mli @@ -13,7 +13,7 @@ type 'a printer = Format.formatter -> 'a -> unit (** {2 Arrays} *) -include module type of Array +include module type of struct include Array end type 'a t = 'a array diff --git a/src/core/CCArrayLabels.mli b/src/core/CCArrayLabels.mli index 2cbe9330..4e7bc98e 100644 --- a/src/core/CCArrayLabels.mli +++ b/src/core/CCArrayLabels.mli @@ -13,7 +13,7 @@ type 'a printer = Format.formatter -> 'a -> unit (** {2 Arrays} *) -include module type of ArrayLabels +include module type of struct include ArrayLabels end type 'a t = 'a array From 6e50ff41c693bd7908024da9129a1cfda9567d39 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Wed, 28 Mar 2018 20:26:17 -0500 Subject: [PATCH 60/60] prepare for 2.1 --- CHANGELOG.adoc | 28 ++++++++++++++++++++++++++++ containers.opam | 2 +- src/core/CCArrayLabels.mli | 12 ++++++------ src/core/CCFloat.mli | 10 +++++----- src/core/CCFormat.mli | 2 +- src/core/CCFun.mli | 2 +- src/core/CCHash.mli | 4 ++-- src/core/CCInt.mli | 26 +++++++++++++------------- src/core/CCInt32.mli | 2 +- src/core/CCInt64.mli | 12 ++++++------ src/core/CCList.mli | 4 ++-- src/core/CCNativeint.mli | 2 +- src/core/CCUtf8_string.mli | 2 +- src/data/CCBijection.mli | 2 +- src/data/CCFun_vec.mli | 2 +- src/iter/CCLazy_list.mli | 4 ++-- src/monomorphic/CCMonomorphic.mli | 14 +++++++------- 17 files changed, 79 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 7e766c84..243ca122 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -1,5 +1,33 @@ = Changelog +== 2.1 + +- make `CCInt64` compatible with `Int64` (breaking!) (closes #192) + +- Add `CCBijection` in containers.data +- feat(mono): add dotted comparison operators for floats +- add `?margin` parameter to `CCFormat.ksprintf` +- add `CCUtf8_string` with basic encoding and decoding functionalities +- Add `CCLazy_list.<|>` +- Adding `CCNativeint` +- enrich `CCInt.Infix` to get a uniform interface with `CCInt{32,64}` +- add `CCInt{32,64}.Infix` +- Adding CCInt32 module +- add `CCHash.combine{5,6}` +- Add infix operators to CCFloat +- feat(list): add `{interleave,intersperse}` (closes #191) +- add missing signatures of `CCArrayLabels` (closes #193) +- Add CCFun.iterate +- add experimental `CCFun_vec` data structure for fast functional vectors + +- fix: strong type aliases in Random (closes #210) +- use standard `List.sort_uniq` +- remove explicit dep on `bytes` in jbuild files +- update printers names in containers.top (closes #201) +- Enable support for Travis CI and Appveyor +- test deps are required when we run tests +- point to JST's blog post on poly compare + == 2.0 === breaking diff --git a/containers.opam b/containers.opam index 612c6187..ded5b0ed 100644 --- a/containers.opam +++ b/containers.opam @@ -1,6 +1,6 @@ opam-version: "1.2" name: "containers" -version: "2.0" +version: "2.1" author: "Simon Cruanes" maintainer: "simon.cruanes.2007@m4x.org" build: [ diff --git a/src/core/CCArrayLabels.mli b/src/core/CCArrayLabels.mli index 4e7bc98e..51cfac5f 100644 --- a/src/core/CCArrayLabels.mli +++ b/src/core/CCArrayLabels.mli @@ -70,13 +70,13 @@ val fold_while : f:('a -> 'b -> 'a * [`Stop | `Continue]) -> init:'a -> 'b t -> val fold_map : f:('acc -> 'a -> 'acc * 'b) -> init:'acc -> 'a t -> 'acc * 'b t (** [fold_map f acc a] is a [fold_left]-like function, but it also maps the array to another array. - @since NEXT_RELEASE *) + @since 2.1 *) val scan_left : f:('acc -> 'a -> 'acc) -> init:'acc -> 'a t -> 'acc t (** [scan_left f acc a] returns the array [ [|acc; f acc x0; f (f acc a.(0)) a.(1); …|] ]. - @since NEXT_RELEASE *) + @since 2.1 *) val iter : f:('a -> unit) -> 'a t -> unit @@ -130,21 +130,21 @@ val sort_ranking : f:('a -> 'a -> int) -> 'a t -> int array val find_map : f:('a -> 'b option) -> 'a t -> 'b option (** [find_map f a] returns [Some y] if there is an element [x] such that [f x = Some y], else it returns [None]. - @since NEXT_RELEASE *) + @since 2.1 *) val find : f:('a -> 'b option) -> 'a t -> 'b option (** [find f a] returns [Some y] if there is an element [x] such that [f x = Some y], else it returns [None]. - @deprecated since NEXT_RELEASE *) + @deprecated since 2.1 *) val find_map_i : f:(int -> 'a -> 'b option) -> 'a t -> 'b option (** Like {!find_map}, but also pass the index to the predicate function. - @since NEXT_RELEASE *) + @since 2.1 *) val findi : f:(int -> 'a -> 'b option) -> 'a t -> 'b option (** Like {!find}, but also pass the index to the predicate function. @since 0.3.4 - @deprecated since NEXT_RELEASE *) + @deprecated since 2.1 *) val find_idx : f:('a -> bool) -> 'a t -> (int * 'a) option (** [find_idx p x] returns [Some (i,x)] where [x] is the [i]-th element of [l], diff --git a/src/core/CCFloat.mli b/src/core/CCFloat.mli index 77b3c46a..21ef0588 100644 --- a/src/core/CCFloat.mli +++ b/src/core/CCFloat.mli @@ -132,19 +132,19 @@ module Infix : sig (** @since 0.17 *) val (+) : t -> t -> t - (** Addition. @since NEXT_RELEASE *) + (** Addition. @since 2.1 *) val (-) : t -> t -> t - (** Subtraction. @since NEXT_RELEASE *) + (** Subtraction. @since 2.1 *) val (~-) : t -> t - (** Unary negation. @since NEXT_RELEASE *) + (** Unary negation. @since 2.1 *) val ( * ) : t -> t -> t - (** Multiplication. @since NEXT_RELEASE *) + (** Multiplication. @since 2.1 *) val (/) : t -> t -> t - (** Division. @since NEXT_RELEASE *) + (** Division. @since 2.1 *) end include module type of Infix diff --git a/src/core/CCFormat.mli b/src/core/CCFormat.mli index f7650483..9baaff0b 100644 --- a/src/core/CCFormat.mli +++ b/src/core/CCFormat.mli @@ -286,7 +286,7 @@ val ksprintf : 'a (** [ksprintf fmt ~f] formats using [fmt], in a way similar to {!sprintf}, and then calls [f] on the resulting string. - @param margin set margin (since NEXT_RELEASE) + @param margin set margin (since 2.1) @since 0.14 *) val to_file : string -> ('a, t, unit, unit) format4 -> 'a diff --git a/src/core/CCFun.mli b/src/core/CCFun.mli index 55d82c1e..2bdc77ef 100644 --- a/src/core/CCFun.mli +++ b/src/core/CCFun.mli @@ -79,7 +79,7 @@ val opaque_identity : 'a -> 'a val iterate : int -> ('a -> 'a) -> 'a -> 'a (** [iterate n f] is [f] iterated [n] times. That is to say, [iterate 0 f x] is [x], [iterate 1 f x] is [f x], [iterate 2 f x] is [f (f x)], etc. - @since NEXT_RELEASE *) + @since 2.1 *) (** {2 Monad} diff --git a/src/core/CCHash.mli b/src/core/CCHash.mli index fe86eac8..e17743a4 100644 --- a/src/core/CCHash.mli +++ b/src/core/CCHash.mli @@ -65,10 +65,10 @@ val combine3 : hash -> hash -> hash -> hash val combine4 : hash -> hash -> hash -> hash -> hash val combine5 : hash -> hash -> hash -> hash -> hash -> hash -(** @since NEXT_RELEASE *) +(** @since 2.1 *) val combine6 : hash -> hash -> hash -> hash -> hash -> hash -> hash -(** @since NEXT_RELEASE *) +(** @since 2.1 *) (** {2 Iterators} *) diff --git a/src/core/CCInt.mli b/src/core/CCInt.mli index 2d0389ac..de926e90 100644 --- a/src/core/CCInt.mli +++ b/src/core/CCInt.mli @@ -116,19 +116,19 @@ module Infix : sig (** Alias to {!range'}. @since 1.2 *) - val (+) : t -> t -> t (** @since NEXT_RELEASE *) - val (-) : t -> t -> t (** @since NEXT_RELEASE *) - val (~-) : t -> t (** @since NEXT_RELEASE *) - val ( * ) : t -> t -> t (** @since NEXT_RELEASE *) - val (/) : t -> t -> t (** @since NEXT_RELEASE *) - val (mod) : t -> t -> t (** @since NEXT_RELEASE *) - val (land) : t -> t -> t (** @since NEXT_RELEASE *) - val (lor) : t -> t -> t (** @since NEXT_RELEASE *) - val (lxor) : t -> t -> t (** @since NEXT_RELEASE *) - val lnot : t -> t (** @since NEXT_RELEASE *) - val (lsl) : t -> int -> t (** @since NEXT_RELEASE *) - val (lsr) : t -> int -> t (** @since NEXT_RELEASE *) - val (asr) : t -> int -> t (** @since NEXT_RELEASE *) + val (+) : t -> t -> t (** @since 2.1 *) + val (-) : t -> t -> t (** @since 2.1 *) + val (~-) : t -> t (** @since 2.1 *) + val ( * ) : t -> t -> t (** @since 2.1 *) + val (/) : t -> t -> t (** @since 2.1 *) + val (mod) : t -> t -> t (** @since 2.1 *) + val (land) : t -> t -> t (** @since 2.1 *) + val (lor) : t -> t -> t (** @since 2.1 *) + val (lxor) : t -> t -> t (** @since 2.1 *) + val lnot : t -> t (** @since 2.1 *) + val (lsl) : t -> int -> t (** @since 2.1 *) + val (lsr) : t -> int -> t (** @since 2.1 *) + val (asr) : t -> int -> t (** @since 2.1 *) end include module type of Infix diff --git a/src/core/CCInt32.mli b/src/core/CCInt32.mli index 1386abf1..33a0cfab 100644 --- a/src/core/CCInt32.mli +++ b/src/core/CCInt32.mli @@ -13,7 +13,7 @@ of type int, and arithmetic operations on int32 are generally slower than those on int. Use int32 only when the application requires exact 32-bit arithmetic. - @since NEXT_RELEASE *) + @since 2.1 *) include module type of struct include Int32 end diff --git a/src/core/CCInt64.mli b/src/core/CCInt64.mli index faf58cd8..a6b31951 100644 --- a/src/core/CCInt64.mli +++ b/src/core/CCInt64.mli @@ -67,7 +67,7 @@ val (asr) : t -> int -> t The result is unspecified if [y < 0] or [y >= 64]. *) (** Infix operators - @since NEXT_RELEASE *) + @since 2.1 *) module Infix : sig val (+) : t -> t -> t val (-) : t -> t -> t @@ -119,7 +119,7 @@ val of_int : int -> t val of_int_exn : int -> t (** Alias to {!Int64.of_int}. - @deprecated since NEXT_RELEASE *) + @deprecated since 2.1 *) val to_int32 : t -> int32 (** Convert the given 64-bit integer (type [int64]) to a @@ -133,7 +133,7 @@ val of_int32 : int32 -> t val of_int32_exn : int32 -> t (** Alias to {!Int64.of_int32}. - @deprecated since NEXT_RELEASE *) + @deprecated since 2.1 *) val to_nativeint : t -> nativeint (** Convert the given 64-bit integer (type [int64]) to a @@ -147,7 +147,7 @@ val of_nativeint : nativeint -> t val of_nativeint_exn : nativeint -> t (** Alias to {!Int64.of_nativeint}. - @deprecated since NEXT_RELEASE *) + @deprecated since 2.1 *) val to_float : t -> float (** Convert the given 64-bit integer to a floating-point number. *) @@ -162,7 +162,7 @@ val of_float : float -> t val of_float_exn : float -> t (** Alias to {!Int64.of_float}. - @deprecated since NEXT_RELEASE *) + @deprecated since 2.1 *) val to_string : t -> string (** Return the string representation of its argument, in decimal. *) @@ -172,7 +172,7 @@ val of_string : string -> t option val of_string_opt : string -> t option (** Alias to {!of_string}. - @since NEXT_RELEASE *) + @since 2.1 *) val of_string_exn : string -> t (** Alias to {!Int64.of_string}. diff --git a/src/core/CCList.mli b/src/core/CCList.mli index 2b8261c5..ddaa6eab 100644 --- a/src/core/CCList.mli +++ b/src/core/CCList.mli @@ -204,12 +204,12 @@ val sublists_of_len : val intersperse : 'a -> 'a list -> 'a list (** Insert the first argument between every element of the list - @since NEXT_RELEASE *) + @since 2.1 *) val interleave : 'a list -> 'a list -> 'a list (** [interleave [x1…xn] [y1…ym]] is [x1,y1,x2,y2,…] and finishes with the suffix of the longest list - @since NEXT_RELEASE *) + @since 2.1 *) val pure : 'a -> 'a t (** [pure] is [return]. *) diff --git a/src/core/CCNativeint.mli b/src/core/CCNativeint.mli index f1754e30..47930754 100644 --- a/src/core/CCNativeint.mli +++ b/src/core/CCNativeint.mli @@ -14,7 +14,7 @@ and arithmetic operations on [nativeint] are generally slower than those on [int]. Use [nativeint] only when the application requires the extra bit of precision over the [int] type. - @since NEXT_RELEASE *) + @since 2.1 *) include module type of struct include Nativeint end diff --git a/src/core/CCUtf8_string.mli b/src/core/CCUtf8_string.mli index 4db382be..da6caae6 100644 --- a/src/core/CCUtf8_string.mli +++ b/src/core/CCUtf8_string.mli @@ -10,7 +10,7 @@ For more elaborate operations, please use the excellent {{: http://erratique.ch/software/uutf} Uutf}. - @since NEXT_RELEASE + @since 2.1 {b status}: experimental *) diff --git a/src/data/CCBijection.mli b/src/data/CCBijection.mli index 996633c4..1c39d41e 100644 --- a/src/data/CCBijection.mli +++ b/src/data/CCBijection.mli @@ -4,7 +4,7 @@ Represents 1-to-1 mappings between two types. Each element from the "left" is mapped to one "right" value, and conversely. - @since NEXT_RELEASE *) + @since 2.1 *) type 'a sequence = ('a -> unit) -> unit diff --git a/src/data/CCFun_vec.mli b/src/data/CCFun_vec.mli index b6389000..76f907f3 100644 --- a/src/data/CCFun_vec.mli +++ b/src/data/CCFun_vec.mli @@ -7,7 +7,7 @@ {b status: experimental. DO NOT USE (yet)} - @since NEXT_RELEASE + @since 2.1 *) type 'a sequence = ('a -> unit) -> unit diff --git a/src/iter/CCLazy_list.mli b/src/iter/CCLazy_list.mli index fae64d50..95eb3c3e 100644 --- a/src/iter/CCLazy_list.mli +++ b/src/iter/CCLazy_list.mli @@ -47,12 +47,12 @@ val flat_map : f:('a -> 'b t) -> 'a t -> 'b t val default : default:'a t -> 'a t -> 'a t (** Choice operator. - @since NEXT_RELEASE *) + @since 2.1 *) module Infix : sig val (>|=) : 'a t -> ('a -> 'b) -> 'b t val (>>=) : 'a t -> ('a -> 'b t) -> 'b t - val (<|>) : 'a t -> 'a t -> 'a t (** Alias to {!default}. @since NEXT_RELEASE *) + val (<|>) : 'a t -> 'a t -> 'a t (** Alias to {!default}. @since 2.1 *) end include module type of Infix diff --git a/src/monomorphic/CCMonomorphic.mli b/src/monomorphic/CCMonomorphic.mli index a188c82b..6b19e49f 100644 --- a/src/monomorphic/CCMonomorphic.mli +++ b/src/monomorphic/CCMonomorphic.mli @@ -17,23 +17,23 @@ val max : int -> int -> int (** {2 Infix operators for Floats} *) -val (=.) : float -> float -> bool (** @since NEXT_RELEASE *) +val (=.) : float -> float -> bool (** @since 2.1 *) -val (<>.) : float -> float -> bool (** @since NEXT_RELEASE *) +val (<>.) : float -> float -> bool (** @since 2.1 *) -val (<.) : float -> float -> bool (** @since NEXT_RELEASE *) +val (<.) : float -> float -> bool (** @since 2.1 *) -val (>.) : float -> float -> bool (** @since NEXT_RELEASE *) +val (>.) : float -> float -> bool (** @since 2.1 *) -val (<=.) : float -> float -> bool (** @since NEXT_RELEASE *) +val (<=.) : float -> float -> bool (** @since 2.1 *) -val (>=.) : float -> float -> bool (** @since NEXT_RELEASE *) +val (>=.) : float -> float -> bool (** @since 2.1 *) (** {2 Shadow Dangerous Operators} *) val (==) : [`Consider_using_CCEqual_physical] [@@ocaml.deprecated "Please use CCEqual.physical or Pervasives.(==) instead."] -(** @since NEXT_RELEASE *) +(** @since 2.1 *) val (!=) : [`Consider_using_CCEqual_physical] [@@ocaml.deprecated "Please use [not CCEqual.physical] or Pervasives.(!=) instead."]