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/AUTHORS.adoc b/AUTHORS.adoc index 56dba742..895cb10e 100644 --- a/AUTHORS.adoc +++ b/AUTHORS.adoc @@ -27,3 +27,5 @@ - Orbifx (Stavros Polymenis) - Rand (@rand00) - Dave Aitken (@actionshrimp) +- Etienne Millon (@emillon) +- Christopher Zimmermann (@madroach) 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/README.adoc b/README.adoc index 6df889c9..9050155f 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::[] @@ -45,6 +46,78 @@ 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`. + See <> for more details. + +[[mono-ops]] +== Monomorphic operators: why, and how? + +=== Why shadow polymorphic operators by default? + +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 +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. + +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 +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 +https://github.com/c-cube/ocaml-containers/issues/196[#196], +https://github.com/c-cube/ocaml-containers/issues/197[#197] + == Change Log See link:CHANGELOG.adoc[this file]. @@ -102,7 +175,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 @@ -112,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 @@ -474,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 diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..ee0f6e0a --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,26 @@ +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 + - CYG_ROOT: C:\cygwin + 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: +- call %CYG_ROOT%\bin\bash.exe -l %APPVEYOR_BUILD_FOLDER%\appveyor-opam.sh 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 2891b06f..93e96782 100644 --- a/benchs/run_benchs.ml +++ b/benchs/run_benchs.ml @@ -110,14 +110,22 @@ 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 bv = BatVect.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 + 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 *) diff --git a/containers.opam b/containers.opam index 21bfedc7..e0497f7d 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: [ @@ -9,18 +9,20 @@ build: [ build-doc: [ "jbuilder" "build" "@doc" ] build-test: [ "jbuilder" "runtest" "-p" name "-j" jobs] depends: [ - "jbuilder" {build} + "jbuilder" {build & >= "1.0+beta12"} "result" + "uchar" + "qtest" { test } + "qcheck" { test } + "ounit" { test } + "sequence" { test } + "gen" { test } + "uutf" { test } + "odoc" { doc } ] depopts: [ "base-unix" "base-threads" - "qtest" { test } - "qcheck" { test } - "oUnit" { test } - "sequence" { test } - "gen" { test } - "odoc" { doc } ] conflicts: [ "sequence" { < "0.5" } 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/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 05c1895c..51cfac5f 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 @@ -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 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 2.1 *) + + 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 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]. *) + that [f x = Some y], else it returns [None]. + @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 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 *) + @since 0.3.4 + @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/CCChar.mli b/src/core/CCChar.mli index 56414df3..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. *) @@ -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/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..21ef0588 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 2.1 *) + + val (-) : t -> t -> t + (** Subtraction. @since 2.1 *) + + val (~-) : t -> t + (** Unary negation. @since 2.1 *) + + val ( * ) : t -> t -> t + (** Multiplication. @since 2.1 *) + + val (/) : t -> t -> t + (** Division. @since 2.1 *) end include module type of Infix 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..9baaff0b 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 "()". *) @@ -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,15 +276,17 @@ 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 : + ?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 2.1) @since 0.14 *) val to_file : string -> ('a, t, unit, unit) format4 -> 'a 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..2bdc77ef 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 2.1 *) + (** {2 Monad} Functions with a fixed domain are monads in their codomain. *) 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..e17743a4 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 2.1 *) + +val combine6 : hash -> hash -> hash -> hash -> hash -> hash -> hash +(** @since 2.1 *) + (** {2 Iterators} *) type 'a sequence = ('a -> unit) -> unit 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/CCInt.ml b/src/core/CCInt.ml index cc87f080..f260c719 100644 --- a/src/core/CCInt.ml +++ b/src/core/CCInt.ml @@ -3,12 +3,52 @@ 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 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 @@ -211,51 +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) -*) - - -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..de926e90 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 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.ml b/src/core/CCInt32.ml new file mode 100644 index 00000000..74aaab63 --- /dev/null +++ b/src/core/CCInt32.ml @@ -0,0 +1,51 @@ +(* This file is free software, part of containers. See file "license" for more details. *) + +include Int32 + +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/CCInt32.mli b/src/core/CCInt32.mli new file mode 100644 index 00000000..33a0cfab --- /dev/null +++ b/src/core/CCInt32.mli @@ -0,0 +1,147 @@ +(* 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 2.1 *) + +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]. *) + +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)}. *) + +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}. *) diff --git a/src/core/CCInt64.ml b/src/core/CCInt64.ml index 9ac33506..bfaed6f9 100644 --- a/src/core/CCInt64.ml +++ b/src/core/CCInt64.ml @@ -2,54 +2,56 @@ 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} *) 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..a6b31951 100644 --- a/src/core/CCInt64.mli +++ b/src/core/CCInt64.mli @@ -2,11 +2,11 @@ (** {1 Int64} - Helpers for 64-bit integers. + Helpers for 64-bit integers @since 0.13 *) -type t = int64 +include module type of struct include Int64 end val (+) : t -> t -> t (** Addition. *) @@ -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 2.1 *) +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)}. *) @@ -89,14 +113,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 2.1 *) val to_int32 : t -> int32 (** Convert the given 64-bit integer (type [int64]) to a @@ -104,14 +127,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. *) +(** Alias to {!Int64.of_int32}. + @deprecated since 2.1 *) val to_nativeint : t -> nativeint (** Convert the given 64-bit integer (type [int64]) to a @@ -119,28 +141,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. *) + @deprecated since 2.1 *) 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 2.1 *) val to_string : t -> string (** Return the string representation of its argument, in decimal. *) @@ -148,10 +170,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 2.1 *) + 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. diff --git a/src/core/CCList.ml b/src/core/CCList.ml index 82291ba3..30ef12a1 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 ~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] @@ -792,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 b80bf76a..ddaa6eab 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. *) @@ -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)]]. @@ -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 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 2.1 *) + val pure : 'a -> 'a t (** [pure] is [return]. *) 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..47930754 --- /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 2.1 *) + +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/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()) +*) 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.ml b/src/core/CCString.ml index 11291efe..47879c71 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 *) @@ -84,7 +84,7 @@ let rev s = (*$Q Q.printable_string (fun s -> \ rev s = (to_list s |> List.rev |> of_list)) - *) +*) (*$= @@ -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 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 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 diff --git a/src/core/CCString.mli b/src/core/CCString.mli index 60de9493..1a551de0 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,30 +38,29 @@ 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} *) -include module type of String +include module type of struct include String end val equal : string -> string -> bool (** Equality function on 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 new file mode 100644 index 00000000..60ccf1b5 --- /dev/null +++ b/src/core/CCUtf8_string.ml @@ -0,0 +1,334 @@ + +(** {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 + +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 + +(** 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 + +let n_bytes = length + +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 + 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]) + @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 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 ( + (* 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 ( + malformed st; + ) + ) 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 processed! *) + ) else ( + (* char is truncated *) + malformed st; + ) + in + if st.i >= st.len then ( + 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 *) + | '\194' .. '\223' -> read_multi 1 ((Char.code c) land 0b11111) (* 110yyyyy *) + | '\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' -> malformed st; + ) + +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 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 + +(* 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 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 = + 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 & ~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 & ~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) + ) +*) + +(*$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 + 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 & ~long_factor:40 + 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 & ~long_factor:40 ~count:100_000 + 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 & ~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 + 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..da6caae6 --- /dev/null +++ b/src/core/CCUtf8_string.mli @@ -0,0 +1,91 @@ + +(** {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 2.1 + + {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 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 + +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/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) 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) diff --git a/src/core/containers.ml b/src/core/containers.ml index 6db033eb..b12a4a2c 100644 --- a/src/core/containers.ml +++ b/src/core/containers.ml @@ -27,11 +27,13 @@ module Hashtbl = struct end module Heap = CCHeap module Int = CCInt +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 @@ -43,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..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 containers.monomorphic)) + (libraries (result uchar containers.monomorphic)) )) 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/CCBijection.ml b/src/data/CCBijection.ml new file mode 100644 index 00000000..3b2b1e3b --- /dev/null +++ b/src/data/CCBijection.ml @@ -0,0 +1,133 @@ +(* 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 +end + +module type S = sig + type t + type left + type right + + 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 + 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 + 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 + type left = L.t + type right = R.t + + module MapL = Map.Make(L) + module MapR = Map.Make(R) + + type t = { + left : right MapL.t; + right : left MapR.t; + } + + let empty = { + left = MapL.empty; + 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 + 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 + + 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 new file mode 100644 index 00000000..1c39d41e --- /dev/null +++ b/src/data/CCBijection.mli @@ -0,0 +1,84 @@ +(* 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 2.1 *) + +type 'a sequence = ('a -> unit) -> unit + +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 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. *) + + 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 + (** @raise Not_found if left is not found *) + + val find_right : right -> t -> left + (** @raise 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 otherwise *) + + val remove_right : right -> t -> t + (** Remove the binding with [right] key if it exists. Returns the + same bijection otherwise *) + + 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 *) + + 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 + with type left = L.t and type right = R.t 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 () 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 c0b6b8c3..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,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. *) @@ -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.ml b/src/data/CCFun_vec.ml new file mode 100644 index 00000000..29af2859 --- /dev/null +++ b/src/data/CCFun_vec.ml @@ -0,0 +1,380 @@ +(* 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] + +(* 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 = + 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 = 'a array + + let length_log = 5 + let max_length = 32 + let mask = max_length-1 + + let () = assert (max_length = 1 lsl length_log) + + 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 () = [| |] + + let empty = [| |] + let is_empty a = length a = 0 + + let return x = [| x |] + + let get a i = + if i<0 || i >= length a then invalid_arg "A.get"; + 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 0 arr 0 n; + arr + + let pop a = + let n = length a in + if n=0 then invalid_arg "A.pop"; + Array.sub a 0 (n-1) + + 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.(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 = + 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 0 arr 0 i; + arr + ) else if mut then ( + (* replace element at [i] in place *) + a.(i) <- x; + a + ) else ( + (* replace element at [i] in copy *) + let arr = Array.copy a in + arr.(i) <- x; + 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 = + | 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 + +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 I_cons (high-1, low) + else aux (high_idx_ high) (I_cons (low_idx_ high, low)) + in + 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 + | 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 + +(*$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 + | 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} + | 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 *) + 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 + | 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} + | 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 + 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 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 fold_rev ~f ~x m = foldi_rev ~f:(fun acc _ x -> f acc x) ~x m + +let rec map f m : _ t = + { subs=A.map (map f) m.subs; + leaves=A.map f m.leaves; + size=m.size; + } + +(*$QR + 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 append a b = + if is_empty b then a + else fold ~f:(fun v x -> push x v) ~x:a b + +(*$QR + 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 + +let of_list l = add_list empty l + +let to_list m = fold_rev m ~f:(fun acc x -> x::acc) ~x:[] + +(*$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 = iteri ~f:(fun _ v -> yield 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 m g = match g() with + | None -> m + | Some x -> add_gen (push x 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 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 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 -> \ + (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 ppv out m = + let first = ref true in + iter m + ~f:(fun v -> + if !first then first := false else Format.fprintf out ";@ "; + ppv out v + ) diff --git a/src/data/CCFun_vec.mli b/src/data/CCFun_vec.mli new file mode 100644 index 00000000..76f907f3 --- /dev/null +++ b/src/data/CCFun_vec.mli @@ -0,0 +1,143 @@ +(* 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: experimental. DO NOT USE (yet)} + + @since 2.1 +*) + +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] + +(* TODO: restore this + (** {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 +(** 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 + (** [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_mut : id:Transient.t -> into:'a t -> 'a t -> 'a t +*) + +(** {6 Conversions} *) + +val to_list : 'a t -> 'a list + +val of_list : 'a list -> 'a t + +val add_list : 'a t -> 'a list -> 'a t + +val add_seq : 'a t -> 'a sequence -> 'a t + +val of_seq : 'a sequence -> 'a t + +val to_seq : 'a t -> 'a sequence + +val add_gen : 'a t -> 'a gen -> 'a t + +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 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/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/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 ffec50fd..a8384e37 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} *) @@ -82,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. *) @@ -94,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]. *) @@ -143,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]). *) @@ -200,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/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/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.ml b/src/iter/CCLazy_list.ml index d4855bd9..16c4e10f 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 ~default l = + lazy ( + match l with + | lazy Nil -> Lazy.force default + | lazy l -> l + ) + +(*$= + [1] (default (return 1) empty |> to_list) +*) + module Infix = struct let (>|=) x f = map ~f x let (>>=) x f = flat_map ~f x + 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 b6111661..95eb3c3e 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). @@ -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 : default:'a t -> 'a t -> 'a t +(** Choice operator. + @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 2.1 *) end include module type of Infix 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/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..6b19e49f 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 2.1 *) + +val (<>.) : float -> float -> bool (** @since 2.1 *) + +val (<.) : float -> float -> bool (** @since 2.1 *) + +val (>.) : float -> float -> bool (** @since 2.1 *) + +val (<=.) : float -> float -> bool (** @since 2.1 *) + +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 2.1 *) +val (!=) : [`Consider_using_CCEqual_physical] +[@@ocaml.deprecated "Please use [not CCEqual.physical] or Pervasives.(!=) instead."] 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/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 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" ] 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)) ))