open! Import let%test_module _ = (module ( struct open Avltree type ('k, 'v) t = ('k, 'v) Avltree.t = private | Empty | Node of { mutable left : ('k, 'v) t ; key : 'k ; mutable value : 'v ; mutable height : int ; mutable right : ('k, 'v) t } | Leaf of { key : 'k ; mutable value : 'v } module For_quickcheck = struct module Key = struct include Int type t = int [@@deriving quickcheck] let quickcheck_generator = Base_quickcheck.Generator.small_positive_or_zero_int ;; end module Data = struct include String type t = string [@@deriving quickcheck] let quickcheck_generator = Base_quickcheck.Generator.string_of Base_quickcheck.Generator.char_lowercase ;; end let compare = Key.compare module Constructor = struct type t = | Add of Key.t * Data.t | Replace of Key.t * Data.t | Remove of Key.t [@@deriving quickcheck, sexp_of] let apply_to_tree t tree = match t with | Add (key, data) -> add tree ~key ~data ~compare ~added:(ref false) ~replace:false | Replace (key, data) -> add tree ~key ~data ~compare ~added:(ref false) ~replace:true | Remove key -> remove tree key ~compare ~removed:(ref false) ;; let apply_to_map t map = match t with | Add (key, data) -> if Map.mem map key then map else Map.set map ~key ~data | Replace (key, data) -> Map.set map ~key ~data | Remove key -> Map.remove map key ;; end module Constructors = struct type t = Constructor.t list [@@deriving quickcheck, sexp_of] end let reify constructors = List.fold constructors ~init:(empty, Map.empty (module Key)) ~f:(fun (t, map) constructor -> ( Constructor.apply_to_tree constructor t , Constructor.apply_to_map constructor map )) ;; let merge map1 map2 = Map.merge map1 map2 ~f:(fun ~key variant -> match variant with | `Left data | `Right data -> Some data | `Both (data1, data2) -> Error.raise_s [%message "duplicate data for key" (key : Key.t) (data1 : Data.t) (data2 : Data.t)]) ;; let rec to_map = function | Empty -> Map.empty (module Key) | Leaf { key; value = data } -> Map.singleton (module Key) key data | Node { left; key; value = data; height = _; right } -> merge (Map.singleton (module Key) key data) (merge (to_map left) (to_map right)) ;; end open For_quickcheck let empty = empty let%test_unit _ = match empty with | Empty -> () | _ -> assert false ;; let is_empty = is_empty let%test _ = is_empty empty let%test_unit _ = Base_quickcheck.Test.run_exn (module Constructors) ~f:(fun constructors -> let t, map = reify constructors in [%test_result: bool] (is_empty t) ~expect:(Map.is_empty map)) ;; let invariant = invariant let%test_unit _ = Base_quickcheck.Test.run_exn (module Constructors) ~f:(fun constructors -> let t, map = reify constructors in invariant t ~compare; [%test_result: Data.t Map.M(Key).t] (to_map t) ~expect:map) ;; let add = add let%test_unit _ = Base_quickcheck.Test.run_exn (module struct type t = Constructor.t list * Key.t * Data.t * bool [@@deriving quickcheck, sexp_of] end) ~f:(fun (constructors, key, data, replace) -> let t, map = reify constructors in (* test [added], other aspects of [add] are tested via [reify] in the [invariant] test above *) let added = ref false in let (_ : (Key.t, Data.t) t) = add t ~key ~data ~compare ~added ~replace in [%test_result: bool] !added ~expect:(not (Map.mem map key))) ;; let remove = remove let%test_unit _ = Base_quickcheck.Test.run_exn (module struct type t = Constructors.t * Key.t [@@deriving quickcheck, sexp_of] end) ~f:(fun (constructors, key) -> let t, map = reify constructors in (* test [removed], other aspects of [remove] are tested via [reify] in the [invariant] test above *) let removed = ref false in let (_ : (Key.t, Data.t) t) = remove t key ~compare ~removed in [%test_result: bool] !removed ~expect:(Map.mem map key)) ;; let find = find let%test_unit _ = Base_quickcheck.Test.run_exn (module struct type t = Constructors.t * Key.t [@@deriving quickcheck, sexp_of] end) ~f:(fun (constructors, key) -> let t, map = reify constructors in [%test_result: Data.t option] (find t key ~compare) ~expect:(Map.find map key)) ;; let mem = mem let%test_unit _ = Base_quickcheck.Test.run_exn (module struct type t = Constructors.t * Key.t [@@deriving quickcheck, sexp_of] end) ~f:(fun (constructors, key) -> let t, map = reify constructors in [%test_result: bool] (mem t key ~compare) ~expect:(Map.mem map key)) ;; let first = first let%test_unit _ = Base_quickcheck.Test.run_exn (module Constructors) ~f:(fun constructors -> let t, map = reify constructors in [%test_result: (Key.t * Data.t) option] (first t) ~expect:(Map.min_elt map)) ;; let last = last let%test_unit _ = Base_quickcheck.Test.run_exn (module Constructors) ~f:(fun constructors -> let t, map = reify constructors in [%test_result: (Key.t * Data.t) option] (last t) ~expect:(Map.max_elt map)) ;; let find_and_call = find_and_call let%test_unit _ = Base_quickcheck.Test.run_exn (module struct type t = Constructors.t * Key.t [@@deriving quickcheck, sexp_of] end) ~f:(fun (constructors, key) -> let t, map = reify constructors in [%test_result: [ `Found of Data.t | `Not_found of Key.t ]] (find_and_call t key ~compare ~if_found:(fun data -> `Found data) ~if_not_found:(fun key -> `Not_found key)) ~expect: (match Map.find map key with | None -> `Not_found key | Some data -> `Found data)) ;; let findi_and_call = findi_and_call let%test_unit _ = Base_quickcheck.Test.run_exn (module struct type t = Constructors.t * Key.t [@@deriving quickcheck, sexp_of] end) ~f:(fun (constructors, key) -> let t, map = reify constructors in [%test_result: [ `Found of Key.t * Data.t | `Not_found of Key.t ]] (findi_and_call t key ~compare ~if_found:(fun ~key ~data -> `Found (key, data)) ~if_not_found:(fun key -> `Not_found key)) ~expect: (match Map.find map key with | None -> `Not_found key | Some data -> `Found (key, data))) ;; let find_and_call1 = find_and_call1 let%test_unit _ = Base_quickcheck.Test.run_exn (module struct type t = Constructors.t * Key.t * int [@@deriving quickcheck, sexp_of] end) ~f:(fun (constructors, key, a) -> let t, map = reify constructors in [%test_result: [ `Found of Data.t * int | `Not_found of Key.t * int ]] (find_and_call1 t key ~compare ~a ~if_found:(fun data a -> `Found (data, a)) ~if_not_found:(fun key a -> `Not_found (key, a))) ~expect: (match Map.find map key with | None -> `Not_found (key, a) | Some data -> `Found (data, a))) ;; let findi_and_call1 = findi_and_call1 let%test_unit _ = Base_quickcheck.Test.run_exn (module struct type t = Constructors.t * Key.t * int [@@deriving quickcheck, sexp_of] end) ~f:(fun (constructors, key, a) -> let t, map = reify constructors in [%test_result: [ `Found of Key.t * Data.t * int | `Not_found of Key.t * int ]] (findi_and_call1 t key ~compare ~a ~if_found:(fun ~key ~data a -> `Found (key, data, a)) ~if_not_found:(fun key a -> `Not_found (key, a))) ~expect: (match Map.find map key with | None -> `Not_found (key, a) | Some data -> `Found (key, data, a))) ;; let find_and_call2 = find_and_call2 let%test_unit _ = Base_quickcheck.Test.run_exn (module struct type t = Constructors.t * Key.t * int * string [@@deriving quickcheck, sexp_of] end) ~f:(fun (constructors, key, a, b) -> let t, map = reify constructors in [%test_result: [ `Found of Data.t * int * string | `Not_found of Key.t * int * string ]] (find_and_call2 t key ~compare ~a ~b ~if_found:(fun data a b -> `Found (data, a, b)) ~if_not_found:(fun key a b -> `Not_found (key, a, b))) ~expect: (match Map.find map key with | None -> `Not_found (key, a, b) | Some data -> `Found (data, a, b))) ;; let findi_and_call2 = findi_and_call2 let%test_unit _ = Base_quickcheck.Test.run_exn (module struct type t = Constructors.t * Key.t * int * string [@@deriving quickcheck, sexp_of] end) ~f:(fun (constructors, key, a, b) -> let t, map = reify constructors in [%test_result: [ `Found of Key.t * Data.t * int * string | `Not_found of Key.t * int * string ]] (findi_and_call2 t key ~compare ~a ~b ~if_found:(fun ~key ~data a b -> `Found (key, data, a, b)) ~if_not_found:(fun key a b -> `Not_found (key, a, b))) ~expect: (match Map.find map key with | None -> `Not_found (key, a, b) | Some data -> `Found (key, data, a, b))) ;; let iter = iter let%test_unit _ = Base_quickcheck.Test.run_exn (module Constructors) ~f:(fun constructors -> let t, map = reify constructors in [%test_result: (Key.t * Data.t) list] (let q = Queue.create () in iter t ~f:(fun ~key ~data -> Queue.enqueue q (key, data)); Queue.to_list q) ~expect:(Map.to_alist map)) ;; let mapi_inplace = mapi_inplace let%test_unit _ = Base_quickcheck.Test.run_exn (module Constructors) ~f:(fun constructors -> let t, map = reify constructors in [%test_result: (Key.t * Data.t) list] (mapi_inplace t ~f:(fun ~key:_ ~data -> data ^ data); fold t ~init:[] ~f:(fun ~key ~data acc -> (key, data) :: acc)) ~expect: (Map.map map ~f:(fun data -> data ^ data) |> Map.to_alist |> List.rev)) ;; let fold = fold let%test_unit _ = Base_quickcheck.Test.run_exn (module Constructors) ~f:(fun constructors -> let t, map = reify constructors in [%test_result: (Key.t * Data.t) list] (fold t ~init:[] ~f:(fun ~key ~data acc -> (key, data) :: acc)) ~expect:(Map.to_alist map |> List.rev)) ;; let choose_exn = choose_exn let%test_unit _ = Base_quickcheck.Test.run_exn (module Constructors) ~f:(fun constructors -> let t, map = reify constructors in [%test_result: bool] (is_some (Option.try_with (fun () -> choose_exn t))) ~expect:(not (Map.is_empty map))) ;; end : module type of Avltree)) ;;