diff --git a/.ocamlinit b/.ocamlinit index 7123b8d..c753a9f 100644 --- a/.ocamlinit +++ b/.ocamlinit @@ -1,9 +1,9 @@ -#directory "_build";; +#directory "_build/src";; #load "sequence.cma";; open Sequence.Infix;; -#directory "_build/bigarray/";; +#directory "_build/src/bigarray/";; #load "bigarray.cma";; (* vim:syntax=ocaml *) diff --git a/Makefile b/Makefile index 8abcac6..6ab6d35 100644 --- a/Makefile +++ b/Makefile @@ -40,8 +40,24 @@ configure: # OASIS_STOP -run-tests: - ./run_tests.native +QTEST_PREAMBLE='' +DONTTEST=src/sequenceLabels.ml +QTESTABLE=$(filter-out $(DONTTEST), \ + $(wildcard src/*.ml) \ + $(wildcard src/*.mli) \ + ) + +qtest-clean: + @rm -rf qtest/ + +qtest-gen: + @mkdir -p qtest + @if which qtest > /dev/null ; then \ + qtest extract --preamble $(QTEST_PREAMBLE) \ + -o qtest/run_qtest.ml \ + $(QTESTABLE) 2> /dev/null ; \ + else touch qtest/run_qtest.ml ; \ + fi examples: ocamlbuild examples/test_sexpr.native @@ -59,7 +75,7 @@ push_stable: all VERSION=$(shell awk '/^Version:/ {print $$2}' _oasis) -SOURCE=*.ml *.mli invert/*.ml invert/*.mli bigarray/*.ml bigarray/*.mli +SOURCE=$(addprefix src/, *.ml *.mli invert/*.ml invert/*.mli bigarray/*.ml bigarray/*.mli) update_next_tag: @echo "update version to $(VERSION)..." diff --git a/README.md b/README.md index 0ca3219..adb639c 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,14 @@ of a `Hashtbl.t`, without creating a list. Documentation ============= -See [the online API](http://cedeela.fr/~simon/software/sequence/Sequence.html). +There is only one type, `'a Sequence.t`, and lots of functions built around +this type. +To get an overview of sequence, its origins and why it was created, +you can start with [the slides of a talk](http://cedeela.fr/~simon/talks/sequence.pdf) +I (c-cube) made at some OCaml meeting. + +See [the online API](http://cedeela.fr/~simon/software/sequence/Sequence.html) +for more details on the set of available functions. Build ===== diff --git a/_oasis b/_oasis index 68bb3cf..892d963 100644 --- a/_oasis +++ b/_oasis @@ -1,6 +1,6 @@ OASISFormat: 0.4 Name: sequence -Version: 0.7 +Version: 0.8 Homepage: https://github.com/c-cube/sequence Authors: Simon Cruanes License: BSD-2-clause @@ -28,12 +28,12 @@ Flag bigarray Default: true Library "sequence" - Path: . + Path: src/ Modules: Sequence, SequenceLabels BuildDepends: bytes Library "invert" - Path: invert + Path: src/invert Build$: flag(invert) Install$: flag(invert) Modules: SequenceInvert @@ -42,7 +42,7 @@ Library "invert" BuildDepends: sequence,delimcc Library "bigarray" - Path: bigarray + Path: src/bigarray Build$: flag(bigarray) Install$: flag(bigarray) Modules: SequenceBigarray @@ -58,19 +58,20 @@ Document sequence XOCamlbuildPath: . XOCamlbuildLibraries: sequence -Test all - Type: custom (0.4) - Command: make run-tests - TestTools: run_tests - Run$: flag(tests) +PreBuildCommand: make qtest-gen -Executable run_tests - Path: tests/ +Executable run_qtest + Path: qtest/ Install: false CompiledObject: native - MainIs: run_tests.ml + MainIs: run_qtest.ml Build$: flag(tests) - BuildDepends: sequence,oUnit + BuildDepends: sequence, qcheck + +Test all + Command: ./run_qtest.native + TestTools: run_qtest + Run$: flag(tests) Executable benchs Path: bench diff --git a/_tags b/_tags index d1fa2c5..80091d0 100644 --- a/_tags +++ b/_tags @@ -1,5 +1,5 @@ # OASIS_START -# DO NOT EDIT (digest: 29e0c9fc65daf16caa16466d6ff32bac) +# DO NOT EDIT (digest: 5495b2a66019398a5b6c2172a2c3ba42) # Ignore VCS directories, you can use the same kind of rule outside # OASIS_START/STOP if you want to exclude directories that contains # useless stuff for the build process @@ -15,25 +15,25 @@ true: annot, bin_annot "_darcs": -traverse "_darcs": not_hygienic # Library sequence -"sequence.cmxs": use_sequence -<*.ml{,i,y}>: pkg_bytes +"src/sequence.cmxs": use_sequence +: pkg_bytes # Library invert -"invert/invert.cmxs": use_invert -: pkg_bytes -: pkg_delimcc -: use_sequence +"src/invert/invert.cmxs": use_invert +: pkg_bytes +: pkg_delimcc +: use_sequence # Library bigarray -"bigarray/bigarray.cmxs": use_bigarray -: pkg_bigarray -: pkg_bytes -: use_sequence -# Executable run_tests -"tests/run_tests.native": pkg_bytes -"tests/run_tests.native": pkg_oUnit -"tests/run_tests.native": use_sequence -: pkg_bytes -: pkg_oUnit -: use_sequence +"src/bigarray/bigarray.cmxs": use_bigarray +: pkg_bigarray +: pkg_bytes +: use_sequence +# Executable run_qtest +"qtest/run_qtest.native": pkg_bytes +"qtest/run_qtest.native": pkg_qcheck +"qtest/run_qtest.native": use_sequence +: pkg_bytes +: pkg_qcheck +: use_sequence # Executable benchs "bench/benchs.native": pkg_benchmark "bench/benchs.native": pkg_bytes @@ -53,4 +53,5 @@ true: annot, bin_annot true: bin_annot <**/*.ml>: warn_A, warn(-4) true: mark_tag_used -: nolabels +<**/*.cmx>: optimize(3) +: nolabels diff --git a/myocamlbuild.ml b/myocamlbuild.ml index 1cfd88e..9c3205d 100644 --- a/myocamlbuild.ml +++ b/myocamlbuild.ml @@ -1,5 +1,5 @@ (* OASIS_START *) -(* DO NOT EDIT (digest: 2ea21bad023bcdcb9626e204d039d0d2) *) +(* DO NOT EDIT (digest: 8cefc2cf375bc79d721299976e7d0715) *) module OASISGettext = struct (* # 22 "src/oasis/OASISGettext.ml" *) @@ -29,6 +29,166 @@ module OASISGettext = struct end +module OASISString = struct +(* # 22 "src/oasis/OASISString.ml" *) + + + (** Various string utilities. + + Mostly inspired by extlib and batteries ExtString and BatString libraries. + + @author Sylvain Le Gall + *) + + + let nsplitf str f = + if str = "" then + [] + else + let buf = Buffer.create 13 in + let lst = ref [] in + let push () = + lst := Buffer.contents buf :: !lst; + Buffer.clear buf + in + let str_len = String.length str in + for i = 0 to str_len - 1 do + if f str.[i] then + push () + else + Buffer.add_char buf str.[i] + done; + push (); + List.rev !lst + + + (** [nsplit c s] Split the string [s] at char [c]. It doesn't include the + separator. + *) + let nsplit str c = + nsplitf str ((=) c) + + + let find ~what ?(offset=0) str = + let what_idx = ref 0 in + let str_idx = ref offset in + while !str_idx < String.length str && + !what_idx < String.length what do + if str.[!str_idx] = what.[!what_idx] then + incr what_idx + else + what_idx := 0; + incr str_idx + done; + if !what_idx <> String.length what then + raise Not_found + else + !str_idx - !what_idx + + + let sub_start str len = + let str_len = String.length str in + if len >= str_len then + "" + else + String.sub str len (str_len - len) + + + let sub_end ?(offset=0) str len = + let str_len = String.length str in + if len >= str_len then + "" + else + String.sub str 0 (str_len - len) + + + let starts_with ~what ?(offset=0) str = + let what_idx = ref 0 in + let str_idx = ref offset in + let ok = ref true in + while !ok && + !str_idx < String.length str && + !what_idx < String.length what do + if str.[!str_idx] = what.[!what_idx] then + incr what_idx + else + ok := false; + incr str_idx + done; + if !what_idx = String.length what then + true + else + false + + + let strip_starts_with ~what str = + if starts_with ~what str then + sub_start str (String.length what) + else + raise Not_found + + + let ends_with ~what ?(offset=0) str = + let what_idx = ref ((String.length what) - 1) in + let str_idx = ref ((String.length str) - 1) in + let ok = ref true in + while !ok && + offset <= !str_idx && + 0 <= !what_idx do + if str.[!str_idx] = what.[!what_idx] then + decr what_idx + else + ok := false; + decr str_idx + done; + if !what_idx = -1 then + true + else + false + + + let strip_ends_with ~what str = + if ends_with ~what str then + sub_end str (String.length what) + else + raise Not_found + + + let replace_chars f s = + let buf = Buffer.create (String.length s) in + String.iter (fun c -> Buffer.add_char buf (f c)) s; + Buffer.contents buf + + let lowercase_ascii = + replace_chars + (fun c -> + if (c >= 'A' && c <= 'Z') then + Char.chr (Char.code c + 32) + else + c) + + let uncapitalize_ascii s = + if s <> "" then + (lowercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s + + let uppercase_ascii = + replace_chars + (fun c -> + if (c >= 'a' && c <= 'z') then + Char.chr (Char.code c - 32) + else + c) + + let capitalize_ascii s = + if s <> "" then + (uppercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s + +end + module OASISExpr = struct (* # 22 "src/oasis/OASISExpr.ml" *) @@ -129,7 +289,7 @@ module OASISExpr = struct end -# 132 "myocamlbuild.ml" +# 292 "myocamlbuild.ml" module BaseEnvLight = struct (* # 22 "src/base/BaseEnvLight.ml" *) @@ -234,7 +394,7 @@ module BaseEnvLight = struct end -# 237 "myocamlbuild.ml" +# 397 "myocamlbuild.ml" module MyOCamlbuildFindlib = struct (* # 22 "src/plugins/ocamlbuild/MyOCamlbuildFindlib.ml" *) @@ -516,7 +676,7 @@ module MyOCamlbuildBase = struct | nm, [], intf_modules -> ocaml_lib nm; let cmis = - List.map (fun m -> (String.uncapitalize m) ^ ".cmi") + List.map (fun m -> (OASISString.uncapitalize_ascii m) ^ ".cmi") intf_modules in dep ["ocaml"; "link"; "library"; "file:"^nm^".cma"] cmis | nm, dir :: tl, intf_modules -> @@ -529,7 +689,7 @@ module MyOCamlbuildBase = struct ["compile"; "infer_interface"; "doc"]) tl; let cmis = - List.map (fun m -> dir^"/"^(String.uncapitalize m)^".cmi") + List.map (fun m -> dir^"/"^(OASISString.uncapitalize_ascii m)^".cmi") intf_modules in dep ["ocaml"; "link"; "library"; "file:"^dir^"/"^nm^".cma"] cmis) @@ -603,19 +763,25 @@ module MyOCamlbuildBase = struct end -# 606 "myocamlbuild.ml" +# 766 "myocamlbuild.ml" open Ocamlbuild_plugin;; let package_default = { MyOCamlbuildBase.lib_ocaml = [ - ("sequence", [], []); - ("invert", ["invert"], []); - ("bigarray", ["bigarray"], []) + ("sequence", ["src"], []); + ("invert", ["src/invert"], []); + ("bigarray", ["src/bigarray"], []) ]; lib_c = []; flags = []; - includes = [] + includes = + [ + ("src/invert", ["src"]); + ("src/bigarray", ["src"]); + ("qtest", ["src"]); + ("bench", ["src"]) + ] } ;; @@ -623,6 +789,6 @@ let conf = {MyOCamlbuildFindlib.no_automatic_syntax = false} let dispatch_default = MyOCamlbuildBase.dispatch_default conf package_default;; -# 627 "myocamlbuild.ml" +# 793 "myocamlbuild.ml" (* OASIS_STOP *) Ocamlbuild_plugin.dispatch dispatch_default;; diff --git a/opam b/opam index 397ca53..07fe8e7 100644 --- a/opam +++ b/opam @@ -1,8 +1,9 @@ opam-version: "1.2" name: "sequence" -version: "0.7" +version: "0.8" author: "Simon Cruanes" maintainer: "simon.cruanes@inria.fr" +license: "BSD-2-clauses" build: [ ["./configure" "--disable-docs" "--%{delimcc:enable}%-invert" diff --git a/sequence.odocl b/sequence.odocl index 52e69a0..783adab 100644 --- a/sequence.odocl +++ b/sequence.odocl @@ -1,5 +1,5 @@ # OASIS_START -# DO NOT EDIT (digest: 8c0ffebbdb3e063d4b3e5cc00517b199) -Sequence -SequenceLabels +# DO NOT EDIT (digest: 5ddf4abb4ebf6a133d6ce26868d8dbf0) +src/Sequence +src/SequenceLabels # OASIS_STOP diff --git a/sequenceLabels.ml b/sequenceLabels.ml deleted file mode 100644 index fb15e92..0000000 --- a/sequenceLabels.ml +++ /dev/null @@ -1 +0,0 @@ -include Sequence diff --git a/setup.ml b/setup.ml index 559a871..9c56fd3 100644 --- a/setup.ml +++ b/setup.ml @@ -1,9 +1,9 @@ (* setup.ml generated for the first time by OASIS v0.4.4 *) (* OASIS_START *) -(* DO NOT EDIT (digest: e28e259a63b26395383267decca6401e) *) +(* DO NOT EDIT (digest: 22604e4a6a5b4cfdf26c5f4f4ed84058) *) (* - Regenerated by OASIS v0.4.5 + Regenerated by OASIS v0.4.6 Visit http://oasis.forge.ocamlcore.org for more information and documentation about functions used in this file. *) @@ -246,6 +246,33 @@ module OASISString = struct String.iter (fun c -> Buffer.add_char buf (f c)) s; Buffer.contents buf + let lowercase_ascii = + replace_chars + (fun c -> + if (c >= 'A' && c <= 'Z') then + Char.chr (Char.code c + 32) + else + c) + + let uncapitalize_ascii s = + if s <> "" then + (lowercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s + + let uppercase_ascii = + replace_chars + (fun c -> + if (c >= 'a' && c <= 'z') then + Char.chr (Char.code c - 32) + else + c) + + let capitalize_ascii s = + if s <> "" then + (uppercase_ascii (String.sub s 0 1)) ^ (String.sub s 1 ((String.length s) - 1)) + else + s end @@ -315,19 +342,15 @@ module OASISUtils = struct let compare_csl s1 s2 = - String.compare (String.lowercase s1) (String.lowercase s2) + String.compare (OASISString.lowercase_ascii s1) (OASISString.lowercase_ascii s2) module HashStringCsl = Hashtbl.Make (struct type t = string - - let equal s1 s2 = - (String.lowercase s1) = (String.lowercase s2) - - let hash s = - Hashtbl.hash (String.lowercase s) + let equal s1 s2 = (compare_csl s1 s2) = 0 + let hash s = Hashtbl.hash (OASISString.lowercase_ascii s) end) module SetStringCsl = @@ -365,7 +388,7 @@ module OASISUtils = struct else buf in - String.lowercase buf + OASISString.lowercase_ascii buf end @@ -471,7 +494,7 @@ module PropList = struct order = Queue.create (); name_norm = (if case_insensitive then - String.lowercase + OASISString.lowercase_ascii else fun s -> s); } @@ -1822,13 +1845,13 @@ module OASISUnixPath = struct let capitalize_file f = let dir = dirname f in let base = basename f in - concat dir (String.capitalize base) + concat dir (OASISString.capitalize_ascii base) let uncapitalize_file f = let dir = dirname f in let base = basename f in - concat dir (String.uncapitalize base) + concat dir (OASISString.uncapitalize_ascii base) end @@ -2890,7 +2913,7 @@ module OASISFileUtil = struct end -# 2893 "setup.ml" +# 2916 "setup.ml" module BaseEnvLight = struct (* # 22 "src/base/BaseEnvLight.ml" *) @@ -2995,7 +3018,7 @@ module BaseEnvLight = struct end -# 2998 "setup.ml" +# 3021 "setup.ml" module BaseContext = struct (* # 22 "src/base/BaseContext.ml" *) @@ -5406,7 +5429,7 @@ module BaseSetup = struct end -# 5409 "setup.ml" +# 5432 "setup.ml" module InternalConfigurePlugin = struct (* # 22 "src/plugins/internal/InternalConfigurePlugin.ml" *) @@ -5845,8 +5868,8 @@ module InternalInstallPlugin = struct let make_fnames modul sufx = List.fold_right begin fun sufx accu -> - (String.capitalize modul ^ sufx) :: - (String.uncapitalize modul ^ sufx) :: + (OASISString.capitalize_ascii modul ^ sufx) :: + (OASISString.uncapitalize_ascii modul ^ sufx) :: accu end sufx @@ -6270,7 +6293,7 @@ module InternalInstallPlugin = struct end -# 6273 "setup.ml" +# 6296 "setup.ml" module OCamlbuildCommon = struct (* # 22 "src/plugins/ocamlbuild/OCamlbuildCommon.ml" *) @@ -6648,7 +6671,7 @@ module OCamlbuildDocPlugin = struct end -# 6651 "setup.ml" +# 6674 "setup.ml" module CustomPlugin = struct (* # 22 "src/plugins/custom/CustomPlugin.ml" *) @@ -6796,7 +6819,7 @@ module CustomPlugin = struct end -# 6799 "setup.ml" +# 6822 "setup.ml" open OASISTypes;; let setup_t = @@ -6809,7 +6832,7 @@ let setup_t = CustomPlugin.Test.main { CustomPlugin.cmd_main = - [(OASISExpr.EBool true, ("make", ["run-tests"]))]; + [(OASISExpr.EBool true, ("./run_qtest.native", []))]; cmd_clean = [(OASISExpr.EBool true, None)]; cmd_distclean = [(OASISExpr.EBool true, None)] }) @@ -6829,7 +6852,7 @@ let setup_t = CustomPlugin.Test.clean { CustomPlugin.cmd_main = - [(OASISExpr.EBool true, ("make", ["run-tests"]))]; + [(OASISExpr.EBool true, ("./run_qtest.native", []))]; cmd_clean = [(OASISExpr.EBool true, None)]; cmd_distclean = [(OASISExpr.EBool true, None)] }) @@ -6847,7 +6870,7 @@ let setup_t = CustomPlugin.Test.distclean { CustomPlugin.cmd_main = - [(OASISExpr.EBool true, ("make", ["run-tests"]))]; + [(OASISExpr.EBool true, ("./run_qtest.native", []))]; cmd_clean = [(OASISExpr.EBool true, None)]; cmd_distclean = [(OASISExpr.EBool true, None)] }) @@ -6892,7 +6915,8 @@ let setup_t = build_type = (`Build, "ocamlbuild", Some "0.4"); build_custom = { - pre_command = [(OASISExpr.EBool true, None)]; + pre_command = + [(OASISExpr.EBool true, Some (("make", ["qtest-gen"])))]; post_command = [(OASISExpr.EBool true, None)] }; install_type = (`Install, "internal", Some "0.4"); @@ -6961,7 +6985,7 @@ let setup_t = { bs_build = [(OASISExpr.EBool true, true)]; bs_install = [(OASISExpr.EBool true, true)]; - bs_path = "."; + bs_path = "src/"; bs_compiled_object = Best; bs_build_depends = [FindlibPackage ("bytes", None)]; bs_build_tools = [ExternalTool "ocamlbuild"]; @@ -6999,7 +7023,7 @@ let setup_t = (OASISExpr.EBool true, false); (OASISExpr.EFlag "invert", true) ]; - bs_path = "invert"; + bs_path = "src/invert"; bs_compiled_object = Best; bs_build_depends = [ @@ -7041,7 +7065,7 @@ let setup_t = (OASISExpr.EBool true, false); (OASISExpr.EFlag "bigarray", true) ]; - bs_path = "bigarray"; + bs_path = "src/bigarray"; bs_compiled_object = Best; bs_build_depends = [ @@ -7096,7 +7120,7 @@ let setup_t = }); Executable ({ - cs_name = "run_tests"; + cs_name = "run_qtest"; cs_data = PropList.Data.create (); cs_plugin_data = [] }, @@ -7107,12 +7131,12 @@ let setup_t = (OASISExpr.EFlag "tests", true) ]; bs_install = [(OASISExpr.EBool true, false)]; - bs_path = "tests/"; + bs_path = "qtest/"; bs_compiled_object = Native; bs_build_depends = [ InternalLibrary "sequence"; - FindlibPackage ("oUnit", None) + FindlibPackage ("qcheck", None) ]; bs_build_tools = [ExternalTool "ocamlbuild"]; bs_c_sources = []; @@ -7124,7 +7148,7 @@ let setup_t = bs_byteopt = [(OASISExpr.EBool true, [])]; bs_nativeopt = [(OASISExpr.EBool true, [])] }, - {exec_custom = false; exec_main_is = "run_tests.ml"}); + {exec_custom = false; exec_main_is = "run_qtest.ml"}); Test ({ cs_name = "all"; @@ -7134,7 +7158,7 @@ let setup_t = { test_type = (`Test, "custom", Some "0.4"); test_command = - [(OASISExpr.EBool true, ("make", ["run-tests"]))]; + [(OASISExpr.EBool true, ("./run_qtest.native", []))]; test_custom = { pre_command = [(OASISExpr.EBool true, None)]; @@ -7153,7 +7177,7 @@ let setup_t = test_tools = [ ExternalTool "ocamlbuild"; - InternalExecutable "run_tests" + InternalExecutable "run_qtest" ] }); Executable @@ -7279,8 +7303,8 @@ let setup_t = plugin_data = [] }; oasis_fn = Some "_oasis"; - oasis_version = "0.4.5"; - oasis_digest = Some "\247\213|\155\007DgsAe\210\221|\198\232\237"; + oasis_version = "0.4.6"; + oasis_digest = Some "r\242\252(l\210\150\168\252\213\181\209 *[\200"; oasis_exec = None; oasis_setup_args = []; setup_update = false @@ -7288,6 +7312,6 @@ let setup_t = let setup () = BaseSetup.setup setup_t;; -# 7292 "setup.ml" +# 7316 "setup.ml" (* OASIS_STOP *) let () = setup ();; diff --git a/src/META b/src/META new file mode 100644 index 0000000..fb45f2c --- /dev/null +++ b/src/META @@ -0,0 +1,33 @@ +# OASIS_START +# DO NOT EDIT (digest: 8041ba3970fcecd2b690dc7b78ccae41) +version = "0.7" +description = "Simple sequence (iterator) datatype and combinators" +requires = "bytes" +archive(byte) = "sequence.cma" +archive(byte, plugin) = "sequence.cma" +archive(native) = "sequence.cmxa" +archive(native, plugin) = "sequence.cmxs" +exists_if = "sequence.cma" +package "invert" ( + version = "0.7" + description = "Simple sequence (iterator) datatype and combinators" + requires = "sequence delimcc" + archive(byte) = "invert.cma" + archive(byte, plugin) = "invert.cma" + archive(native) = "invert.cmxa" + archive(native, plugin) = "invert.cmxs" + exists_if = "invert.cma" +) + +package "bigarray" ( + version = "0.7" + description = "Simple sequence (iterator) datatype and combinators" + requires = "sequence bigarray" + archive(byte) = "bigarray.cma" + archive(byte, plugin) = "bigarray.cma" + archive(native) = "bigarray.cmxa" + archive(native, plugin) = "bigarray.cmxs" + exists_if = "bigarray.cma" +) +# OASIS_STOP + diff --git a/src/bigarray/bigarray.mldylib b/src/bigarray/bigarray.mldylib new file mode 100644 index 0000000..f817c41 --- /dev/null +++ b/src/bigarray/bigarray.mldylib @@ -0,0 +1,4 @@ +# OASIS_START +# DO NOT EDIT (digest: dca476c3b57e859aa3b1c75ec0959ed9) +SequenceBigarray +# OASIS_STOP diff --git a/src/bigarray/bigarray.mllib b/src/bigarray/bigarray.mllib new file mode 100644 index 0000000..f817c41 --- /dev/null +++ b/src/bigarray/bigarray.mllib @@ -0,0 +1,4 @@ +# OASIS_START +# DO NOT EDIT (digest: dca476c3b57e859aa3b1c75ec0959ed9) +SequenceBigarray +# OASIS_STOP diff --git a/bigarray/sequenceBigarray.ml b/src/bigarray/sequenceBigarray.ml similarity index 100% rename from bigarray/sequenceBigarray.ml rename to src/bigarray/sequenceBigarray.ml diff --git a/bigarray/sequenceBigarray.mli b/src/bigarray/sequenceBigarray.mli similarity index 100% rename from bigarray/sequenceBigarray.mli rename to src/bigarray/sequenceBigarray.mli diff --git a/invert/.merlin b/src/invert/.merlin similarity index 100% rename from invert/.merlin rename to src/invert/.merlin diff --git a/src/invert/invert.mldylib b/src/invert/invert.mldylib new file mode 100644 index 0000000..b031b43 --- /dev/null +++ b/src/invert/invert.mldylib @@ -0,0 +1,4 @@ +# OASIS_START +# DO NOT EDIT (digest: d74492d261fcc87665b60e0331c04236) +SequenceInvert +# OASIS_STOP diff --git a/src/invert/invert.mllib b/src/invert/invert.mllib new file mode 100644 index 0000000..b031b43 --- /dev/null +++ b/src/invert/invert.mllib @@ -0,0 +1,4 @@ +# OASIS_START +# DO NOT EDIT (digest: d74492d261fcc87665b60e0331c04236) +SequenceInvert +# OASIS_STOP diff --git a/invert/sequenceInvert.ml b/src/invert/sequenceInvert.ml similarity index 100% rename from invert/sequenceInvert.ml rename to src/invert/sequenceInvert.ml diff --git a/invert/sequenceInvert.mli b/src/invert/sequenceInvert.mli similarity index 100% rename from invert/sequenceInvert.mli rename to src/invert/sequenceInvert.mli diff --git a/sequence.ml b/src/sequence.ml similarity index 75% rename from sequence.ml rename to src/sequence.ml index b1d9ad4..96d2452 100644 --- a/sequence.ml +++ b/src/sequence.ml @@ -11,6 +11,10 @@ type 'a sequence = 'a t type (+'a, +'b) t2 = ('a -> 'b -> unit) -> unit (** Sequence of pairs of values of type ['a] and ['b]. *) +(*$inject + let pp_ilist = Q.Print.(list int) +*) + (** Build a sequence from a iter function *) let from_iter f = f @@ -20,6 +24,13 @@ let rec from_fun f k = match f () with let empty _ = () +(*$R + let seq = empty in + OUnit.assert_bool "empty" (is_empty seq); + OUnit.assert_bool "empty" + (try iter (fun _ -> raise Exit) seq; true with Exit -> false); +*) + let singleton x k = k x let return x k = k x let pure f k = k f @@ -31,6 +42,12 @@ let snoc l x k = l k; k x let repeat x k = while true do k x done +(*$R + let seq = repeat "hello" in + OUnit.assert_equal ["hello"; "hello"; "hello"] + (seq |> take 3 |> to_list); +*) + let rec iterate f x k = k x; iterate f (f x) k @@ -55,6 +72,12 @@ let fold f init seq = seq (fun elt -> r := f !r elt); !r +(*$R + let n = (1 -- 10) + |> fold (+) 0 in + OUnit.assert_equal 55 n; +*) + let foldi f init seq = let i = ref 0 in let r = ref init in @@ -64,6 +87,13 @@ let foldi f init seq = incr i); !r +(*$R + let l = ["hello"; "world"] + |> of_list + |> foldi (fun acc i x -> (i,x) :: acc) [] in + OUnit.assert_equal [1, "world"; 0, "hello"] l; +*) + let map f seq k = seq (fun x -> k (f x)) let mapi f seq k = @@ -86,12 +116,37 @@ let append s1 s2 k = s1 k; s2 k let concat s k = s (fun s' -> s' k) +(*$R + let s1 = (1 -- 5) in + let s2 = (6 -- 10) in + let l = [1;2;3;4;5;6;7;8;9;10] in + OUnit.assert_equal l (to_list (append s1 s2)); +*) + +(*$R + (1 -- 1000) + |> map (fun i -> i -- (i+1)) + |> concat + |> length + |> OUnit.assert_equal 2000 +*) + let flatten s = concat s let flatMap f seq k = seq (fun x -> f x k) let flat_map = flatMap +(*$R + (1 -- 1000) + |> flat_map (fun i -> i -- (i+1)) + |> length + |> OUnit.assert_equal 2000 +*) + +let flat_map_l f seq k = + seq (fun x -> List.iter k (f x)) + let fmap f seq k = seq (fun x -> match f x with | None -> () @@ -104,6 +159,14 @@ let intersperse elem seq k = let first = ref true in seq (fun x -> (if !first then first := false else k elem); k x) +(*$R + (1 -- 100) + |> (fun seq -> intersperse 0 seq) + |> take 10 + |> to_list + |> OUnit.assert_equal [1;0;2;0;3;0;4;0;5;0] +*) + (** Mutable unrolled list to serve as intermediate storage *) module MList = struct type 'a node = @@ -204,6 +267,35 @@ let persistent seq = let l = MList.of_seq seq in MList.to_seq l +(*$R + let printer = pp_ilist in + let stream = Stream.from (fun i -> if i < 5 then Some i else None) in + let seq = of_stream stream in + OUnit.assert_equal ~printer [0;1;2;3;4] (seq |> to_list); + OUnit.assert_equal ~printer [] (seq |> to_list); +*) + +(*$R + let printer = pp_ilist in + let stream = Stream.from (fun i -> if i < 5 then Some i else None) in + let seq = of_stream stream in + (* consume seq into a persistent version of itself *) + let seq' = persistent seq in + OUnit.assert_equal ~printer [] (seq |> to_list); + OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> to_list); + OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> to_list); + OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> to_stream |> of_stream |> to_list); +*) + +(*$R + let printer = pp_ilist in + let seq = (0 -- 10_000) in + let seq' = persistent seq in + OUnit.assert_equal 10_001 (length seq'); + OUnit.assert_equal 10_001 (length seq'); + OUnit.assert_equal ~printer [0;1;2;3] (seq' |> take 4 |> to_list); +*) + type 'a lazy_state = | LazySuspend | LazyCached of 'a t @@ -224,6 +316,14 @@ let sort ?(cmp=Pervasives.compare) seq = let l = List.fast_sort cmp l in fun k -> List.iter k l +(*$R + (1 -- 100) + |> sort ~cmp:(fun i j -> j - i) + |> take 4 + |> to_list + |> OUnit.assert_equal [100;99;98;97] +*) + let group_succ_by ?(eq=fun x y -> x = y) seq k = let cur = ref [] in seq (fun x -> @@ -239,6 +339,12 @@ let group_succ_by ?(eq=fun x y -> x = y) seq k = let group = group_succ_by +(*$R + [1;2;3;3;2;2;3;4] + |> of_list |> group_succ_by ?eq:None |> to_list + |> OUnit.assert_equal [[1];[2];[3;3];[2;2];[3];[4]] +*) + let group_by (type k) ?(hash=Hashtbl.hash) ?(eq=(=)) seq = let module Tbl = Hashtbl.Make(struct type t = k @@ -255,6 +361,12 @@ let group_by (type k) ?(hash=Hashtbl.hash) ?(eq=(=)) seq = fun yield -> Tbl.iter (fun _ l -> yield l) tbl +(*$R + [1;2;3;3;2;2;3;4] + |> of_list |> group_by ?eq:None ?hash:None |> sort ?cmp:None |> to_list + |> OUnit.assert_equal [[1];[2;2;2];[3;3;3];[4]] +*) + let uniq ?(eq=fun x y -> x = y) seq k = let has_prev = ref false and prev = ref (Obj.magic 0) in (* avoid option type, costly *) @@ -268,6 +380,12 @@ let uniq ?(eq=fun x y -> x = y) seq k = k x )) +(*$R + [1;2;2;3;4;4;4;3;3] + |> of_list |> uniq ?eq:None |> to_list + |> OUnit.assert_equal [1;2;3;4;3] +*) + let sort_uniq (type elt) ?(cmp=Pervasives.compare) seq = let module S = Set.Make(struct type t = elt @@ -276,9 +394,28 @@ let sort_uniq (type elt) ?(cmp=Pervasives.compare) seq = let set = fold (fun acc x -> S.add x acc) S.empty seq in fun k -> S.iter k set +(*$R + [42;1;2;3;4;5;4;3;2;1] + |> of_list + |> sort_uniq ?cmp:None + |> to_list + |> OUnit.assert_equal [1;2;3;4;5;42] +*) + let product outer inner k = outer (fun x -> inner (fun y -> k (x,y))) +(*$R + let stream = Stream.from (fun i -> if i < 3 then Some i else None) in + let a = of_stream stream in + let b = of_list ["a";"b";"c"] in + let s = product a b |> map (fun (x,y) -> y,x) + |> to_list |> List.sort compare in + OUnit.assert_equal ["a",0; "a", 1; "a", 2; + "b",0; "b", 1; "b", 2; + "c",0; "c", 1; "c", 2;] s +*) + let product2 outer inner k = outer (fun x -> inner (fun y -> k x y)) @@ -289,17 +426,41 @@ let join ~join_row s1 s2 k = | None -> () | Some c -> k c)) +(*$R + let s1 = (1 -- 3) in + let s2 = of_list ["1"; "2"] in + let join_row i j = + if string_of_int i = j then Some (string_of_int i ^ " = " ^ j) else None + in + let s = join ~join_row s1 s2 in + OUnit.assert_equal ["1 = 1"; "2 = 2"] (to_list s); +*) + let rec unfoldr f b k = match f b with | None -> () | Some (x, b') -> k x; unfoldr f b' k +(*$R + let f x = if x < 5 then Some (string_of_int x,x+1) else None in + unfoldr f 0 + |> to_list + |> OUnit.assert_equal ["0"; "1"; "2"; "3"; "4"] +*) + let scan f acc seq k = k acc; let acc = ref acc in seq (fun elt -> let acc' = f !acc elt in k acc'; acc := acc') +(*$R + (1 -- 5) + |> scan (+) 0 + |> to_list + |> OUnit.assert_equal ~printer:pp_ilist [0;1;3;6;10;15] +*) + let max ?(lt=fun x y -> x < y) seq = let ret = ref None in seq @@ -341,6 +502,13 @@ let take n seq k = k x) with ExitTake -> () +(*$R + let l = to_list (take 0 (of_list [1])) in + OUnit.assert_equal ~printer:pp_ilist [] l; + let l = to_list (take 5 (of_list [1;2;3;4;5;6;7;8;9;10])) in + OUnit.assert_equal ~printer:pp_ilist [1;2;3;4;5] l; +*) + exception ExitTakeWhile let take_while p seq k = @@ -362,11 +530,20 @@ let fold_while f s seq = try seq consume; !state with ExitFoldWhile -> !state +(*$R + let n = of_list [true;true;false;true] + |> fold_while (fun acc b -> if b then acc+1, `Continue else acc, `Stop) 0 in + OUnit.assert_equal 2 n; +*) let drop n seq k = let count = ref 0 in seq (fun x -> if !count >= n then k x else incr count) +(*$R + (1 -- 5) |> drop 2 |> to_list |> OUnit.assert_equal [3;4;5] +*) + let drop_while p seq k = let drop = ref true in seq @@ -379,6 +556,10 @@ let rev seq = let l = MList.of_seq seq in fun k -> MList.iter_rev k l +(*$R + (1 -- 5) |> rev |> to_list |> OUnit.assert_equal [5;4;3;2;1] +*) + exception ExitForall let for_all p seq = @@ -387,6 +568,16 @@ let for_all p seq = true with ExitForall -> false +(*$R + OUnit.assert_bool "true" (for_all (fun x -> x < 10) (1--9)); + OUnit.assert_bool "false" (not (for_all (fun x -> x < 10) (2--11))); + OUnit.assert_bool "true" (for_all (fun _ -> false) empty); + OUnit.assert_bool "nested" + (for_all + (fun seq -> not (for_all (fun x -> x < 8) seq)) + (1 -- 10 >|= fun x -> x--20)); +*) + exception ExitExists (** Exists there some element satisfying the predicate? *) @@ -396,6 +587,16 @@ let exists p seq = false with ExitExists -> true +(*$R + (1 -- 100) + |> exists (fun x -> x = 59) + |> OUnit.assert_bool "exists"; + (1 -- 100) + |> exists (fun x -> x < 0) + |> (fun x -> not x) + |> OUnit.assert_bool "not exists"; +*) + let mem ?(eq=(=)) x seq = exists (eq x) seq exception ExitFind @@ -417,6 +618,10 @@ let length seq = seq (fun _ -> incr r); !r +(*$R + (1 -- 1000) |> length |> OUnit.assert_equal 1000 +*) + exception ExitIsEmpty let is_empty seq = @@ -519,6 +724,15 @@ let of_queue q k = Queue.iter k q let hashtbl_add h seq = seq (fun (k,v) -> Hashtbl.add h k v) +(*$R + let h = (1 -- 5) + |> zip_i + |> to_hashtbl2 in + (0 -- 4) + |> iter (fun i -> OUnit.assert_equal (i+1) (Hashtbl.find h i)); + OUnit.assert_equal [0;1;2;3;4] (hashtbl_keys h |> sort ?cmp:None |> to_list); +*) + let hashtbl_replace h seq = seq (fun (k,v) -> Hashtbl.replace h k v) @@ -570,13 +784,50 @@ let of_in_channel ic = let to_buffer seq buf = seq (fun c -> Buffer.add_char buf c) +(*$R + let b = Buffer.create 4 in + "hello world" + |> of_str |> rev |> map Char.uppercase + |> (fun seq -> to_buffer seq b); + OUnit.assert_equal "DLROW OLLEH" (Buffer.contents b); +*) + (** Iterator on integers in [start...stop] by steps 1 *) let int_range ~start ~stop k = for i = start to stop do k i done +(*$R + OUnit.assert_equal ~printer:pp_ilist [1;2;3;4] (to_list (1--4)); + OUnit.assert_equal ~printer:pp_ilist [10;9;8;7;6] (to_list (10 --^ 6)); + OUnit.assert_equal ~printer:pp_ilist [] (to_list (10--4)); + OUnit.assert_equal ~printer:pp_ilist [] (to_list (10 --^ 60)); +*) + let int_range_dec ~start ~stop k = for i = start downto stop do k i done +let int_range_by ~step i j yield = + if step=0 then invalid_arg "int_range_by"; + for k = 0 to (j - i) / step do + yield (k * step + i) + done + +(*$= & ~printer:Q.Print.(list int) + [1;2;3;4] (int_range_by ~step:1 1 4 |> to_list) + [4;3;2;1] (int_range_by ~step:~-1 4 1 |> to_list) + [6;4;2] (int_range_by 6 1 ~step:~-2 |> to_list) + [] (int_range_by ~step:1 4 1 |> to_list) +*) + +(*$Q + Q.(pair small_int small_int) (fun (i,j) -> \ + let i = Pervasives.min i j and j = Pervasives.max i j in \ + (i--j |> to_list) = (int_range_by ~step:1 i j |> to_list)) + Q.(pair small_int small_int) (fun (i,j) -> \ + let i = Pervasives.min i j and j = Pervasives.max i j in \ + (i--j |> to_rev_list) = (int_range_by ~step:~-1 j i |> to_list)) +*) + let bools k = k false; k true let of_set (type s) (type v) m set = @@ -856,3 +1107,10 @@ module IO = struct let write_lines ?mode ?flags filename seq = write_bytes_lines ?mode ?flags filename (map Bytes.unsafe_of_string seq) end + +(* regression tests *) + +(*$R + let s = (take 10 (repeat 1)) in + OUnit.assert_bool "not empty" (not (is_empty s)); +*) diff --git a/src/sequence.mldylib b/src/sequence.mldylib new file mode 100644 index 0000000..52e69a0 --- /dev/null +++ b/src/sequence.mldylib @@ -0,0 +1,5 @@ +# OASIS_START +# DO NOT EDIT (digest: 8c0ffebbdb3e063d4b3e5cc00517b199) +Sequence +SequenceLabels +# OASIS_STOP diff --git a/sequence.mli b/src/sequence.mli similarity index 98% rename from sequence.mli rename to src/sequence.mli index c4e3a90..0cc6dd5 100644 --- a/sequence.mli +++ b/src/sequence.mli @@ -158,6 +158,10 @@ val flat_map : ('a -> 'b t) -> 'a t -> 'b t element of the initial sequence, and calls {!concat}. @since 0.5 *) +val flat_map_l : ('a -> 'b list) -> 'a t -> 'b t +(** Convenience function combining {!flat_map} and {!of_list} + @since 0.8 *) + val fmap : ('a -> 'b option) -> 'a t -> 'b t (** @deprecated use {!filter_map} since 0.6 *) @@ -417,6 +421,12 @@ val int_range_dec : start:int -> stop:int -> int t (** Iterator on decreasing integers in [stop...start] by steps -1. See {!(--^)} for an infix version *) +val int_range_by : step:int -> int -> int -> int t +(** [int_range_by ~step i j] is the range starting at [i], including [j], + where the difference between successive elements is [step]. + use a negative [step] for a decreasing sequence. + @raise Invalid_argument if [step=0] *) + val bools : bool t (** Iterates on [true] and [false] @since 0.7 *) diff --git a/src/sequence.mllib b/src/sequence.mllib new file mode 100644 index 0000000..52e69a0 --- /dev/null +++ b/src/sequence.mllib @@ -0,0 +1,5 @@ +# OASIS_START +# DO NOT EDIT (digest: 8c0ffebbdb3e063d4b3e5cc00517b199) +Sequence +SequenceLabels +# OASIS_STOP diff --git a/src/sequenceLabels.ml b/src/sequenceLabels.ml new file mode 100644 index 0000000..96d2452 --- /dev/null +++ b/src/sequenceLabels.ml @@ -0,0 +1,1116 @@ + +(* This file is free software, part of sequence. See file "license" for more details. *) + +(** {1 Simple and Efficient Iterators} *) + +(** Sequence abstract iterator type *) +type 'a t = ('a -> unit) -> unit + +type 'a sequence = 'a t + +type (+'a, +'b) t2 = ('a -> 'b -> unit) -> unit +(** Sequence of pairs of values of type ['a] and ['b]. *) + +(*$inject + let pp_ilist = Q.Print.(list int) +*) + +(** Build a sequence from a iter function *) +let from_iter f = f + +let rec from_fun f k = match f () with + | None -> () + | Some x -> k x; from_fun f k + +let empty _ = () + +(*$R + let seq = empty in + OUnit.assert_bool "empty" (is_empty seq); + OUnit.assert_bool "empty" + (try iter (fun _ -> raise Exit) seq; true with Exit -> false); +*) + +let singleton x k = k x +let return x k = k x +let pure f k = k f + +let doubleton x y k = k x; k y + +let cons x l k = k x; l k +let snoc l x k = l k; k x + +let repeat x k = while true do k x done + +(*$R + let seq = repeat "hello" in + OUnit.assert_equal ["hello"; "hello"; "hello"] + (seq |> take 3 |> to_list); +*) + +let rec iterate f x k = + k x; + iterate f (f x) k + +let rec forever f k = + k (f ()); + forever f k + +let cycle s k = while true do s k; done + +let iter f seq = seq f + +let iteri f seq = + let r = ref 0 in + seq + (fun x -> + f !r x; + incr r) + +let fold f init seq = + let r = ref init in + seq (fun elt -> r := f !r elt); + !r + +(*$R + let n = (1 -- 10) + |> fold (+) 0 in + OUnit.assert_equal 55 n; +*) + +let foldi f init seq = + let i = ref 0 in + let r = ref init in + seq + (fun elt -> + r := f !r !i elt; + incr i); + !r + +(*$R + let l = ["hello"; "world"] + |> of_list + |> foldi (fun acc i x -> (i,x) :: acc) [] in + OUnit.assert_equal [1, "world"; 0, "hello"] l; +*) + +let map f seq k = seq (fun x -> k (f x)) + +let mapi f seq k = + let i = ref 0 in + seq (fun x -> k (f !i x); incr i) + +let map_by_2 f seq k = + let r = ref None in + let f y = match !r with + | None -> r := Some y + | Some x -> k (f x y) + in + seq f ; + match !r with + | None -> () | Some x -> k x + +let filter p seq k = seq (fun x -> if p x then k x) + +let append s1 s2 k = s1 k; s2 k + +let concat s k = s (fun s' -> s' k) + +(*$R + let s1 = (1 -- 5) in + let s2 = (6 -- 10) in + let l = [1;2;3;4;5;6;7;8;9;10] in + OUnit.assert_equal l (to_list (append s1 s2)); +*) + +(*$R + (1 -- 1000) + |> map (fun i -> i -- (i+1)) + |> concat + |> length + |> OUnit.assert_equal 2000 +*) + +let flatten s = concat s + +let flatMap f seq k = seq (fun x -> f x k) + +let flat_map = flatMap + +(*$R + (1 -- 1000) + |> flat_map (fun i -> i -- (i+1)) + |> length + |> OUnit.assert_equal 2000 +*) + +let flat_map_l f seq k = + seq (fun x -> List.iter k (f x)) + +let fmap f seq k = + seq (fun x -> match f x with + | None -> () + | Some y -> k y + ) + +let filter_map = fmap + +let intersperse elem seq k = + let first = ref true in + seq (fun x -> (if !first then first := false else k elem); k x) + +(*$R + (1 -- 100) + |> (fun seq -> intersperse 0 seq) + |> take 10 + |> to_list + |> OUnit.assert_equal [1;0;2;0;3;0;4;0;5;0] +*) + +(** Mutable unrolled list to serve as intermediate storage *) +module MList = struct + type 'a node = + | Nil + | Cons of 'a array * int ref * 'a node ref + + (* build and call callback on every element *) + let of_seq_with seq k = + let start = ref Nil in + let chunk_size = ref 8 in + (* fill the list. prev: tail-reference from previous node *) + let prev, cur = ref start, ref Nil in + seq + (fun x -> + k x; (* callback *) + match !cur with + | Nil -> + let n = !chunk_size in + if n < 4096 then chunk_size := 2 * !chunk_size; + cur := Cons (Array.make n x, ref 1, ref Nil) + | Cons (a,n,next) -> + assert (!n < Array.length a); + a.(!n) <- x; + incr n; + if !n = Array.length a then ( + !prev := !cur; + prev := next; + cur := Nil)); + !prev := !cur; + !start + + let of_seq seq = + of_seq_with seq (fun _ -> ()) + + let rec iter f l = match l with + | Nil -> () + | Cons (a, n, tl) -> + for i=0 to !n - 1 do f a.(i) done; + iter f !tl + + let iteri f l = + let rec iteri i f l = match l with + | Nil -> () + | Cons (a, n, tl) -> + for j=0 to !n - 1 do f (i+j) a.(j) done; + iteri (i+ !n) f !tl + in iteri 0 f l + + let rec iter_rev f l = match l with + | Nil -> () + | Cons (a, n, tl) -> + iter_rev f !tl; + for i = !n-1 downto 0 do f a.(i) done + + let length l = + let rec len acc l = match l with + | Nil -> acc + | Cons (_, n, tl) -> len (acc+ !n) !tl + in len 0 l + + (** Get element by index *) + let rec get l i = match l with + | Nil -> raise (Invalid_argument "MList.get") + | Cons (a, n, _) when i < !n -> a.(i) + | Cons (_, n, tl) -> get !tl (i- !n) + + let to_seq l k = iter k l + + let _to_next arg l = + let cur = ref l in + let i = ref 0 in (* offset in cons *) + let rec get_next _ = match !cur with + | Nil -> None + | Cons (_, n, tl) when !i = !n -> + cur := !tl; + i := 0; + get_next arg + | Cons (a, _, _) -> + let x = a.(!i) in + incr i; + Some x + in get_next + + let to_gen l = _to_next () l + + let to_stream l = + Stream.from (_to_next 42 l) (* 42=magic cookiiiiiie *) + + let to_klist l = + let rec make (l,i) () = match l with + | Nil -> `Nil + | Cons (_, n, tl) when i = !n -> make (!tl,0) () + | Cons (a, _, _) -> `Cons (a.(i), make (l,i+1)) + in make (l,0) +end + +let persistent seq = + let l = MList.of_seq seq in + MList.to_seq l + +(*$R + let printer = pp_ilist in + let stream = Stream.from (fun i -> if i < 5 then Some i else None) in + let seq = of_stream stream in + OUnit.assert_equal ~printer [0;1;2;3;4] (seq |> to_list); + OUnit.assert_equal ~printer [] (seq |> to_list); +*) + +(*$R + let printer = pp_ilist in + let stream = Stream.from (fun i -> if i < 5 then Some i else None) in + let seq = of_stream stream in + (* consume seq into a persistent version of itself *) + let seq' = persistent seq in + OUnit.assert_equal ~printer [] (seq |> to_list); + OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> to_list); + OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> to_list); + OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> to_stream |> of_stream |> to_list); +*) + +(*$R + let printer = pp_ilist in + let seq = (0 -- 10_000) in + let seq' = persistent seq in + OUnit.assert_equal 10_001 (length seq'); + OUnit.assert_equal 10_001 (length seq'); + OUnit.assert_equal ~printer [0;1;2;3] (seq' |> take 4 |> to_list); +*) + +type 'a lazy_state = + | LazySuspend + | LazyCached of 'a t + +let persistent_lazy (seq:'a t) = + let r = ref LazySuspend in + fun k -> + match !r with + | LazyCached seq' -> seq' k + | LazySuspend -> + (* here if this traversal is interruted, no caching occurs *) + let seq' = MList.of_seq_with seq k in + r := LazyCached (MList.to_seq seq') + +let sort ?(cmp=Pervasives.compare) seq = + (* use an intermediate list, then sort the list *) + let l = fold (fun l x -> x::l) [] seq in + let l = List.fast_sort cmp l in + fun k -> List.iter k l + +(*$R + (1 -- 100) + |> sort ~cmp:(fun i j -> j - i) + |> take 4 + |> to_list + |> OUnit.assert_equal [100;99;98;97] +*) + +let group_succ_by ?(eq=fun x y -> x = y) seq k = + let cur = ref [] in + seq (fun x -> + match !cur with + | [] -> cur := [x] + | (y::_) as l when eq x y -> + cur := x::l (* [x] belongs to the group *) + | (_::_) as l -> + k l; (* yield group, and start another one *) + cur := [x]); + (* last list *) + if !cur <> [] then k !cur + +let group = group_succ_by + +(*$R + [1;2;3;3;2;2;3;4] + |> of_list |> group_succ_by ?eq:None |> to_list + |> OUnit.assert_equal [[1];[2];[3;3];[2;2];[3];[4]] +*) + +let group_by (type k) ?(hash=Hashtbl.hash) ?(eq=(=)) seq = + let module Tbl = Hashtbl.Make(struct + type t = k + let equal = eq + let hash = hash + end) in + (* compute group table *) + let tbl = Tbl.create 32 in + seq + (fun x -> + let l = try Tbl.find tbl x with Not_found -> [] in + Tbl.replace tbl x (x::l) + ); + fun yield -> + Tbl.iter (fun _ l -> yield l) tbl + +(*$R + [1;2;3;3;2;2;3;4] + |> of_list |> group_by ?eq:None ?hash:None |> sort ?cmp:None |> to_list + |> OUnit.assert_equal [[1];[2;2;2];[3;3;3];[4]] +*) + +let uniq ?(eq=fun x y -> x = y) seq k = + let has_prev = ref false + and prev = ref (Obj.magic 0) in (* avoid option type, costly *) + seq + (fun x -> + if !has_prev && eq !prev x + then () (* duplicate *) + else ( + has_prev := true; + prev := x; + k x + )) + +(*$R + [1;2;2;3;4;4;4;3;3] + |> of_list |> uniq ?eq:None |> to_list + |> OUnit.assert_equal [1;2;3;4;3] +*) + +let sort_uniq (type elt) ?(cmp=Pervasives.compare) seq = + let module S = Set.Make(struct + type t = elt + let compare = cmp + end) in + let set = fold (fun acc x -> S.add x acc) S.empty seq in + fun k -> S.iter k set + +(*$R + [42;1;2;3;4;5;4;3;2;1] + |> of_list + |> sort_uniq ?cmp:None + |> to_list + |> OUnit.assert_equal [1;2;3;4;5;42] +*) + +let product outer inner k = + outer (fun x -> inner (fun y -> k (x,y))) + +(*$R + let stream = Stream.from (fun i -> if i < 3 then Some i else None) in + let a = of_stream stream in + let b = of_list ["a";"b";"c"] in + let s = product a b |> map (fun (x,y) -> y,x) + |> to_list |> List.sort compare in + OUnit.assert_equal ["a",0; "a", 1; "a", 2; + "b",0; "b", 1; "b", 2; + "c",0; "c", 1; "c", 2;] s +*) + +let product2 outer inner k = + outer (fun x -> inner (fun y -> k x y)) + +let join ~join_row s1 s2 k = + s1 (fun a -> + s2 (fun b -> + match join_row a b with + | None -> () + | Some c -> k c)) + +(*$R + let s1 = (1 -- 3) in + let s2 = of_list ["1"; "2"] in + let join_row i j = + if string_of_int i = j then Some (string_of_int i ^ " = " ^ j) else None + in + let s = join ~join_row s1 s2 in + OUnit.assert_equal ["1 = 1"; "2 = 2"] (to_list s); +*) + +let rec unfoldr f b k = match f b with + | None -> () + | Some (x, b') -> + k x; + unfoldr f b' k + +(*$R + let f x = if x < 5 then Some (string_of_int x,x+1) else None in + unfoldr f 0 + |> to_list + |> OUnit.assert_equal ["0"; "1"; "2"; "3"; "4"] +*) + +let scan f acc seq k = + k acc; + let acc = ref acc in + seq (fun elt -> let acc' = f !acc elt in k acc'; acc := acc') + +(*$R + (1 -- 5) + |> scan (+) 0 + |> to_list + |> OUnit.assert_equal ~printer:pp_ilist [0;1;3;6;10;15] +*) + +let max ?(lt=fun x y -> x < y) seq = + let ret = ref None in + seq + (fun x -> match !ret with + | None -> ret := Some x + | Some y -> if lt y x then ret := Some x); + !ret + +let min ?(lt=fun x y -> x < y) seq = + let ret = ref None in + seq + (fun x -> match !ret with + | None -> ret := Some x + | Some y -> if lt x y then ret := Some x); + !ret + +exception ExitHead + +let head seq = + let r = ref None in + try + seq (fun x -> r := Some x; raise ExitHead); None + with ExitHead -> !r + +let head_exn seq = + match head seq with + | None -> invalid_arg "Sequence.head_exn" + | Some x -> x + +exception ExitTake + +let take n seq k = + let count = ref 0 in + try + seq + (fun x -> + if !count = n then raise ExitTake; + incr count; + k x) + with ExitTake -> () + +(*$R + let l = to_list (take 0 (of_list [1])) in + OUnit.assert_equal ~printer:pp_ilist [] l; + let l = to_list (take 5 (of_list [1;2;3;4;5;6;7;8;9;10])) in + OUnit.assert_equal ~printer:pp_ilist [1;2;3;4;5] l; +*) + +exception ExitTakeWhile + +let take_while p seq k = + try + seq (fun x -> if p x then k x else raise ExitTakeWhile) + with ExitTakeWhile -> () + +exception ExitFoldWhile + +let fold_while f s seq = + let state = ref s in + let consume x = + let acc, cont = f (!state) x in + state := acc; + match cont with + | `Stop -> raise ExitFoldWhile + | `Continue -> () + in + try + seq consume; !state + with ExitFoldWhile -> !state +(*$R + let n = of_list [true;true;false;true] + |> fold_while (fun acc b -> if b then acc+1, `Continue else acc, `Stop) 0 in + OUnit.assert_equal 2 n; +*) + +let drop n seq k = + let count = ref 0 in + seq (fun x -> if !count >= n then k x else incr count) + +(*$R + (1 -- 5) |> drop 2 |> to_list |> OUnit.assert_equal [3;4;5] +*) + +let drop_while p seq k = + let drop = ref true in + seq + (fun x -> + if !drop + then if p x then () else (drop := false; k x) + else k x) + +let rev seq = + let l = MList.of_seq seq in + fun k -> MList.iter_rev k l + +(*$R + (1 -- 5) |> rev |> to_list |> OUnit.assert_equal [5;4;3;2;1] +*) + +exception ExitForall + +let for_all p seq = + try + seq (fun x -> if not (p x) then raise ExitForall); + true + with ExitForall -> false + +(*$R + OUnit.assert_bool "true" (for_all (fun x -> x < 10) (1--9)); + OUnit.assert_bool "false" (not (for_all (fun x -> x < 10) (2--11))); + OUnit.assert_bool "true" (for_all (fun _ -> false) empty); + OUnit.assert_bool "nested" + (for_all + (fun seq -> not (for_all (fun x -> x < 8) seq)) + (1 -- 10 >|= fun x -> x--20)); +*) + +exception ExitExists + +(** Exists there some element satisfying the predicate? *) +let exists p seq = + try + seq (fun x -> if p x then raise ExitExists); + false + with ExitExists -> true + +(*$R + (1 -- 100) + |> exists (fun x -> x = 59) + |> OUnit.assert_bool "exists"; + (1 -- 100) + |> exists (fun x -> x < 0) + |> (fun x -> not x) + |> OUnit.assert_bool "not exists"; +*) + +let mem ?(eq=(=)) x seq = exists (eq x) seq + +exception ExitFind + +let find f seq = + let r = ref None in + begin + try + seq + (fun x -> match f x with + | None -> () + | Some _ as res -> r := res; raise ExitFind); + with ExitFind -> () + end; + !r + +let length seq = + let r = ref 0 in + seq (fun _ -> incr r); + !r + +(*$R + (1 -- 1000) |> length |> OUnit.assert_equal 1000 +*) + +exception ExitIsEmpty + +let is_empty seq = + try seq (fun _ -> raise ExitIsEmpty); true + with ExitIsEmpty -> false + +(** {2 Transform a sequence} *) + +let empty2 _ = () + +let is_empty2 seq2 = + try ignore (seq2 (fun _ _ -> raise ExitIsEmpty)); true + with ExitIsEmpty -> false + +let length2 seq2 = + let r = ref 0 in + seq2 (fun _ _ -> incr r); + !r + +let zip seq2 k = seq2 (fun x y -> k (x,y)) + +let unzip seq k = seq (fun (x,y) -> k x y) + +let zip_i seq k = + let r = ref 0 in + seq (fun x -> let n = !r in incr r; k n x) + +let fold2 f acc seq2 = + let acc = ref acc in + seq2 (fun x y -> acc := f !acc x y); + !acc + +let iter2 f seq2 = seq2 f + +let map2 f seq2 k = seq2 (fun x y -> k (f x y)) + +let map2_2 f g seq2 k = + seq2 (fun x y -> k (f x y) (g x y)) + +(** {2 Basic data structures converters} *) + +let to_list seq = List.rev (fold (fun y x -> x::y) [] seq) + +let to_rev_list seq = fold (fun y x -> x :: y) [] seq + +let of_list l k = List.iter k l + +let on_list f l = + to_list (f (of_list l)) + +let to_opt = head + +let of_opt o k = match o with + | None -> () + | Some x -> k x + +let to_array seq = + let l = MList.of_seq seq in + let n = MList.length l in + if n = 0 + then [||] + else ( + let a = Array.make n (MList.get l 0) in + MList.iteri (fun i x -> a.(i) <- x) l; + a + ) + +let of_array a k = Array.iter k a + +let of_array_i a k = + for i = 0 to Array.length a - 1 do + k (i, Array.unsafe_get a i) + done + +let of_array2 a k = + for i = 0 to Array.length a - 1 do + k i (Array.unsafe_get a i) + done + +let array_slice a i j k = + assert (i >= 0 && j < Array.length a); + for idx = i to j do + k a.(idx); (* iterate on sub-array *) + done + +let of_stream s k = Stream.iter k s + +let to_stream seq = + let l = MList.of_seq seq in + MList.to_stream l + +let to_stack s seq = iter (fun x -> Stack.push x s) seq + +let of_stack s k = Stack.iter k s + +let to_queue q seq = seq (fun x -> Queue.push x q) + +let of_queue q k = Queue.iter k q + +let hashtbl_add h seq = + seq (fun (k,v) -> Hashtbl.add h k v) + +(*$R + let h = (1 -- 5) + |> zip_i + |> to_hashtbl2 in + (0 -- 4) + |> iter (fun i -> OUnit.assert_equal (i+1) (Hashtbl.find h i)); + OUnit.assert_equal [0;1;2;3;4] (hashtbl_keys h |> sort ?cmp:None |> to_list); +*) + +let hashtbl_replace h seq = + seq (fun (k,v) -> Hashtbl.replace h k v) + +let to_hashtbl seq = + let h = Hashtbl.create 3 in + hashtbl_replace h seq; + h + +let to_hashtbl2 seq2 = + let h = Hashtbl.create 3 in + seq2 (fun k v -> Hashtbl.replace h k v); + h + +let of_hashtbl h k = Hashtbl.iter (fun a b -> k (a, b)) h + +let of_hashtbl2 h k = Hashtbl.iter k h + +let hashtbl_keys h k = Hashtbl.iter (fun a _ -> k a) h + +let hashtbl_values h k = Hashtbl.iter (fun _ b -> k b) h + +let of_str s k = String.iter k s + +let to_str seq = + let b = Buffer.create 64 in + iter (fun c -> Buffer.add_char b c) seq; + Buffer.contents b + +let concat_str seq = + let b = Buffer.create 64 in + iter (Buffer.add_string b) seq; + Buffer.contents b + +exception OneShotSequence + +let of_in_channel ic = + let first = ref true in + fun k -> + if not !first + then raise OneShotSequence + else ( + first := false; + try + while true do + let c = input_char ic in k c + done + with End_of_file -> ()) + +let to_buffer seq buf = + seq (fun c -> Buffer.add_char buf c) + +(*$R + let b = Buffer.create 4 in + "hello world" + |> of_str |> rev |> map Char.uppercase + |> (fun seq -> to_buffer seq b); + OUnit.assert_equal "DLROW OLLEH" (Buffer.contents b); +*) + +(** Iterator on integers in [start...stop] by steps 1 *) +let int_range ~start ~stop k = + for i = start to stop do k i done + +(*$R + OUnit.assert_equal ~printer:pp_ilist [1;2;3;4] (to_list (1--4)); + OUnit.assert_equal ~printer:pp_ilist [10;9;8;7;6] (to_list (10 --^ 6)); + OUnit.assert_equal ~printer:pp_ilist [] (to_list (10--4)); + OUnit.assert_equal ~printer:pp_ilist [] (to_list (10 --^ 60)); +*) + +let int_range_dec ~start ~stop k = + for i = start downto stop do k i done + +let int_range_by ~step i j yield = + if step=0 then invalid_arg "int_range_by"; + for k = 0 to (j - i) / step do + yield (k * step + i) + done + +(*$= & ~printer:Q.Print.(list int) + [1;2;3;4] (int_range_by ~step:1 1 4 |> to_list) + [4;3;2;1] (int_range_by ~step:~-1 4 1 |> to_list) + [6;4;2] (int_range_by 6 1 ~step:~-2 |> to_list) + [] (int_range_by ~step:1 4 1 |> to_list) +*) + +(*$Q + Q.(pair small_int small_int) (fun (i,j) -> \ + let i = Pervasives.min i j and j = Pervasives.max i j in \ + (i--j |> to_list) = (int_range_by ~step:1 i j |> to_list)) + Q.(pair small_int small_int) (fun (i,j) -> \ + let i = Pervasives.min i j and j = Pervasives.max i j in \ + (i--j |> to_rev_list) = (int_range_by ~step:~-1 j i |> to_list)) +*) + +let bools k = k false; k true + +let of_set (type s) (type v) m set = + let module S = (val m : Set.S with type t = s and type elt = v) in + fun k -> S.iter k set + +let to_set (type s) (type v) m seq = + let module S = (val m : Set.S with type t = s and type elt = v) in + fold + (fun set x -> S.add x set) + S.empty seq + +type 'a gen = unit -> 'a option +type 'a klist = unit -> [`Nil | `Cons of 'a * 'a klist] + +let of_gen g = + (* consume the generator to build a MList *) + let rec iter1 k = match g () with + | None -> () + | Some x -> k x; iter1 k + in + let l = MList.of_seq iter1 in + MList.to_seq l + +let to_gen seq = + let l = MList.of_seq seq in + MList.to_gen l + +let rec of_klist l k = match l() with + | `Nil -> () + | `Cons (x,tl) -> k x; of_klist tl k + +let to_klist seq = + let l = MList.of_seq seq in + MList.to_klist l + +(** {2 Functorial conversions between sets and sequences} *) + +module Set = struct + module type S = sig + include Set.S + val of_seq : elt sequence -> t + val to_seq : t -> elt sequence + val to_list : t -> elt list + val of_list : elt list -> t + end + + (** Create an enriched Set module from the given one *) + module Adapt(X : Set.S) : S with type elt = X.elt and type t = X.t = struct + let to_seq set k = X.iter k set + + let of_seq seq = fold (fun set x -> X.add x set) X.empty seq + + let to_list set = to_list (to_seq set) + + include X + + let of_list l = List.fold_left (fun set x -> add x set) empty l + end + + (** Functor to build an extended Set module from an ordered type *) + module Make(X : Set.OrderedType) = struct + module MySet = Set.Make(X) + include Adapt(MySet) + end +end + +(** {2 Conversion between maps and sequences.} *) + +module Map = struct + module type S = sig + include Map.S + val to_seq : 'a t -> (key * 'a) sequence + val of_seq : (key * 'a) sequence -> 'a t + val keys : 'a t -> key sequence + val values : 'a t -> 'a sequence + val to_list : 'a t -> (key * 'a) list + val of_list : (key * 'a) list -> 'a t + end + + (** Adapt a pre-existing Map module to make it sequence-aware *) + module Adapt(M : Map.S) = struct + let to_seq m = from_iter (fun k -> M.iter (fun x y -> k (x,y)) m) + + let of_seq seq = fold (fun m (k,v) -> M.add k v m) M.empty seq + + let keys m = from_iter (fun k -> M.iter (fun x _ -> k x) m) + + let values m = from_iter (fun k -> M.iter (fun _ y -> k y) m) + + let of_list l = of_seq (of_list l) + + let to_list x = to_list (to_seq x) + + include M + end + + (** Create an enriched Map module, with sequence-aware functions *) + module Make(V : Map.OrderedType) : S with type key = V.t = struct + module M = Map.Make(V) + include Adapt(M) + end +end + +(** {2 Infinite sequences of random values} *) + +let random_int bound = forever (fun () -> Random.int bound) + +let random_bool = forever Random.bool + +let random_float bound = forever (fun () -> Random.float bound) + +let random_array a k = + assert (Array.length a > 0); + while true do + let i = Random.int (Array.length a) in + k a.(i); + done + +let random_list l = random_array (Array.of_list l) + +(* See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle *) +let shuffle_array a = + for k = Array.length a - 1 downto 0+1 do + let l = Random.int (k+1) in + let tmp = a.(l) in + a.(l) <- a.(k); + a.(k) <- tmp; + done + +let shuffle seq = + let a = to_array seq in + shuffle_array a ; + of_array a + +let shuffle_buffer n seq k = + let seq_front = take n seq in + let a = to_array seq_front in + let l = Array.length a in + if l < n then begin + shuffle_array a ; + of_array a k + end + else begin + let seq = drop n seq in + let f x = + let i = Random.int n in + let y = a.(i) in + a.(i) <- x ; + k y + in + seq f + end + +(** {2 Sampling} *) + +(** See https://en.wikipedia.org/wiki/Reservoir_sampling#Algorithm_R *) +let sample n seq = + match head seq with + | None -> [||] + | Some x -> + let a = Array.make n x in + let i = ref (-1) in + let f x = + incr i ; + if !i < n then + a.(!i) <- x + else + let j = Random.int n in + if j <= n then a.(!i) <- x + else () + in + seq f ; + if !i < n then Array.sub a 0 !i + else a + +(** {2 Infix functions} *) + +module Infix = struct + let (--) i j = int_range ~start:i ~stop:j + + let (--^) i j = int_range_dec ~start:i ~stop:j + + let (>>=) x f = flat_map f x + + let (>|=) x f = map f x + + let (<*>) funs args k = + funs (fun f -> args (fun x -> k (f x))) + + let (<+>) = append +end + +include Infix + +(** {2 Pretty printing of sequences} *) + +(** Pretty print a sequence of ['a], using the given pretty printer + to print each elements. An optional separator string can be provided. *) +let pp_seq ?(sep=", ") pp_elt formatter seq = + let first = ref true in + seq + (fun x -> + (if !first then first := false + else ( + Format.pp_print_string formatter sep; + Format.pp_print_cut formatter (); + )); + pp_elt formatter x) + +let pp_buf ?(sep=", ") pp_elt buf seq = + let first = ref true in + seq + (fun x -> + if !first then first := false else Buffer.add_string buf sep; + pp_elt buf x) + +let to_string ?sep pp_elt seq = + let buf = Buffer.create 25 in + pp_buf ?sep (fun buf x -> Buffer.add_string buf (pp_elt x)) buf seq; + Buffer.contents buf + +(** {2 Basic IO} *) + +module IO = struct + let lines_of ?(mode=0o644) ?(flags=[Open_rdonly]) filename = + fun k -> + let ic = open_in_gen flags mode filename in + try + while true do + let line = input_line ic in + k line + done + with + | End_of_file -> close_in ic + | e -> close_in_noerr ic; raise e + + let chunks_of ?(mode=0o644) ?(flags=[]) ?(size=1024) filename = + fun k -> + let ic = open_in_gen flags mode filename in + try + let buf = Bytes.create size in + let n = ref 0 in + let stop = ref false in + while not !stop do + n := 0; + (* try to read [size] chars. If [input] returns [0] it means + the end of file, so we stop, but first we yield the current chunk *) + while !n < size && not !stop do + let n' = input ic buf !n (size - !n) in + if n' = 0 then stop := true else n := !n + n'; + done; + if !n > 0 + then k (Bytes.sub_string buf 0 !n) + done; + close_in ic + with e -> + close_in_noerr ic; + raise e + + let write_bytes_to ?(mode=0o644) ?(flags=[Open_creat;Open_wronly]) filename seq = + let oc = open_out_gen flags mode filename in + try + seq (fun s -> output oc s 0 (Bytes.length s)); + close_out oc + with e -> + close_out oc; + raise e + + let write_to ?mode ?flags filename seq = + write_bytes_to ?mode ?flags filename (map Bytes.unsafe_of_string seq) + + let write_bytes_lines ?mode ?flags filename seq = + let ret = Bytes.unsafe_of_string "\n" in + write_bytes_to ?mode ?flags filename (snoc (intersperse ret seq) ret) + + let write_lines ?mode ?flags filename seq = + write_bytes_lines ?mode ?flags filename (map Bytes.unsafe_of_string seq) +end + +(* regression tests *) + +(*$R + let s = (take 10 (repeat 1)) in + OUnit.assert_bool "not empty" (not (is_empty s)); +*) diff --git a/sequenceLabels.mli b/src/sequenceLabels.mli similarity index 100% rename from sequenceLabels.mli rename to src/sequenceLabels.mli diff --git a/tests/run_tests.ml b/tests/run_tests.ml deleted file mode 100644 index 0fa3d58..0000000 --- a/tests/run_tests.ml +++ /dev/null @@ -1,9 +0,0 @@ - -open OUnit - -let suite = - "run_tests" >::: - [ Test_sequence.suite; ] - -let _ = - OUnit.run_test_tt_main suite diff --git a/tests/test_sequence.ml b/tests/test_sequence.ml deleted file mode 100644 index 0c0a23b..0000000 --- a/tests/test_sequence.ml +++ /dev/null @@ -1,263 +0,0 @@ - -open OUnit - -module S = Sequence - -let pp_ilist l = - let b = Buffer.create 15 in - let fmt = Format.formatter_of_buffer b in - Format.fprintf fmt "@[%a@]" (S.pp_seq Format.pp_print_int) (S.of_list l); - Buffer.contents b - -let test_empty () = - let seq = S.empty in - OUnit.assert_bool "empty" (S.is_empty seq); - OUnit.assert_bool "empty" - (try S.iter (fun _ -> raise Exit) seq; true with Exit -> false); - () - -let test_repeat () = - let seq = S.repeat "hello" in - OUnit.assert_equal ["hello"; "hello"; "hello"] - (seq |> S.take 3 |> S.to_list); - () - -let test_concat () = - let s1 = S.(1 -- 5) in - let s2 = S.(6 -- 10) in - let l = [1;2;3;4;5;6;7;8;9;10] in - OUnit.assert_equal l (S.to_list (S.append s1 s2)); - () - -let test_fold () = - let n = S.(1 -- 10) - |> S.fold (+) 0 in - OUnit.assert_equal 55 n; - () - -let test_foldi () = - let l = ["hello"; "world"] - |> S.of_list - |> S.foldi (fun acc i x -> (i,x) :: acc) [] in - OUnit.assert_equal [1, "world"; 0, "hello"] l; - () - -let test_fold_while () = - let n = S.of_list [true;true;false;true] - |> S.fold_while (fun acc b -> if b then acc+1, `Continue else acc, `Stop) 0 in - OUnit.assert_equal 2 n; - () - -let test_exists () = - S.(1 -- 100) - |> S.exists (fun x -> x = 59) - |> OUnit.assert_bool "exists"; - S.(1 -- 100) - |> S.exists (fun x -> x < 0) - |> (fun x -> not x) - |> OUnit.assert_bool "not exists"; - () - -let test_length () = - S.(1 -- 1000) |> S.length |> OUnit.assert_equal 1000 - -let test_concat2 () = - S.(1 -- 1000) - |> S.map (fun i -> S.(i -- (i+1))) - |> S.concat - |> S.length - |> OUnit.assert_equal 2000 - -let test_flat_map () = - S.(1 -- 1000) - |> S.flat_map (fun i -> S.(i -- (i+1))) - |> S.length - |> OUnit.assert_equal 2000 - -let test_intersperse () = - S.(1 -- 100) - |> (fun seq -> S.intersperse 0 seq) - |> S.take 10 - |> S.to_list - |> OUnit.assert_equal [1;0;2;0;3;0;4;0;5;0] - -let test_not_persistent () = - let printer = pp_ilist in - let stream = Stream.from (fun i -> if i < 5 then Some i else None) in - let seq = S.of_stream stream in - OUnit.assert_equal ~printer [0;1;2;3;4] (seq |> S.to_list); - OUnit.assert_equal ~printer [] (seq |> S.to_list); - () - -let test_persistent () = - let printer = pp_ilist in - let stream = Stream.from (fun i -> if i < 5 then Some i else None) in - let seq = S.of_stream stream in - (* consume seq into a persistent version of itself *) - let seq' = S.persistent seq in - OUnit.assert_equal ~printer [] (seq |> S.to_list); - OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> S.to_list); - OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> S.to_list); - OUnit.assert_equal ~printer [0;1;2;3;4] (seq' |> S.to_stream |> S.of_stream |> S.to_list); - () - -let test_big_persistent () = - let printer = pp_ilist in - let seq = S.(0 -- 10_000) in - let seq' = S.persistent seq in - OUnit.assert_equal 10_001 (S.length seq'); - OUnit.assert_equal 10_001 (S.length seq'); - OUnit.assert_equal ~printer [0;1;2;3] (seq' |> S.take 4 |> S.to_list); - () - -let test_sort () = - S.(1 -- 100) - |> S.sort ~cmp:(fun i j -> j - i) - |> S.take 4 - |> S.to_list - |> OUnit.assert_equal [100;99;98;97] - -let test_sort_uniq () = - [42;1;2;3;4;5;4;3;2;1] - |> S.of_list - |> S.sort_uniq ?cmp:None - |> S.to_list - |> OUnit.assert_equal [1;2;3;4;5;42] - -let test_group_succ () = - [1;2;3;3;2;2;3;4] - |> S.of_list |> S.group_succ_by ?eq:None |> S.to_list - |> OUnit.assert_equal [[1];[2];[3;3];[2;2];[3];[4]] - -let test_group_by () = - [1;2;3;3;2;2;3;4] - |> S.of_list |> S.group_by ?eq:None ?hash:None |> S.sort ?cmp:None |> S.to_list - |> OUnit.assert_equal [[1];[2;2;2];[3;3;3];[4]] - -let test_uniq () = - [1;2;2;3;4;4;4;3;3] - |> S.of_list |> S.uniq ?eq:None |> S.to_list - |> OUnit.assert_equal [1;2;3;4;3] - -let test_product () = - let stream = Stream.from (fun i -> if i < 3 then Some i else None) in - let a = S.of_stream stream in - let b = S.of_list ["a";"b";"c"] in - let s = S.product a b |> S.map (fun (x,y) -> y,x) - |> S.to_list |> List.sort compare in - OUnit.assert_equal ["a",0; "a", 1; "a", 2; - "b",0; "b", 1; "b", 2; - "c",0; "c", 1; "c", 2;] s - -let test_join () = - let s1 = S.(1 -- 3) in - let s2 = S.of_list ["1"; "2"] in - let join_row i j = - if string_of_int i = j then Some (string_of_int i ^ " = " ^ j) else None - in - let s = S.join ~join_row s1 s2 in - OUnit.assert_equal ["1 = 1"; "2 = 2"] (S.to_list s); - () - -let test_scan () = - S.(1 -- 5) - |> S.scan (+) 0 - |> S.to_list - |> OUnit.assert_equal ~printer:pp_ilist [0;1;3;6;10;15] - -let test_drop () = - S.(1 -- 5) |> S.drop 2 |> S.to_list |> OUnit.assert_equal [3;4;5] - -let test_rev () = - S.(1 -- 5) |> S.rev |> S.to_list |> OUnit.assert_equal [5;4;3;2;1] - -let test_unfoldr () = - let f x = if x < 5 then Some (string_of_int x,x+1) else None in - S.unfoldr f 0 - |> S.to_list - |> OUnit.assert_equal ["0"; "1"; "2"; "3"; "4"] - -let test_hashtbl () = - let h = S.(1 -- 5) - |> S.zip_i - |> S.to_hashtbl2 in - S.(0 -- 4) - |> S.iter (fun i -> OUnit.assert_equal (i+1) (Hashtbl.find h i)); - OUnit.assert_equal [0;1;2;3;4] (S.hashtbl_keys h |> S.sort ?cmp:None |> S.to_list); - () - -let test_buff () = - let b = Buffer.create 4 in - "hello world" - |> S.of_str |> S.rev |> S.map Char.uppercase - |> (fun seq -> S.to_buffer seq b); - OUnit.assert_equal "DLROW OLLEH" (Buffer.contents b); - () - -let test_int_range () = - OUnit.assert_equal ~printer:pp_ilist [1;2;3;4] S.(to_list (1--4)); - OUnit.assert_equal ~printer:pp_ilist [10;9;8;7;6] S.(to_list (10 --^ 6)); - OUnit.assert_equal ~printer:pp_ilist [] S.(to_list (10--4)); - OUnit.assert_equal ~printer:pp_ilist [] S.(to_list (10 --^ 60)); - () - -let test_take () = - let l = S.(to_list (take 0 (of_list [1]))) in - OUnit.assert_equal ~printer:pp_ilist [] l; - let l = S.(to_list (take 5 (of_list [1;2;3;4;5;6;7;8;9;10]))) in - OUnit.assert_equal ~printer:pp_ilist [1;2;3;4;5] l; - () - -let test_for_all () = - OUnit.assert_bool "true" S.(for_all (fun x -> x < 10) (1--9)); - OUnit.assert_bool "false" S.(not (for_all (fun x -> x < 10) (2--11))); - OUnit.assert_bool "true" S.(for_all (fun _ -> false) empty); - OUnit.assert_bool "nested" - S.( - for_all - (fun seq -> - not (for_all (fun x -> x < 8) seq) - ) (1 -- 10 >|= fun x -> x--20) - ); - () - -let test_regression1 () = - let s = S.(take 10 (repeat 1)) in - OUnit.assert_bool "not empty" (not (S.is_empty s)); - () - -let suite = - "test_sequence" >::: - [ "test_empty" >:: test_empty; - "test_repeat" >:: test_repeat; - "test_concat" >:: test_concat; - "test_concat2" >:: test_concat2; - "test_fold" >:: test_fold; - "test_foldi" >:: test_foldi; - "test_exists" >:: test_exists; - "test_length" >:: test_length; - "test_concat" >:: test_concat; - "test_flatMap" >:: test_flat_map; - "test_intersperse" >:: test_intersperse; - "test_not_persistent" >:: test_not_persistent; - "test_persistent" >:: test_persistent; - "test_big_persistent" >:: test_big_persistent; - "test_sort" >:: test_sort; - "test_sort_uniq" >:: test_sort_uniq; - "test_group_succ_by" >:: test_group_succ; - "test_group_by" >:: test_group_by; - "test_uniq" >:: test_uniq; - "test_product" >:: test_product; - "test_join" >:: test_join; - "test_scan" >:: test_scan; - "test_drop" >:: test_drop; - "test_rev" >:: test_rev; - "test_unfoldr" >:: test_unfoldr; - "test_hashtbl" >:: test_hashtbl; - "test_int_range" >:: test_int_range; - "test_take" >:: test_take; - "test_fold_while" >:: test_fold_while; - "test_buff" >:: test_buff; - "test_for_all" >:: test_for_all; - "test_regression1" >:: test_regression1; - ]