Source file test_binary_searchable.ml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
open! Base
open! Binary_searchable
include Test_binary_searchable_intf

module type S_gen = sig
  open Binary_searchable

  type 'a t
  type 'a elt

  val binary_search : ('a t, 'a elt, 'a elt) binary_search
  val binary_search_segmented : ('a t, 'a elt) binary_search_segmented
end

module type Indexable_gen_and_for_test = sig
  include S_gen

  module For_test : sig
    val compare : bool elt -> bool elt -> int
    val small : bool elt
    val big : bool elt
    val of_array : bool elt array -> bool t
  end
end

module Test_gen (M : Indexable_gen_and_for_test) = struct
  open M

  let%test_module "test_binary_searchable" =
    (module struct
      let compare = For_test.compare
      let elt_compare = For_test.compare
      let s = For_test.small
      let b = For_test.big

      let binary_search ?pos ?len ~compare t how v =
        binary_search ?pos ?len ~compare (For_test.of_array t) how v
      ;;

      let ( = ) = Poly.equal
      let%test _ = binary_search ~compare [||] `First_equal_to s = None
      let%test _ = binary_search ~compare [| s |] `First_equal_to s = Some 0
      let%test _ = binary_search ~compare [| s |] `First_equal_to b = None
      let%test _ = binary_search ~compare [| s; b |] `First_equal_to s = Some 0
      let%test _ = binary_search ~compare [| s; b |] `First_equal_to b = Some 1
      let%test _ = binary_search ~compare [| b; b |] `First_equal_to s = None
      let%test _ = binary_search ~compare [| s; s |] `First_equal_to b = None
      let%test _ = binary_search ~compare [| s; b; b |] `First_equal_to b = Some 1
      let%test _ = binary_search ~compare [| s; s; b |] `First_equal_to s = Some 0
      let%test _ = binary_search ~compare [| b; b; b |] `First_equal_to s = None
      let%test _ = binary_search ~compare [||] `Last_equal_to s = None
      let%test _ = binary_search ~compare [| s |] `Last_equal_to s = Some 0
      let%test _ = binary_search ~compare [| s |] `Last_equal_to b = None
      let%test _ = binary_search ~compare [| s; b |] `Last_equal_to b = Some 1
      let%test _ = binary_search ~compare [| s; b |] `Last_equal_to s = Some 0
      let%test _ = binary_search ~compare [| b; b |] `Last_equal_to s = None
      let%test _ = binary_search ~compare [| s; s |] `Last_equal_to b = None
      let%test _ = binary_search ~compare [| s; b; b |] `Last_equal_to b = Some 2
      let%test _ = binary_search ~compare [| s; s; b |] `Last_equal_to s = Some 1
      let%test _ = binary_search ~compare [| b; b; b |] `Last_equal_to s = None
      let%test _ = binary_search ~compare [||] `First_greater_than_or_equal_to s = None

      let%test _ =
        binary_search ~compare [| b |] `First_greater_than_or_equal_to s = Some 0
      ;;

      let%test _ =
        binary_search ~compare [| s |] `First_greater_than_or_equal_to s = Some 0
      ;;

      let%test _ = binary_search ~compare [| s |] `First_strictly_greater_than s = None
      let%test _ = binary_search ~compare [||] `Last_less_than_or_equal_to s = None
      let%test _ = binary_search ~compare [| b |] `Last_less_than_or_equal_to s = None
      let%test _ = binary_search ~compare [| s |] `Last_less_than_or_equal_to s = Some 0
      let%test _ = binary_search ~compare [| s |] `Last_strictly_less_than s = None

      let create_test_case (num_s, num_b) =
        let arr = Array.create b ~len:(num_s + num_b) in
        for i = 0 to num_s - 1 do
          arr.(i) <- s
        done;
        arr
      ;;

      let only_small = 10_000, 0
      let only_big = 0, 10_000
      let both = 2531, 4717

      let%test _ =
        match binary_search (create_test_case only_small) ~compare `First_equal_to s with
        | None -> false
        | Some _ -> true
      ;;

      let%test _ =
        let arr = create_test_case both in
        match binary_search arr ~compare `First_equal_to b with
        | None -> false
        | Some v -> v = 2531
      ;;

      let%test _ =
        let arr = create_test_case only_small in
        binary_search arr ~compare `First_equal_to b = None
      ;;

      let create_deterministic_test () =
        Array.init 100_000 ~f:(fun i -> if i > 50_000 then b else s)
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `First_equal_to s = Some 0
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `Last_equal_to s = Some 50_000
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `First_greater_than_or_equal_to s = Some 0
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `Last_less_than_or_equal_to s = Some 50_000
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `First_strictly_greater_than s = Some 50_001
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `Last_strictly_less_than b = Some 50_000
      ;;

      (* tests around a gap*)
      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `First_equal_to b = Some 50_001
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `Last_equal_to b = Some 99_999
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `First_greater_than_or_equal_to b = Some 50_001
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `Last_less_than_or_equal_to b = Some 99_999
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `First_strictly_greater_than b = None
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        binary_search arr ~compare `Last_strictly_less_than b = Some 50_000
      ;;

      (* test beginning of array *)

      let%test _ =
        let arr = create_test_case only_big in
        binary_search arr ~compare `First_equal_to s = None
      ;;

      let%test _ =
        let arr = create_test_case only_big in
        binary_search arr ~compare `Last_equal_to s = None
      ;;

      let%test _ =
        let arr = create_test_case only_big in
        binary_search arr ~compare `First_greater_than_or_equal_to s = Some 0
      ;;

      let%test _ =
        let arr = create_test_case only_big in
        binary_search arr ~compare `Last_less_than_or_equal_to s = None
      ;;

      let%test _ =
        let arr = create_test_case only_big in
        binary_search arr ~compare `First_strictly_greater_than s = Some 0
      ;;

      let%test _ =
        let arr = create_test_case only_big in
        binary_search arr ~compare `Last_strictly_less_than b = None
      ;;

      (* test end of array *)

      let%test _ =
        let arr = create_test_case only_small in
        binary_search arr ~compare `First_equal_to b = None
      ;;

      let%test _ =
        let arr = create_test_case only_small in
        binary_search arr ~compare `Last_equal_to b = None
      ;;

      let%test _ =
        let arr = create_test_case only_small in
        binary_search arr ~compare `First_greater_than_or_equal_to b = None
      ;;

      let%test _ =
        let arr = create_test_case only_small in
        binary_search arr ~compare `Last_less_than_or_equal_to b = Some 9_999
      ;;

      let%test _ =
        let arr = create_test_case only_small in
        binary_search arr ~compare `First_strictly_greater_than s = None
      ;;

      let%test _ =
        let arr = create_test_case only_small in
        binary_search arr ~compare `Last_strictly_less_than b = Some 9_999
      ;;

      let%test_unit _ =
        for length = 0 to 5 do
          for num_s = 0 to length do
            let arr = Array.init length ~f:(fun i -> if i < num_s then s else b) in
            for pos = -1 to length do
              for len = -1 to length + 1 do
                (*try*)
                let should_raise =
                  Exn.does_raise (fun () ->
                    Ordered_collection_common.check_pos_len_exn
                      ~pos
                      ~len
                      ~total_length:length)
                in
                let result =
                  Result.try_with (fun () ->
                    binary_search arr ~pos ~len ~compare:elt_compare `Last_equal_to s)
                in
                match should_raise, result with
                | true, Error _ -> ()
                | true, Ok _ -> failwith "expected it to raise but it didn't"
                | false, Error _ -> failwith "expected it to not raise, but it raised"
                | false, Ok result ->
                  let searched = num_s - 1 in
                  let correct_result =
                    if searched < pos
                    then None
                    else if len = 0
                    then None
                    else if searched >= pos + len
                    then Some (pos + len - 1)
                    else Some searched
                  in
                  if not (correct_result = result) then failwith "Wrong result"
                (*with exn ->
                    failwiths "binary_search bug"
                    (exn, `length length, `search_key search_key, `pos pos, `len len)
                    <:sexp_of< exn * [ `length of int ] * [ `search_key of int ]
                 * [ `pos of int ] * [ `len of int ] >>*)
              done
            done
          done
        done
      ;;

      let binary_search_segmented a = binary_search_segmented (For_test.of_array a)

      (*test for binary_search_segmented*)
      let%test _ =
        let arr = create_deterministic_test () in
        let segment_of x = if x = b then `Right else `Left in
        binary_search_segmented arr ~segment_of `Last_on_left = Some 50_000
        && binary_search_segmented arr ~segment_of `First_on_right = Some 50_001
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        let segment_of _ = `Right in
        binary_search_segmented arr ~segment_of `Last_on_left = None
        && binary_search_segmented arr ~segment_of `First_on_right = Some 0
      ;;

      let%test _ =
        let arr = create_deterministic_test () in
        let segment_of _ = `Left in
        binary_search_segmented arr ~segment_of `Last_on_left = Some 99_999
        && binary_search_segmented arr ~segment_of `First_on_right = None
      ;;
    end)
  ;;
end

module Test (M : Binary_searchable_and_for_test) = Test_gen (struct
  type 'a t = M.t
  type 'a elt = M.elt

  let binary_search = M.binary_search
  let binary_search_segmented = M.binary_search_segmented

  module For_test = M.For_test
end)

module Test1 (M : Binary_searchable1_and_for_test) = Test_gen (struct
  type 'a t = 'a M.t
  type 'a elt = 'a

  let binary_search = M.binary_search
  let binary_search_segmented = M.binary_search_segmented

  module For_test = struct
    let of_array = M.For_test.of_array
    let compare = Bool.compare
    let small = false
    let big = true
  end
end)

module Make_and_test (M : Indexable_and_for_test) = struct
  module B = Binary_searchable.Make (M)
  include B

  include Test (struct
    type t = M.t
    type elt = M.elt

    include B
    module For_test = M.For_test
  end)
end

module Make1_and_test (M : Indexable1_and_for_test) = struct
  module B = Binary_searchable.Make1 (M)
  include B

  include Test1 (struct
    type 'a t = 'a M.t

    include B
    module For_test = M.For_test
  end)
end