mte/unikernel/duniverse/base/test/map_full_interface/functor.ml

1580 lines
47 KiB
OCaml
Raw Normal View History

2025-11-11 02:07:51 +01:00
(** Comprehensive testing of [Base.Map].
This file tests all exports of [Base.Map]. Every time a new export is added, we have
to add a new definition somewhere here. Every time we add a definition, we should add
a test unless the definition is untestable (e.g., a module type) or trivial (e.g., a
module containing only ppx-derived definitions). We should document categories of
untested definitions, mark them as untested, and keep them separate from definitions
that need tests. *)
open! Base
open Base_quickcheck
open Expect_test_helpers_base
include Functor_intf.Definitions
open struct
(** quickcheck configuration *)
let quickcheck_config =
let test_count =
(* In js_of_ocaml, quickcheck is slow due to 64-bit arithmetic, and some map
operations are especially slow due to use of exceptions and exception handlers.
So on "other" backends, we turn the test count down. *)
match Sys.backend_type with
| Native | Bytecode -> 10_000
| Other _ -> 1_000
in
{ Base_quickcheck.Test.default_config with test_count }
;;
let quickcheck_m here m ~f = quickcheck_m here m ~f ~config:quickcheck_config
end
module Instance (Cmp : sig
type comparator_witness
val comparator : (int, comparator_witness) Comparator.t
end) =
struct
module Key = struct
type t = int [@@deriving quickcheck, sexp_of]
type comparator_witness = Cmp.comparator_witness
let comparator = Cmp.comparator
let compare = comparator.compare
let equal = [%compare.equal: t]
let quickcheck_generator = Base_quickcheck.Generator.small_strictly_positive_int
include Comparable.Infix (struct
type nonrec t = t
let compare = compare
end)
end
type 'a t = 'a Map.M(Key).t [@@deriving equal, sexp_of]
let key x = x
let int x = x
let tree x = x
let quickcheck_generator gen =
Base_quickcheck.Generator.map_t_m
(module Key)
Base_quickcheck.Generator.small_strictly_positive_int
gen
;;
let quickcheck_observer obs =
Base_quickcheck.Observer.map_t Base_quickcheck.Observer.int obs
;;
let quickcheck_shrinker shr =
Base_quickcheck.Shrinker.map_t Base_quickcheck.Shrinker.int shr
;;
end
(** A functor like [Instance], but for tree types. *)
module Instance_tree (Cmp : sig
type comparator_witness
val comparator : (int, comparator_witness) Comparator.t
end) =
struct
module M = Instance (Cmp)
include M
type 'a t = (int, 'a, Cmp.comparator_witness) Map.Using_comparator.Tree.t
let of_tree tree = Map.Using_comparator.of_tree ~comparator:Cmp.comparator tree
let to_tree t = Map.Using_comparator.to_tree t
let quickcheck_generator gen =
Base_quickcheck.Generator.map (M.quickcheck_generator gen) ~f:to_tree
;;
let quickcheck_observer obs =
Base_quickcheck.Observer.unmap (M.quickcheck_observer obs) ~f:of_tree
;;
let quickcheck_shrinker shr =
Base_quickcheck.Shrinker.map (M.quickcheck_shrinker shr) ~f:to_tree ~f_inverse:of_tree
;;
let equal equal_a = Map.Using_comparator.Tree.equal ~comparator:Cmp.comparator equal_a
let sexp_of_t sexp_of_a t = M.sexp_of_t sexp_of_a (of_tree t)
end
(** Functor for [List.t] *)
module Lst (T : sig
type t [@@deriving equal, sexp_of]
end) =
struct
type t = T.t list [@@deriving equal, sexp_of]
end
(** Functor for [Or_error], ignoring error contents when comparing. *)
module Ok (T : sig
type t [@@deriving equal, sexp_of]
end) =
struct
type t = (T.t, (Error.t[@equal.ignore])) Result.t [@@deriving equal, sexp_of]
end
(** Functor for [Option.t] *)
module Opt (T : sig
type t [@@deriving equal, sexp_of]
end) =
struct
type t = T.t option [@@deriving equal, sexp_of]
end
(** Functor for pairs of a single type. Random generation frequently generates pairs of
identical values. *)
module Pair (T : sig
type t [@@deriving equal, quickcheck, sexp_of]
end) =
struct
type t = T.t * T.t [@@deriving equal, quickcheck, sexp_of]
let quickcheck_generator =
let open Base_quickcheck.Generator.Let_syntax in
match%bind Base_quickcheck.Generator.bool with
| true -> [%generator: t]
| false ->
let%map x = [%generator: T.t] in
x, x
;;
end
(* Used in [test__*.ml]. *)
module Test_creators_and_accessors
(Types : Types)
(Impl : S with module Types := Types)
(Instance : Instance with module Types := Types) : S with module Types := Types = struct
open Instance
open Impl
open struct
(** Test helpers, not to be exported. *)
module Alist = struct
type t = (Key.t * int) list [@@deriving compare, equal, quickcheck, sexp_of]
end
module Alist_merge = struct
type t = (Key.t * (int, int) Map.Merge_element.t) list [@@deriving equal, sexp_of]
end
module Alist_multi = struct
type t = (Key.t * int list) list [@@deriving equal, quickcheck, sexp_of]
end
module Diff = struct
type t = (Key.t, int) Map.Symmetric_diff_element.t list [@@deriving equal, sexp_of]
end
module Inst = struct
type t = int Instance.t [@@deriving equal, quickcheck, sexp_of]
end
module Inst_and_key = struct
type t = Inst.t * Key.t [@@deriving quickcheck, sexp_of]
end
module Inst_and_key_and_data = struct
type t = Inst.t * Key.t * int [@@deriving quickcheck, sexp_of]
end
module Inst_inst = struct
type t = Inst.t Instance.t [@@deriving equal, quickcheck, sexp_of]
end
module Inst_pair = struct
type t = (int * int) Instance.t [@@deriving equal, quickcheck, sexp_of]
end
module Inst_multi = struct
type t = int list Instance.t [@@deriving equal, quickcheck, sexp_of]
end
module Key_and_data = struct
type t = Key.t * int [@@deriving equal, sexp_of]
end
module Key_and_data_inst = struct
type t = (Key.t * int) Instance.t [@@deriving equal, sexp_of]
end
module Key_and_data_inst_multi = struct
type t = (Key.t * int) list Instance.t [@@deriving equal, sexp_of]
end
module Maybe_bound = struct
include Maybe_bound
type 'a t = 'a Maybe_bound.t =
| Incl of 'a
| Excl of 'a
| Unbounded
[@@deriving quickcheck, sexp_of]
end
let ok_or_duplicate_key = function
| `Ok x -> Ok x
| `Duplicate_key key -> Or_error.error_s [%sexp (key : Key.t)]
;;
end
(** creators *)
let empty = empty
let () = require_equal [%here] (module Sexp) [%sexp (create empty : int t)] [%sexp []]
let singleton = singleton
let () =
require_equal
[%here]
(module Sexp)
[%sexp (create singleton (key 1) 2 : int t)]
[%sexp [ [ 1; 2 ] ]]
;;
let of_alist = of_alist
let of_alist_or_error = of_alist_or_error
let of_alist_exn = of_alist_exn
let () =
quickcheck_m
[%here]
(module Alist)
~f:(fun alist ->
let t_or_error = create of_alist_or_error alist in
let t_exn = Or_error.try_with (fun () -> create of_alist_exn alist) in
let t_or_duplicate =
match create of_alist alist with
| `Ok t -> Ok t
| `Duplicate_key key -> Or_error.error_s [%sexp (key : Key.t)]
in
require_equal
[%here]
(module Ok (Alist))
(Or_error.map t_or_error ~f:to_alist)
(let compare a b = Comparable.lift Key.compare ~f:fst a b in
if List.contains_dup alist ~compare
then Or_error.error_string "duplicate"
else Ok (List.sort alist ~compare));
require_equal [%here] (module Ok (Inst)) t_exn t_or_error;
require_equal [%here] (module Ok (Inst)) t_or_duplicate t_or_error)
;;
let of_alist_multi = of_alist_multi
let of_alist_fold = of_alist_fold
let of_alist_reduce = of_alist_reduce
let () =
quickcheck_m
[%here]
(module Alist)
~f:(fun alist ->
let t_multi = create of_alist_multi alist in
let t_fold =
create of_alist_fold alist ~init:[] ~f:(fun xs x -> x :: xs) |> map ~f:List.rev
in
let t_reduce =
create of_alist_reduce (List.Assoc.map alist ~f:List.return) ~f:(fun x y ->
x @ y)
in
require_equal
[%here]
(module Alist_multi)
(to_alist t_multi)
(List.Assoc.sort_and_group alist ~compare:Key.compare);
require_equal [%here] (module Inst_multi) t_fold t_multi;
require_equal [%here] (module Inst_multi) t_reduce t_multi)
;;
let of_sequence = of_sequence
let of_sequence_or_error = of_sequence_or_error
let of_sequence_exn = of_sequence_exn
let () =
quickcheck_m
[%here]
(module Alist)
~f:(fun alist ->
let seq = Sequence.of_list alist in
let t_or_error = create of_sequence_or_error seq in
let t_exn = Or_error.try_with (fun () -> create of_sequence_exn seq) in
let t_or_duplicate =
match create of_sequence seq with
| `Ok t -> Ok t
| `Duplicate_key key -> Or_error.error_s [%sexp (key : Key.t)]
in
let expect = create of_alist_or_error alist in
require_equal [%here] (module Ok (Inst)) t_or_error expect;
require_equal [%here] (module Ok (Inst)) t_exn expect;
require_equal [%here] (module Ok (Inst)) t_or_duplicate expect)
;;
let of_sequence_multi = of_sequence_multi
let of_sequence_fold = of_sequence_fold
let of_sequence_reduce = of_sequence_reduce
let () =
quickcheck_m
[%here]
(module Alist)
~f:(fun alist ->
let seq = Sequence.of_list alist in
let t_multi = create of_sequence_multi seq in
let t_fold =
create of_sequence_fold seq ~init:[] ~f:(fun xs x -> x :: xs) |> map ~f:List.rev
in
let t_reduce =
create
of_sequence_reduce
(alist |> List.Assoc.map ~f:List.return |> Sequence.of_list)
~f:(fun x y -> x @ y)
in
let expect = create of_alist_multi alist in
require_equal [%here] (module Inst_multi) t_multi expect;
require_equal [%here] (module Inst_multi) t_fold expect;
require_equal [%here] (module Inst_multi) t_reduce expect)
;;
let of_list_with_key = of_list_with_key
let of_list_with_key_or_error = of_list_with_key_or_error
let of_list_with_key_exn = of_list_with_key_exn
let of_list_with_key_multi = of_list_with_key_multi
let of_list_with_key_fold = of_list_with_key_fold
let of_list_with_key_reduce = of_list_with_key_reduce
let () =
quickcheck_m
[%here]
(module Alist)
~f:(fun list ->
let alist = List.map list ~f:(fun (key, data) -> key, (key, data)) in
require_equal
[%here]
(module Ok (Key_and_data_inst))
(create of_list_with_key list ~get_key:fst |> ok_or_duplicate_key)
(create of_alist alist |> ok_or_duplicate_key);
require_equal
[%here]
(module Ok (Key_and_data_inst))
(create of_list_with_key_or_error list ~get_key:fst)
(create of_alist_or_error alist);
require_equal
[%here]
(module Ok (Key_and_data_inst))
(Or_error.try_with (fun () -> create of_list_with_key_exn list ~get_key:fst))
(Or_error.try_with (fun () -> create of_alist_exn alist));
require_equal
[%here]
(module Key_and_data_inst_multi)
(create of_list_with_key_multi list ~get_key:fst)
(create of_alist_multi alist);
require_equal
[%here]
(module Key_and_data_inst_multi)
(create of_list_with_key_fold list ~get_key:fst ~init:[] ~f:(fun acc x ->
x :: acc)
|> map ~f:List.rev)
(create of_alist_multi alist);
require_equal
[%here]
(module Key_and_data_inst_multi)
(create
of_list_with_key_reduce
(List.map list ~f:List.return)
~get_key:(fun x -> x |> List.hd_exn |> fst)
~f:(fun x y -> x @ y))
(create of_alist_multi alist))
;;
let of_increasing_sequence = of_increasing_sequence
let () =
quickcheck_m
[%here]
(module Alist)
~f:(fun alist ->
let seq = Sequence.of_list alist in
let actual = create of_increasing_sequence seq in
let expect =
if List.is_sorted alist ~compare:(fun a b ->
Comparable.lift Key.compare ~f:fst a b)
then create of_alist_or_error alist
else Or_error.error_string "decreasing keys"
in
require_equal [%here] (module Ok (Inst)) actual expect)
;;
let of_sorted_array = of_sorted_array
let () =
quickcheck_m
[%here]
(module Alist)
~f:(fun alist ->
let actual = create of_sorted_array (Array.of_list alist) in
let expect =
let compare a b = Comparable.lift Key.compare ~f:fst a b in
if List.is_sorted_strictly ~compare alist
|| List.is_sorted_strictly ~compare (List.rev alist)
then create of_alist_or_error alist
else Or_error.error_string "unsorted"
in
require_equal [%here] (module Ok (Inst)) actual expect)
;;
let of_sorted_array_unchecked = of_sorted_array_unchecked
let () =
quickcheck_m
[%here]
(module Alist)
~f:(fun alist ->
let alist =
List.dedup_and_sort alist ~compare:(fun a b ->
Comparable.lift Key.compare ~f:fst a b)
in
let actual_fwd = create of_sorted_array_unchecked (Array.of_list alist) in
let actual_rev = create of_sorted_array_unchecked (Array.of_list_rev alist) in
let expect = create of_alist_exn alist in
require_equal [%here] (module Inst) actual_fwd expect;
require_equal [%here] (module Inst) actual_rev expect)
;;
let of_increasing_iterator_unchecked = of_increasing_iterator_unchecked
let () =
quickcheck_m
[%here]
(module Alist)
~f:(fun alist ->
let alist =
List.dedup_and_sort alist ~compare:(fun a b ->
Comparable.lift Key.compare ~f:fst a b)
in
let actual =
let array = Array.of_list alist in
create
of_increasing_iterator_unchecked
~len:(Array.length array)
~f:(Array.get array)
in
let expect = create of_alist_exn alist in
require_equal [%here] (module Inst) actual expect)
;;
let of_iteri = of_iteri
let of_iteri_exn = of_iteri_exn
let () =
quickcheck_m
[%here]
(module Alist)
~f:(fun alist ->
let iteri ~f = List.iter alist ~f:(fun (key, data) -> f ~key ~data) [@nontail] in
let actual_or_duplicate =
match create of_iteri ~iteri with
| `Ok t -> Ok t
| `Duplicate_key key -> Or_error.error_s [%sexp (key : Key.t)]
in
let actual_exn = Or_error.try_with (fun () -> create of_iteri_exn ~iteri) in
let expect = create of_alist_or_error alist in
require_equal [%here] (module Ok (Inst)) actual_or_duplicate expect;
require_equal [%here] (module Ok (Inst)) actual_exn expect)
;;
let map_keys = map_keys
let map_keys_exn = map_keys_exn
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, k) ->
let f key = Comparable.min Key.compare k key in
let actual_or_duplicate =
match create map_keys t ~f with
| `Ok t -> Ok t
| `Duplicate_key key -> Or_error.error_s [%sexp (key : Key.t)]
in
let actual_exn = Or_error.try_with (fun () -> create map_keys_exn t ~f) in
let expect =
to_alist t
|> List.map ~f:(fun (key, data) -> f key, data)
|> create of_alist_or_error
in
require_equal [%here] (module Ok (Inst)) actual_or_duplicate expect;
require_equal [%here] (module Ok (Inst)) actual_exn expect)
;;
let transpose_keys = transpose_keys
let () =
quickcheck_m
[%here]
(module Inst_inst)
~f:(fun t ->
let transpose_keys = create (access transpose_keys) in
let transposed = transpose_keys t in
require [%here] (access invariants transposed);
let round_trip = transpose_keys transposed in
require_equal
[%here]
(module Inst_inst)
(filter t ~f:(Fn.non is_empty))
round_trip)
;;
(** accessors *)
let invariants = invariants
let () =
quickcheck_m [%here] (module Inst) ~f:(fun t -> require [%here] (access invariants t))
;;
let is_empty = is_empty
let length = length
let () =
quickcheck_m
[%here]
(module Inst)
~f:(fun t ->
let len = length t in
require_equal [%here] (module Bool) (is_empty t) (len = 0);
require_equal [%here] (module Int) len (List.length (to_alist t)))
;;
let mem = mem
let find = find
let find_exn = find_exn
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, key) ->
let expect = List.Assoc.find (to_alist t) key ~equal:Key.equal in
require_equal [%here] (module Bool) (access mem t key) (Option.is_some expect);
require_equal [%here] (module Opt (Int)) (access find t key) expect;
require_equal
[%here]
(module Opt (Int))
(Option.try_with (fun () -> access find_exn t key))
expect)
;;
let set = set
let () =
quickcheck_m
[%here]
(module Inst_and_key_and_data)
~f:(fun (t, key, data) ->
require_equal
[%here]
(module Alist)
(to_alist (access set t ~key ~data))
(List.sort
~compare:(fun a b -> Comparable.lift Key.compare ~f:fst a b)
((key, data) :: List.Assoc.remove (to_alist t) key ~equal:Key.equal)))
;;
let add = add
let add_exn = add_exn
let () =
quickcheck_m
[%here]
(module Inst_and_key_and_data)
~f:(fun (t, key, data) ->
let t_add =
match access add t ~key ~data with
| `Ok t -> Ok t
| `Duplicate -> Or_error.error_string "duplicate"
in
let t_add_exn = Or_error.try_with (fun () -> access add_exn t ~key ~data) in
let expect =
if access mem t key
then Or_error.error_string "duplicate"
else Ok (access set t ~key ~data)
in
require_equal [%here] (module Ok (Inst)) t_add expect;
require_equal [%here] (module Ok (Inst)) t_add_exn expect)
;;
let remove = remove
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, key) ->
require_equal
[%here]
(module Alist)
(to_alist (access remove t key))
(List.Assoc.remove (to_alist t) key ~equal:Key.equal))
;;
let change = change
let () =
quickcheck_m
[%here]
(module struct
type t = Inst.t * Key.t * int option [@@deriving quickcheck, sexp_of]
end)
~f:(fun (t, key, maybe_data) ->
let actual =
access change t key ~f:(fun previous ->
require_equal [%here] (module Opt (Int)) previous (access find t key);
maybe_data)
in
let expect =
match maybe_data with
| None -> access remove t key
| Some data -> access set t ~key ~data
in
require_equal [%here] (module Inst) actual expect)
;;
let update = update
let () =
quickcheck_m
[%here]
(module Inst_and_key_and_data)
~f:(fun (t, key, data) ->
let actual =
access update t key ~f:(fun previous ->
require_equal [%here] (module Opt (Int)) previous (access find t key);
data)
in
let expect = access set t ~key ~data in
require_equal [%here] (module Inst) actual expect)
;;
let find_multi = find_multi
let add_multi = add_multi
let remove_multi = remove_multi
let () =
quickcheck_m
[%here]
(module struct
type t = Inst_multi.t * Key.t * int [@@deriving quickcheck, sexp_of]
end)
~f:(fun (t, key, data) ->
require_equal
[%here]
(module Lst (Int))
(access find_multi t key)
(access find t key |> Option.value ~default:[]);
require_equal
[%here]
(module Inst_multi)
(access add_multi t ~key ~data)
(access update t key ~f:(fun option -> data :: Option.value option ~default:[]));
require_equal
[%here]
(module Inst_multi)
(access remove_multi t key)
(access change t key ~f:(function
| None | Some ([] | [ _ ]) -> None
| Some (_ :: (_ :: _ as rest)) -> Some rest)))
;;
let iter_keys = iter_keys
let iter = iter
let iteri = iteri
let () =
quickcheck_m
[%here]
(module Inst)
~f:(fun t ->
let actuali =
let q = Queue.create () in
iteri t ~f:(fun ~key ~data -> Queue.enqueue q (key, data));
Queue.to_list q
in
let actual_keys =
let q = Queue.create () in
iter_keys t ~f:(Queue.enqueue q);
Queue.to_list q
in
let actual =
let q = Queue.create () in
iter t ~f:(Queue.enqueue q);
Queue.to_list q
in
require_equal [%here] (module Alist) actuali (to_alist t);
require_equal [%here] (module Lst (Key)) actual_keys (keys t);
require_equal [%here] (module Lst (Int)) actual (data t))
;;
let map = map
let mapi = mapi
let () =
quickcheck_m
[%here]
(module Inst)
~f:(fun t ->
require_equal
[%here]
(module Inst)
(map t ~f:Int.succ)
(t |> to_alist |> List.Assoc.map ~f:Int.succ |> create of_alist_exn);
require_equal
[%here]
(module struct
type t = (Key.t * int) Instance.t [@@deriving equal, sexp_of]
end)
(mapi t ~f:(fun ~key ~data -> key, data))
(t |> to_alist |> List.map ~f:(fun (k, v) -> k, (k, v)) |> create of_alist_exn))
;;
let filter_keys = filter_keys
let filter = filter
let filteri = filteri
module Physical_equality (T : sig
type t [@@deriving sexp_of]
end) =
struct
type t = T.t [@@deriving sexp_of]
let equal a b = phys_equal a b
end
let () =
quickcheck_m
[%here]
(module Inst_and_key_and_data)
~f:(fun (t, k, d) ->
require_equal
[%here]
(module Physical_equality (Inst))
(filter ~f:(fun _ -> true) t)
t;
require_equal
[%here]
(module Alist)
(to_alist (filter_keys t ~f:(fun key -> Key.( <= ) key k)))
(List.filter (to_alist t) ~f:(fun (key, _) -> Key.( <= ) key k));
require_equal
[%here]
(module Alist)
(to_alist (filter t ~f:(fun data -> data <= d)))
(List.filter (to_alist t) ~f:(fun (_, data) -> data <= d));
require_equal
[%here]
(module Alist)
(to_alist (filteri t ~f:(fun ~key ~data -> Key.( <= ) key k && data <= d)))
(List.filter (to_alist t) ~f:(fun (key, data) -> Key.( <= ) key k && data <= d)))
;;
let filter_map = filter_map
let filter_mapi = filter_mapi
let () =
quickcheck_m
[%here]
(module Inst_and_key_and_data)
~f:(fun (t, k, d) ->
require_equal
[%here]
(module Alist)
(to_alist (filter_map t ~f:(fun data -> Option.some_if (data >= d) (data - d))))
(List.filter_map (to_alist t) ~f:(fun (key, data) ->
Option.some_if (data >= d) (key, data - d)));
require_equal
[%here]
(module Alist)
(to_alist
(filter_mapi t ~f:(fun ~key ~data ->
Option.some_if (Key.( <= ) key k && data >= d) (data - d))))
(List.filter_map (to_alist t) ~f:(fun (key, data) ->
Option.some_if (Key.( <= ) key k && data >= d) (key, data - d))))
;;
let partition_mapi = partition_mapi
let partition_map = partition_map
let partitioni_tf = partitioni_tf
let partition_tf = partition_tf
let () =
quickcheck_m
[%here]
(module Inst_and_key_and_data)
~f:(fun (t, k, d) ->
require_equal
[%here]
(module Physical_equality (Inst))
(fst (partition_tf ~f:(fun _ -> true) t))
t;
require_equal
[%here]
(module Pair (Alist))
(let a, b = partition_tf t ~f:(fun data -> data <= d) in
to_alist a, to_alist b)
(List.partition_tf (to_alist t) ~f:(fun (_, data) -> data <= d));
require_equal
[%here]
(module Pair (Alist))
(let a, b =
partitioni_tf t ~f:(fun ~key ~data -> Key.( <= ) key k && data <= d)
in
to_alist a, to_alist b)
(List.partition_tf (to_alist t) ~f:(fun (key, data) ->
Key.( <= ) key k && data <= d));
require_equal
[%here]
(module Pair (Alist))
(let a, b =
partition_map t ~f:(fun data ->
if data >= d then First (data - d) else Second d)
in
to_alist a, to_alist b)
(List.partition_map (to_alist t) ~f:(fun (key, data) ->
if data >= d then First (key, data - d) else Second (key, d)));
require_equal
[%here]
(module Pair (Alist))
(let a, b =
partition_mapi t ~f:(fun ~key ~data ->
if Key.( <= ) key k && data >= d then First (data - d) else Second d)
in
to_alist a, to_alist b)
(List.partition_map (to_alist t) ~f:(fun (key, data) ->
if Key.( <= ) key k && data >= d
then First (key, data - d)
else Second (key, d))))
;;
let fold = fold
let fold_right = fold_right
let () =
quickcheck_m
[%here]
(module Inst)
~f:(fun t ->
require_equal
[%here]
(module Alist)
(fold t ~init:[] ~f:(fun ~key ~data list -> (key, data) :: list))
(List.rev (to_alist t));
require_equal
[%here]
(module Alist)
(fold_right t ~init:[] ~f:(fun ~key ~data list -> (key, data) :: list))
(to_alist t))
;;
let fold_until = fold_until
let iteri_until = iteri_until
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, threshold) ->
require_equal
[%here]
(module struct
type t = int list * Base.Map.Finished_or_unfinished.t
[@@deriving equal, sexp_of]
end)
(let q = Queue.create () in
let status =
iteri_until t ~f:(fun ~key ~data ->
if Key.( >= ) key threshold
then Stop
else (
Queue.enqueue q data;
Continue))
in
Queue.to_list q, status)
(let list =
to_alist t
|> List.take_while ~f:(fun (key, _) -> Key.( < ) key threshold)
|> List.map ~f:snd
in
list, if List.length list = length t then Finished else Unfinished))
;;
let combine_errors = combine_errors
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, threshold) ->
let t =
mapi t ~f:(fun ~key ~data ->
if Key.( <= ) key threshold then Ok data else Or_error.error_string "too big")
in
require_equal
[%here]
(module Ok (Inst))
(access combine_errors t)
(to_alist t
|> List.map ~f:(fun (key, result) ->
Or_error.map result ~f:(fun data -> key, data))
|> Or_error.combine_errors
|> Or_error.map ~f:(create of_alist_exn)))
;;
let unzip = unzip
let () =
quickcheck_m
[%here]
(module Inst_pair)
~f:(fun t ->
require_equal
[%here]
(module Pair (Alist))
(let a, b = unzip t in
to_alist a, to_alist b)
(to_alist t
|> List.map ~f:(fun (key, (a, b)) -> (key, a), (key, b))
|> List.unzip))
;;
let equal = equal
let compare_direct = compare_direct
let () =
quickcheck_m
[%here]
(module Pair (Inst))
~f:(fun (a, b) ->
require_equal
[%here]
(module Ordering)
(Ordering.of_int (access compare_direct Int.compare a b))
(Ordering.of_int (Alist.compare (to_alist a) (to_alist b)));
require_equal
[%here]
(module Bool)
(access compare_direct Int.compare a b = 0)
(access equal Int.equal a b))
;;
let keys = keys
let data = data
let to_alist = to_alist
let to_sequence = to_sequence
let () =
quickcheck_m
[%here]
(module Inst)
~f:(fun t ->
let alist = to_alist t in
require_equal [%here] (module Inst) (create of_alist_exn alist) t;
require_equal [%here] (module Lst (Key)) (keys t) (List.map alist ~f:fst);
require_equal [%here] (module Lst (Int)) (data t) (List.map alist ~f:snd);
require_equal
[%here]
(module Alist)
(Sequence.to_list ((access to_sequence) t))
alist)
;;
let () =
quickcheck_m
[%here]
(module struct
type t = Inst.t * [ `Decreasing | `Increasing ] [@@deriving quickcheck, sexp_of]
end)
~f:(fun (t, key_order) ->
let alist = to_alist t ~key_order in
require_equal
[%here]
(module Lst (Key_and_data))
alist
(match key_order with
| `Increasing -> to_alist t
| `Decreasing -> List.rev (to_alist t));
require_equal
[%here]
(module Lst (Key_and_data))
alist
(Sequence.to_list
((access to_sequence)
t
~order:
(match key_order with
| `Decreasing -> `Decreasing_key
| `Increasing -> `Increasing_key))))
;;
let () =
quickcheck_m
[%here]
(module struct
type t = Inst.t * [ `Decreasing_key | `Increasing_key ] * Key.t * Key.t
[@@deriving quickcheck, sexp_of]
end)
~f:(fun (t, order, keys_greater_or_equal_to, keys_less_or_equal_to) ->
let alist =
Sequence.to_list
((access to_sequence)
t
~order
~keys_greater_or_equal_to
~keys_less_or_equal_to)
in
require_equal
[%here]
(module Lst (Key_and_data))
alist
(List.filter
(match order with
| `Decreasing_key -> List.rev (to_alist t)
| `Increasing_key -> to_alist t)
~f:(fun (key, _) ->
Key.( <= ) keys_greater_or_equal_to key
&& Key.( <= ) key keys_less_or_equal_to)))
;;
let merge = merge
let iter2 = iter2
let fold2 = fold2
let () =
quickcheck_m
[%here]
(module struct
module Inst2 = Pair (Inst)
type t = Inst2.t * Key.t [@@deriving quickcheck, sexp_of]
end)
~f:(fun ((a, b), k) ->
let merge_alist =
access merge a b ~f:(fun ~key elt ->
Option.some_if (Key.( > ) key k) (key, elt))
|> data
in
let iter2_alist =
let q = Queue.create () in
access iter2 a b ~f:(fun ~key ~data:elt ->
if Key.( > ) key k then Queue.enqueue q (key, elt));
Queue.to_list q
in
let fold2_alist =
access fold2 a b ~init:[] ~f:(fun ~key ~data:elt acc ->
if Key.( > ) key k then (key, elt) :: acc else acc)
|> List.rev
in
let expect =
[ map a ~f:Either.first; map b ~f:Either.second ]
|> List.concat_map ~f:to_alist
|> List.Assoc.sort_and_group ~compare:Key.compare
|> List.filter_map ~f:(fun (key, list) ->
let elt =
match (list : _ Either.t list) with
| [ First x ] -> `Left x
| [ Second y ] -> `Right y
| [ First x; Second y ] -> `Both (x, y)
| _ -> assert false
in
Option.some_if (Key.( > ) key k) (key, elt))
in
require_equal [%here] (module Alist_merge) merge_alist expect;
require_equal [%here] (module Alist_merge) iter2_alist expect;
require_equal [%here] (module Alist_merge) fold2_alist expect)
;;
let merge_disjoint_exn = merge_disjoint_exn
let () =
quickcheck_m
[%here]
(module Pair (Inst))
~f:(fun (a, b) ->
let actual = Option.try_with (fun () -> access merge_disjoint_exn a b) in
let expect =
if existsi a ~f:(fun ~key ~data:_ -> access mem b key)
then None
else
Some
(access merge a b ~f:(fun ~key:_ elt ->
match elt with
| `Left x | `Right x -> Some x
| `Both _ -> assert false))
in
require_equal [%here] (module Opt (Inst)) actual expect)
;;
let merge_skewed = merge_skewed
let () =
quickcheck_m
[%here]
(module Pair (Inst))
~f:(fun (a, b) ->
let actual = access merge_skewed a b ~combine:(fun ~key a b -> int key + a + b) in
let expect =
access merge a b ~f:(fun ~key elt ->
match elt with
| `Left a -> Some a
| `Right b -> Some b
| `Both (a, b) -> Some (int key + a + b))
in
require_equal [%here] (module Inst) actual expect)
;;
let symmetric_diff = symmetric_diff
let fold_symmetric_diff = fold_symmetric_diff
let () =
quickcheck_m
[%here]
(module Pair (Inst))
~f:(fun (a, b) ->
let diff_alist =
access symmetric_diff a b ~data_equal:Int.equal |> Sequence.to_list
in
let fold_alist =
access
fold_symmetric_diff
a
b
~data_equal:(fun x y -> Int.equal x y)
~init:[]
~f:(fun acc pair -> pair :: acc)
|> List.rev
in
let expect =
access merge a b ~f:(fun ~key:_ elt ->
match elt with
| `Left x -> Some (`Left x)
| `Right y -> Some (`Right y)
| `Both (x, y) -> if x = y then None else Some (`Unequal (x, y)))
|> to_alist
in
require_equal [%here] (module Diff) diff_alist expect;
require_equal [%here] (module Diff) fold_alist expect)
;;
let min_elt = min_elt
let max_elt = max_elt
let min_elt_exn = min_elt_exn
let max_elt_exn = max_elt_exn
let () =
quickcheck_m
[%here]
(module Inst)
~f:(fun t ->
require_equal
[%here]
(module Opt (Key_and_data))
(min_elt t)
(List.hd (to_alist t));
require_equal
[%here]
(module Opt (Key_and_data))
(max_elt t)
(List.last (to_alist t));
require_equal
[%here]
(module Opt (Key_and_data))
(Option.try_with (fun () -> min_elt_exn t))
(List.hd (to_alist t));
require_equal
[%here]
(module Opt (Key_and_data))
(Option.try_with (fun () -> max_elt_exn t))
(List.last (to_alist t)))
;;
let for_all = for_all
let for_alli = for_alli
let exists = exists
let existsi = existsi
let count = count
let counti = counti
let () =
quickcheck_m
[%here]
(module Inst_and_key_and_data)
~f:(fun (t, k, d) ->
let f data = data <= d in
let fi ~key ~data = Key.( <= ) key k && data <= d in
let fp (key, data) = fi ~key ~data in
let data = data t in
let alist = to_alist t in
require_equal [%here] (module Bool) (for_all t ~f) (List.for_all data ~f);
require_equal [%here] (module Bool) (for_alli t ~f:fi) (List.for_all alist ~f:fp);
require_equal [%here] (module Bool) (exists t ~f) (List.exists data ~f);
require_equal [%here] (module Bool) (existsi t ~f:fi) (List.exists alist ~f:fp);
require_equal [%here] (module Int) (count t ~f) (List.count data ~f);
require_equal [%here] (module Int) (counti t ~f:fi) (List.count alist ~f:fp))
;;
let sum = sum
let sumi = sumi
let () =
quickcheck_m
[%here]
(module Inst)
~f:(fun t ->
let f data = data * 2 in
let fi ~key ~data = (Instance.int key * 2) + (data * 3) in
let fp (key, data) = fi ~key ~data in
let m = (module Int : Container.Summable with type t = int) in
let data = data t in
let alist = to_alist t in
require_equal [%here] (module Int) (sum m t ~f) (List.sum m data ~f);
require_equal [%here] (module Int) (sumi m t ~f:fi) (List.sum m alist ~f:fp))
;;
let split = split
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, k) ->
require_equal
[%here]
(module struct
type t = Inst.t * (Key.t * int) option * Inst.t [@@deriving equal, sexp_of]
end)
(access split t k)
(let before, equal, after =
List.partition3_map (to_alist t) ~f:(fun (key, data) ->
match Ordering.of_int (Key.compare key k) with
| Less -> `Fst (key, data)
| Equal -> `Snd (key, data)
| Greater -> `Trd (key, data))
in
create of_alist_exn before, List.hd equal, create of_alist_exn after))
;;
let split_le_gt = split_le_gt
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, k) ->
require_equal
[%here]
(module struct
type t = Inst.t * Inst.t [@@deriving equal, sexp_of]
end)
(access split_le_gt t k)
(let before, after =
List.partition_tf (to_alist t) ~f:(fun (key, _) -> Key.( <= ) key k)
in
create of_alist_exn before, create of_alist_exn after))
;;
let split_lt_ge = split_lt_ge
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, k) ->
require_equal
[%here]
(module struct
type t = Inst.t * Inst.t [@@deriving equal, sexp_of]
end)
(access split_lt_ge t k)
(let before, after =
List.partition_tf (to_alist t) ~f:(fun (key, _) -> Key.( < ) key k)
in
create of_alist_exn before, create of_alist_exn after))
;;
let append = append
let () =
quickcheck_m
[%here]
(module Pair (Inst))
~f:(fun (a, b) ->
require_equal
[%here]
(module Ok (Inst))
(match access append ~lower_part:a ~upper_part:b with
| `Ok t -> Ok t
| `Overlapping_key_ranges -> Or_error.error_string "overlap")
(match max_elt a, min_elt b with
| Some (x, _), Some (y, _) when Key.( >= ) x y ->
Or_error.error_string "overlap"
| _ -> Ok (create of_alist_exn (to_alist a @ to_alist b)));
let a' =
(* we rely on the fact that the [Inst] generator uses positive keys *)
create map_keys_exn a ~f:(fun k -> key (-int k))
in
require_equal
[%here]
(module Ok (Inst))
(match access append ~lower_part:a' ~upper_part:b with
| `Ok t -> Ok t
| `Overlapping_key_ranges -> Or_error.error_string "overlap")
(Ok (create of_alist_exn (to_alist a' @ to_alist b))))
;;
let subrange = subrange
let fold_range_inclusive = fold_range_inclusive
let range_to_alist = range_to_alist
let () =
quickcheck_m
[%here]
(module struct
type t = Inst.t * Key.t Maybe_bound.t * Key.t Maybe_bound.t
[@@deriving quickcheck, sexp_of]
end)
~f:(fun (t, lower_bound, upper_bound) ->
let subrange_alist = access subrange t ~lower_bound ~upper_bound |> to_alist in
let min =
match lower_bound with
| Unbounded -> key Int.min_value
| Incl min -> min
| Excl too_small ->
(* key generator does not generate [max_value], so this cannot overflow *)
key (int too_small + 1)
in
let max =
match upper_bound with
| Unbounded -> key Int.max_value
| Incl max -> max
| Excl too_large ->
(* key generator does not generate [min_value], so this cannot overflow *)
key (int too_large - 1)
in
let fold_alist =
access fold_range_inclusive t ~min ~max ~init:[] ~f:(fun ~key ~data acc ->
(key, data) :: acc)
|> List.rev
in
let range_alist = access range_to_alist t ~min ~max in
let expect =
if Maybe_bound.bounds_crossed
~lower:lower_bound
~upper:upper_bound
~compare:Key.compare
then []
else
List.filter (to_alist t) ~f:(fun (key, _) ->
Maybe_bound.interval_contains_exn
key
~lower:lower_bound
~upper:upper_bound
~compare:Key.compare)
in
require_equal [%here] (module Alist) subrange_alist expect;
require_equal [%here] (module Alist) fold_alist expect;
require_equal [%here] (module Alist) range_alist expect)
;;
let closest_key = closest_key
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, k) ->
let alist = to_alist t in
let rev_alist = List.rev alist in
require_equal
[%here]
(module Opt (Key_and_data))
(access closest_key t `Less_than k)
(List.find rev_alist ~f:(fun (key, _) -> Key.( < ) key k));
require_equal
[%here]
(module Opt (Key_and_data))
(access closest_key t `Less_or_equal_to k)
(List.find rev_alist ~f:(fun (key, _) -> Key.( <= ) key k));
require_equal
[%here]
(module Opt (Key_and_data))
(access closest_key t `Greater_or_equal_to k)
(List.find alist ~f:(fun (key, _) -> Key.( >= ) key k));
require_equal
[%here]
(module Opt (Key_and_data))
(access closest_key t `Greater_than k)
(List.find alist ~f:(fun (key, _) -> Key.( > ) key k)))
;;
let nth = nth
let nth_exn = nth_exn
let rank = rank
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, k) ->
List.iteri (to_alist t) ~f:(fun i (key, data) ->
require_equal [%here] (module Opt (Key_and_data)) (nth t i) (Some (key, data));
require_equal
[%here]
(module Opt (Key_and_data))
(Option.try_with (fun () -> nth_exn t i))
(nth t i);
require_equal [%here] (module Opt (Int)) (access rank t key) (Some i));
require_equal [%here] (module Opt (Key_and_data)) (nth t (length t)) None;
require_equal
[%here]
(module Opt (Int))
(access rank t k)
(List.find_mapi (to_alist t) ~f:(fun i (key, _) ->
Option.some_if (Key.equal key k) i)))
;;
let binary_search = binary_search
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, k) ->
let targets = [%all: Binary_searchable.Which_target_by_key.t] in
let compare (key, _) k = Key.compare key k in
List.iter targets ~f:(fun which_target ->
require_equal
[%here]
(module Opt (Key_and_data))
(access
binary_search
t
~compare:(fun ~key ~data k' ->
require_equal [%here] (module Key) k' k;
require_equal [%here] (module Opt (Int)) (access find t key) (Some data);
compare (key, data) k')
which_target
k)
(let array = Array.of_list (to_alist t) in
Array.binary_search array ~compare which_target k
|> Option.map ~f:(Array.get array))))
;;
let binary_search_segmented = binary_search_segmented
let () =
quickcheck_m
[%here]
(module Inst_and_key)
~f:(fun (t, k) ->
let targets = [%all: Binary_searchable.Which_target_by_segment.t] in
let segment_of (key, _) = if Key.( <= ) key k then `Left else `Right in
List.iter targets ~f:(fun which_target ->
require_equal
[%here]
(module Opt (Key_and_data))
(access
binary_search_segmented
t
~segment_of:(fun ~key ~data ->
require_equal [%here] (module Opt (Int)) (access find t key) (Some data);
segment_of (key, data))
which_target)
(let array = Array.of_list (to_alist t) in
Array.binary_search_segmented array ~segment_of which_target
|> Option.map ~f:(Array.get array))))
;;
let binary_search_subrange = binary_search_subrange
let () =
quickcheck_m
[%here]
(module struct
type t = Inst.t * Key.t Maybe_bound.t * Key.t Maybe_bound.t
[@@deriving quickcheck, sexp_of]
end)
~f:(fun (t, lower_bound, upper_bound) ->
require_equal
[%here]
(module Inst)
(access
binary_search_subrange
t
~compare:(fun ~key ~data bound ->
require_equal [%here] (module Opt (Int)) (access find t key) (Some data);
Key.compare key bound)
~lower_bound
~upper_bound)
(access subrange t ~lower_bound ~upper_bound))
;;
module Make_applicative_traversals (A : Applicative.Lazy_applicative) = struct
module M = Make_applicative_traversals (A)
let mapi = M.mapi
let filter_mapi = M.filter_mapi
end
let () =
let module M =
Make_applicative_traversals (struct
module M = struct
type 'a t = 'a
let return x = x
let apply f x = f x
let of_thunk f = f ()
let map = `Define_using_apply
end
include M
include Applicative.Make (M)
end)
in
quickcheck_m
[%here]
(module Inst)
~f:(fun t ->
let f1 ~key:_ ~data = (data * 2) + 1 in
let f2 ~key:_ ~data = if data < 0 then None else Some data in
require_equal [%here] (module Inst) (mapi t ~f:f1) (M.mapi t ~f:f1);
require_equal [%here] (module Inst) (filter_mapi t ~f:f2) (M.filter_mapi t ~f:f2))
;;
(** tree conversion *)
let to_tree = to_tree
let of_tree = of_tree
let () =
quickcheck_m
[%here]
(module Inst)
~f:(fun t ->
let tree = to_tree t in
let round_trip = create of_tree tree in
require_equal [%here] (module Inst) t round_trip;
require_equal
[%here]
(module Alist)
(to_alist t)
(Map.Using_comparator.Tree.to_alist (Instance.tree tree)))
;;
end