• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MinaProtocol / mina / 2863

05 Nov 2024 06:20PM UTC coverage: 30.754% (-16.6%) from 47.311%
2863

push

buildkite

web-flow
Merge pull request #16296 from MinaProtocol/dkijania/more_multi_jobs

more multi jobs in CI

20276 of 65930 relevant lines covered (30.75%)

8631.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.78
/src/lib/network_pool/f_sequence.ml
1
open Core
1✔
2

3
(** A digit is a container of 1-4 elements. *)
4
module Digit = struct
5
  (* We use GADTs to track whether it's valid to remove/add an element to a
6
     digit, which gets us type safe (un)cons and (un)snoc. *)
7

8
  [@@@warning "-37"]
9

10
  type addable = Type_addable
11

12
  type not_addable = Type_not_addable
13

14
  type removable = Type_removable
15

16
  type not_removable = Type_not_removable
17

18
  [@@@warning "+37"]
19

20
  type (_, _, 'e) t =
21
    | One : 'e -> (addable, not_removable, 'e) t
22
    | Two : 'e * 'e -> (addable, removable, 'e) t
23
    | Three : 'e * 'e * 'e -> (addable, removable, 'e) t
24
    | Four : 'e * 'e * 'e * 'e -> (not_addable, removable, 'e) t
25

26
  (* Can't derive compare on GADTs, instead we provide an explicit instance. *)
27
  let compare (type add_x rem_x add_y rem_y) cmp_e (x : (add_x, rem_x, 'e) t)
28
      (y : (add_y, rem_y, 'e) t) =
29
    let fallthrough ~f = function 0 -> f () | n -> n in
×
30
    match (x, y) with
31
    | One x, One y ->
×
32
        cmp_e x y
33
    | One _, _ ->
×
34
        -1
35
    | _, One _ ->
×
36
        1
37
    | Two (x1, x2), Two (y1, y2) ->
×
38
        fallthrough (cmp_e x1 y1) ~f:(fun () -> cmp_e x2 y2)
×
39
    | Two _, _ ->
×
40
        -1
41
    | _, Two _ ->
×
42
        1
43
    | Three (x1, x2, x3), Three (y1, y2, y3) ->
×
44
        fallthrough (cmp_e x1 y1) ~f:(fun () ->
×
45
            fallthrough (cmp_e x2 y2) ~f:(fun () -> cmp_e x3 y3) )
×
46
    | Three _, _ ->
×
47
        -1
48
    | _, Three _ ->
×
49
        1
50
    | Four (x1, x2, x3, x4), Four (y1, y2, y3, y4) ->
×
51
        fallthrough (cmp_e x1 y1) ~f:(fun () ->
×
52
            fallthrough (cmp_e x2 y2) ~f:(fun () ->
×
53
                fallthrough (cmp_e x3 y3) ~f:(fun () -> cmp_e x4 y4) ) )
×
54
    | Four _, _ ->
55
        .
56
    | _, Four _ ->
57
        .
58

59
  (* "Eliminators" dispatching on addability/removability. You could achieve
60
      the same effect more directly using or-patterns, but the code that
61
      makes the typechecker understand existentials under or-patterns isn't
62
      in our compiler version. (ocaml/ocaml#2110)
63
  *)
64
  let addable_elim :
65
      type a r.
66
         ((addable, r, 'e) t -> 'o) (** Function handling addable case *)
67
      -> ((not_addable, removable, 'e) t -> 'o)
68
         (** Function handling non-addable case *)
69
      -> (a, r, 'e) t
70
      -> 'o =
71
   fun f g t ->
72
    match t with One _ -> f t | Two _ -> f t | Three _ -> f t | Four _ -> g t
×
73

74
  let removable_elim :
75
      type a r.
76
         ((a, removable, 'e) t -> 'o) (** Function handling removable case*)
77
      -> ((addable, not_removable, 'e) t -> 'o)
78
         (** Function handling non-removable case *)
79
      -> (a, r, 'e) t
80
      -> 'o =
81
   fun f g t ->
82
    match t with One _ -> g t | Two _ -> f t | Three _ -> f t | Four _ -> f t
×
83

84
  (** Existential type for when addability is determined at runtime. *)
85
  type ('r, 'e) t_any_a = Mk_any_a : ('a, 'r, 'e) t -> ('r, 'e) t_any_a
86

87
  (** Same for removability. *)
88
  type ('a, 'e) t_any_r = Mk_any_r : ('a, 'r, 'e) t -> ('a, 'e) t_any_r
89

90
  (** Both. *)
91
  type 'e t_any_ar = Mk_any_ar : ('a, 'r, 'e) t -> 'e t_any_ar
92

93
  (** "Broaden" a t_any_a into a t_any_ar, i.e. forget that we know the
94
      removability status. *)
95
  let broaden_any_a : ('r, 'e) t_any_a -> 'e t_any_ar =
96
   fun (Mk_any_a t) -> Mk_any_ar t
×
97

98
  (** Same deal for t_any_r *)
99
  let broaden_any_r : ('a, 'e) t_any_r -> 'e t_any_ar =
100
   fun (Mk_any_r t) -> Mk_any_ar t
×
101

102
  let cons : type r. 'e -> (addable, r, 'e) t -> (removable, 'e) t_any_a =
103
   fun v d ->
104
    match d with
×
105
    | One a ->
×
106
        Mk_any_a (Two (v, a))
107
    | Two (a, b) ->
×
108
        Mk_any_a (Three (v, a, b))
109
    | Three (a, b, c) ->
×
110
        Mk_any_a (Four (v, a, b, c))
111

112
  let snoc : type r. (addable, r, 'e) t -> 'e -> (removable, 'e) t_any_a =
113
   fun d v ->
114
    match d with
×
115
    | One a ->
×
116
        Mk_any_a (Two (a, v))
117
    | Two (a, b) ->
×
118
        Mk_any_a (Three (a, b, v))
119
    | Three (a, b, c) ->
×
120
        Mk_any_a (Four (a, b, c, v))
121

122
  let uncons : type a. (a, removable, 'e) t -> 'e * (addable, 'e) t_any_r =
123
    function
124
    | Two (a, b) ->
×
125
        (a, Mk_any_r (One b))
126
    | Three (a, b, c) ->
×
127
        (a, Mk_any_r (Two (b, c)))
128
    | Four (a, b, c, d) ->
×
129
        (a, Mk_any_r (Three (b, c, d)))
130

131
  let unsnoc : type a. (a, removable, 'e) t -> (addable, 'e) t_any_r * 'e =
132
    function
133
    | Two (a, b) ->
×
134
        (Mk_any_r (One a), b)
135
    | Three (a, b, c) ->
×
136
        (Mk_any_r (Two (a, b)), c)
137
    | Four (a, b, c, d) ->
×
138
        (Mk_any_r (Three (a, b, c)), d)
139

140
  let foldr : type a r. ('e -> 'acc -> 'acc) -> 'acc -> (a, r, 'e) t -> 'acc =
141
   fun f z d ->
142
    match d with
×
143
    | One a ->
×
144
        f a z
145
    | Two (a, b) ->
×
146
        f a (f b z)
×
147
    | Three (a, b, c) ->
×
148
        f a (f b (f c z))
×
149
    | Four (a, b, c, d) ->
×
150
        f a (f b (f c (f d z)))
×
151

152
  let foldl : type a r. ('acc -> 'e -> 'acc) -> 'acc -> (a, r, 'e) t -> 'acc =
153
   fun f z d ->
154
    match d with
×
155
    | One a ->
×
156
        f z a
157
    | Two (a, b) ->
×
158
        f (f z a) b
×
159
    | Three (a, b, c) ->
×
160
        f (f (f z a) b) c
×
161
    | Four (a, b, c, d) ->
×
162
        f (f (f (f z a) b) c) d
×
163

164
  let to_list : type a r. (a, r, 'e) t -> 'e list =
165
   fun t -> foldr List.cons [] t
×
166

167
  let gen_any_ar : int t_any_ar Quickcheck.Generator.t =
168
    let open Quickcheck.Generator.Let_syntax in
169
    let gen_measure = Int.gen_incl 1 20 in
170
    let%bind a, b, c, d =
171
      Quickcheck.Generator.tuple4 gen_measure gen_measure gen_measure
1✔
172
        gen_measure
173
    in
174
    Quickcheck.Generator.of_list
×
175
      [ Mk_any_ar (One a)
176
      ; Mk_any_ar (Two (a, b))
177
      ; Mk_any_ar (Three (a, b, c))
178
      ; Mk_any_ar (Four (a, b, c, d))
179
      ]
180

181
  (** Given a measurement function, compute the total measure of a digit.
182
      See below for an explanation of what measure is.
183
  *)
184
  let measure : ('e -> int) -> (_, _, 'e) t -> int =
185
   fun measure' -> foldl (fun m e -> m + measure' e) 0
×
186

187
  (** Split a digit by measure. Again see below. *)
188
  let split :
189
      type a r.
190
         ('e -> int)
191
      -> int
192
      -> int
193
      -> (a, r, 'e) t
194
      -> 'e t_any_ar option * 'e * 'e t_any_ar option =
195
   fun measure' target acc t ->
196
    (* Addable inputs go to addable outputs, but non-addable inputs may go to
197
       either. We use a separate function for addables to represent this and
198
       minimizing the amount of Obj.magicking we need to do. *)
199
    let rec split_addable :
×
200
        type r.
201
           int
202
        -> (addable, r, 'e) t
203
        -> (addable, 'e) t_any_r option * 'e * (addable, 'e) t_any_r option =
204
     fun acc t ->
205
      removable_elim
×
206
        (fun t' ->
207
          let head, Mk_any_r tail = uncons t' in
×
208
          if acc + measure' head >= target then
×
209
            (None, head, Some (Mk_any_r tail))
×
210
          else
211
            match split_addable (acc + measure' head) tail with
×
212
            | Some (Mk_any_r lhs), m, rhs ->
×
213
                let (Mk_any_a cons_res) = cons head lhs in
214
                (* t' is addable, so the tail of t' is twice-addable. We just
215
                   passed that tail to split_addable, which always returns
216
                   digits with <= the number of elements of the input. So
217
                   cons_res is addable but it's not possible to convince the
218
                   typechecker of that, as far as I can tell.
219
                *)
220
                let cons_res' : (addable, removable, 'e) t =
×
221
                  Obj.magic cons_res
222
                in
223
                (Some (Mk_any_r cons_res'), m, rhs)
224
            | None, m, rhs ->
×
225
                (Some (Mk_any_r (One head)), m, rhs) )
226
        (fun (One a) ->
227
          if acc + measure' a >= target then (None, a, None)
×
228
          else failwith "Digit.split index out of bounds" )
×
229
        t
230
    in
231
    addable_elim
232
      (fun t' ->
233
        let lhs, m, rhs = split_addable acc t' in
×
234
        (Option.map ~f:broaden_any_r lhs, m, Option.map ~f:broaden_any_r rhs) )
×
235
      (fun t' ->
236
        let head, Mk_any_r tail = uncons t' in
×
237
        if acc + measure' head >= target then (None, head, Some (Mk_any_ar tail))
×
238
        else
239
          let lhs, m, rhs = split_addable (acc + measure' head) tail in
×
240
          match lhs with
×
241
          | None ->
×
242
              (Some (Mk_any_ar (One head)), m, Option.map ~f:broaden_any_r rhs)
×
243
          | Some (Mk_any_r lhs') ->
×
244
              ( Some (broaden_any_a (cons head lhs'))
×
245
              , m
246
              , Option.map ~f:broaden_any_r rhs ) )
×
247
      t
248

249
  let opt_to_list : 'a t_any_ar option -> 'a list = function
250
    | None ->
×
251
        []
252
    | Some (Mk_any_ar dig) ->
×
253
        to_list dig
254

255
  let%test_unit "Digit.split preserves contents and order" =
256
    Quickcheck.test
×
257
      (let open Quickcheck.Generator.Let_syntax in
258
      let%bind (Mk_any_ar dig as dig') = gen_any_ar in
259
      let%bind idx = Int.gen_incl 1 (List.length @@ to_list dig) in
×
260
      return (dig', idx))
×
261
      ~f:(fun (Mk_any_ar dig, target) ->
262
        let lhs_opt, m, rhs_opt = split Fn.id target 0 dig in
×
263
        let lhs', rhs' = (opt_to_list lhs_opt, opt_to_list rhs_opt) in
×
264
        [%test_eq: int list] (lhs' @ [ m ] @ rhs') (to_list dig) )
×
265

266
  let%test_unit "Digit.split matches list implementation" =
267
    Quickcheck.test
×
268
      ~sexp_of:(fun (Mk_any_ar dig, idx) ->
269
        Tuple2.sexp_of_t
×
270
          (List.sexp_of_t Int.sexp_of_t)
×
271
          Int.sexp_of_t
272
          (to_list dig, idx) )
×
273
      (let open Quickcheck.Generator.Let_syntax in
274
      let%bind (Mk_any_ar dig) = gen_any_ar in
275
      let%bind idx = Int.gen_incl 1 (List.length @@ to_list dig) in
×
276
      return (Mk_any_ar dig, idx))
×
277
      ~f:(fun (Mk_any_ar dig, idx) ->
278
        let as_list = to_list dig in
×
279
        let lhs_list = List.take as_list (idx - 1) in
×
280
        let m_list = List.nth_exn as_list (idx - 1) in
×
281
        let rhs_list = List.drop as_list idx in
×
282
        [%test_eq: int list] (lhs_list @ (m_list :: rhs_list)) as_list ;
×
283
        let lhs_fseq, m_fseq, rhs_fseq = split (Fn.const 1) idx 0 dig in
×
284
        let lhs_fseq', rhs_fseq' =
×
285
          (opt_to_list lhs_fseq, opt_to_list rhs_fseq)
×
286
        in
287
        [%test_eq: int list] (lhs_fseq' @ (m_fseq :: rhs_fseq')) (to_list dig) ;
×
288
        [%test_eq: int list] lhs_list lhs_fseq' ;
×
289
        [%test_eq: int] m_list m_fseq ;
×
290
        [%test_eq: int list] rhs_list rhs_fseq' ;
×
291
        [%test_eq: int] (List.length lhs_fseq') (idx - 1) ;
×
292
        [%test_eq: int] (List.length rhs_fseq') (List.length as_list - idx) )
×
293

294
  (* See comment below about measures for why index 0 is an edge case. *)
295
  let%test_unit "Digit.split with index 0 is trivial" =
296
    Quickcheck.test
×
297
      (Quickcheck.Generator.tuple2 gen_any_ar (Int.gen_incl 0 200))
×
298
      ~f:(fun (Mk_any_ar dig, acc) ->
299
        let as_list = to_list dig in
×
300
        let lhs, m, rhs = split Fn.id acc acc dig in
×
301
        assert (Option.is_none lhs) ;
×
302
        [%test_eq: int] m (List.hd_exn as_list) ;
×
303
        match rhs with
×
304
        | None ->
×
305
            [%test_eq: int list] [] (List.tl_exn as_list)
×
306
        | Some (Mk_any_ar rhs') ->
×
307
            [%test_eq: int list] (to_list rhs') (List.tl_exn as_list) )
×
308

309
  let%test _ =
310
    match split Fn.id 1 0 (One 1) with None, 1, None -> true | _ -> false
×
311

312
  let%test _ =
313
    match split Fn.id 5 0 (Three (0, 2, 4)) with
×
314
    | Some (Mk_any_ar (Two (0, 2))), 4, None ->
×
315
        true
316
    | _ ->
×
317
        false
318

319
  let%test _ =
320
    match split Fn.id 10 0 (Four (2, 3, 5, 1)) with
×
321
    | Some (Mk_any_ar (Two (2, 3))), 5, Some (Mk_any_ar (One 1)) ->
×
322
        true
323
    | _ ->
×
324
        false
325

326
  let%test _ =
327
    match split Fn.id 7 0 (Four (2, 4, 3, 2)) with
×
328
    | Some (Mk_any_ar (Two (2, 4))), 3, Some (Mk_any_ar (One 2)) ->
×
329
        true
330
    | _ ->
×
331
        false
332
end
333

334
(** Nodes containing 2-3 elements, with a cached measurement. *)
335
module Node = struct
336
  (** This implementation doesn't actually use 2-nodes, but they're here for
337
      future use. The paper uses them in the append operation, which isn't
338
      implemented here.
339
  *)
340
  type 'e t = Two of int * 'e * 'e | Three of int * 'e * 'e * 'e
×
341
  [@@deriving equal, compare]
342

343
  (** Extract the cached measurement *)
344
  let measure : 'e t -> int =
345
   fun t -> match t with Two (m, _, _) -> m | Three (m, _, _, _) -> m
×
346

347
  let to_digit : 'e t -> (Digit.addable, Digit.removable, 'e) Digit.t = function
348
    | Two (_m, a, b) ->
×
349
        Digit.Two (a, b)
350
    | Three (_m, a, b, c) ->
×
351
        Digit.Three (a, b, c)
352

353
  (* smart constructors to maintain correct measures *)
354
  let _mk_2 : ('e -> int) -> 'e -> 'e -> 'e t =
355
   fun f a b -> Two (f a + f b, a, b)
×
356

357
  let mk_3 : ('e -> int) -> 'e -> 'e -> 'e -> 'e t =
358
   fun f a b c -> Three (f a + f b + f c, a, b, c)
×
359

360
  let split_to_digits :
361
         ('e -> int)
362
      -> int
363
      -> int
364
      -> 'e t
365
      -> 'e Digit.t_any_ar option * 'e * 'e Digit.t_any_ar option =
366
   fun measure' target acc t -> to_digit t |> Digit.split measure' target acc
×
367
end
368

369
(** Finally, the actual finger tree type! *)
370
type 'e t =
371
  | Empty : 'e t  (** Empty tree *)
372
  | Single : 'e -> 'e t  (** Single element tree *)
373
  | Deep :
374
      ( int
375
      * ('aL, 'rL, 'e) Digit.t
376
      * 'e Node.t t Lazy.t
377
      * ('aR, 'rR, 'e) Digit.t )
378
      -> 'e t
379
      (** The recursive case. We have a cached measurement, prefix and suffix
380
          fingers, and a subtree. Note the subtree has a different type than its
381
          parent. The top level has 'es, the next level has 'e Node.ts, the next
382
          has 'e Node.t Node.ts and so on. As you go deeper, the breadth
383
          increases exponentially. *)
384

385
(* Can't derive compare for GADTs.. *)
386
let rec compare : type e. (e -> e -> int) -> e t -> e t -> int =
387
 fun cmp_e x y ->
388
  match (x, y) with
×
389
  | Empty, Empty ->
×
390
      0
391
  | Empty, _ ->
×
392
      -1
393
  | _, Empty ->
×
394
      1
395
  | Single x, Single y ->
×
396
      cmp_e x y
397
  | Single _, _ ->
×
398
      -1
399
  | _, Single _ ->
×
400
      1
401
  | Deep (i, pre_x, mid_x, suff_x), Deep (j, pre_y, mid_y, suff_y) ->
×
402
      let cmp = Int.compare i j in
403
      if cmp <> 0 then cmp
×
404
      else
405
        let cmp = Digit.compare cmp_e pre_x pre_y in
×
406
        if cmp <> 0 then cmp
×
407
        else
408
          let cmp = Lazy.compare (compare (Node.compare cmp_e)) mid_x mid_y in
×
409
          if cmp <> 0 then cmp else Digit.compare cmp_e suff_x suff_y
×
410
  | Deep _, _ ->
411
      .
412
  | _, Deep _ ->
413
      .
414

415
(* About measurements: in the paper they define finger trees more generally than
416
   this implementation. Given a monoid m, a measurement function for elements
417
   e -> m, and "monotonic" predicates on m, if you cache the measure of subtrees
418
   you can index into and split finger trees at the transition point of the
419
   predicates in log time. The output of any of the split functions is a triple
420
   of the longest subsequence starting from the beginning where the predicate is
421
   false, the element that flips it to true, and the subsequence after that up
422
   to the end. In this implementation the monoid is natural numbers under
423
   summation, the measurement is 'Fn.const 1' and the predicates are
424
   (fun x -> x >= idx). So the measure of a tree is how many elements are in it
425
   and the transition point is where there are >= idx elements. Index 0 is a
426
   special case, since forall x : â„•. x >= 0. In that case the first subsequence
427
   is empty, the transition point is the first element, and the second
428
   subsequence is the rest of the input sequence.
429

430
   You'll see many functions take a parameter measure' to compute measures of
431
   elements with. This is always either Node.measure or 'Fn.const 1' depending
432
   on if we're at the top level or not.
433

434
   Other measurement functions and monoids get you priority queues, search trees
435
   and interval trees.
436
*)
437
let measure : ('e -> int) -> 'e t -> int =
438
 fun measure' t ->
439
  match t with Empty -> 0 | Single a -> measure' a | Deep (m, _, _, _) -> m
×
440

441
(** Smart constructor for deep nodes that tracks measure. *)
442
let deep :
443
       ('e -> int)
444
    -> (_, _, 'e) Digit.t
445
    -> 'e Node.t t
446
    -> (_, _, 'e) Digit.t
447
    -> 'e t =
448
 fun measure' prefix middle suffix ->
449
  Deep
×
450
    ( Digit.measure measure' prefix
×
451
      + measure Node.measure middle
×
452
      + Digit.measure measure' suffix
×
453
    , prefix
454
    , lazy middle
455
    , suffix )
456

457
let empty : 'e t = Empty
458

459
(** Add a new element to the left end of the tree. *)
460
let rec cons' : 'e. ('e -> int) -> 'e -> 'e t -> 'e t =
461
 fun measure' v t ->
462
  match t with
×
463
  | Empty ->
×
464
      Single v
465
  | Single v' ->
×
466
      deep measure' (Digit.One v) Empty (Digit.One v')
467
  | Deep (_, prefix, middle, suffix) ->
×
468
      (* If there is space in the left finger, the finger is the only thing that
469
         needs to change. If not we need to make a recursive call. A recursive
470
         call frees up two finger slots and is needed every third cons
471
         operation, so the amortized cost is constant for a two layer tree.
472
         Because each level triples the number of elements in the fingers, we
473
         free up 2 * 3^level slots per recursive call and need to do so every
474
         2 * 3^level conses. So cons is amortized O(1) for arbitrary depth
475
         trees.
476
      *)
477
      Digit.addable_elim
478
        (fun prefix' ->
479
          let (Mk_any_a prefix'') = Digit.cons v prefix' in
×
480
          deep measure' prefix'' (Lazy.force middle) suffix )
×
481
        (fun (Four (a, b, c, d)) ->
482
          deep measure'
×
483
            (Digit.Two (v, a))
484
            (cons' Node.measure (Node.mk_3 measure' b c d) @@ Lazy.force middle)
×
485
            suffix )
486
        prefix
487

488
let cons : 'e -> 'e t -> 'e t = fun x xs -> cons' (Fn.const 1) x xs
×
489

490
(** Add a new element to the right end of the tree. This is a mirror of cons' *)
491
let rec snoc' : 'e. ('e -> int) -> 'e t -> 'e -> 'e t =
492
 fun measure' t v ->
493
  match t with
×
494
  | Empty ->
×
495
      Single v
496
  | Single v' ->
×
497
      deep measure' (Digit.One v') Empty (Digit.One v)
498
  | Deep (_, prefix, middle, suffix) ->
×
499
      Digit.addable_elim
500
        (fun digit ->
501
          let (Mk_any_a digit') = Digit.snoc digit v in
×
502
          deep measure' prefix (Lazy.force middle) digit' )
×
503
        (fun (Four (a, b, c, d)) ->
504
          deep measure' prefix
×
505
            (snoc' Node.measure (Lazy.force middle) @@ Node.mk_3 measure' a b c)
×
506
            (Digit.Two (d, v)) )
507
        suffix
508

509
let snoc : 'e t -> 'e -> 'e t = fun xs x -> snoc' (Fn.const 1) xs x
×
510

511
(** Create a finger tree from a digit *)
512
let tree_of_digit : ('e -> int) -> ('a, 'r, 'e) Digit.t -> 'e t =
513
 fun measure' dig -> Digit.foldr (cons' measure') Empty dig
×
514

515
(** If the input is non-empty, get the first element and the rest of the
516
    sequence. If it is empty, return None. *)
517
let rec uncons' : 'e. ('e -> int) -> 'e t -> ('e * 'e t) option =
518
 fun measure' t ->
519
  match t with
×
520
  | Empty ->
×
521
      None
522
  | Single e ->
×
523
      Some (e, empty)
524
  | Deep (_m, prefix, middle, suffix) ->
×
525
      Digit.removable_elim
526
        (fun prefix' ->
527
          let head, Mk_any_r prefix_rest = Digit.uncons prefix' in
×
528
          Some (head, deep measure' prefix_rest (force middle) suffix) )
×
529
        (fun (One e) ->
530
          match uncons' Node.measure (force middle) with
×
531
          | None ->
×
532
              Some (e, tree_of_digit measure' suffix)
×
533
          | Some (node, rest) ->
×
534
              Some (e, deep measure' (Node.to_digit node) rest suffix) )
×
535
        prefix
536

537
(** Uncons for the top level trees. *)
538
let uncons : 'e t -> ('e * 'e t) option = fun t -> uncons' (Fn.const 1) t
×
539

540
(** Mirror of uncons' for the last element. *)
541
let rec unsnoc' : 'e. ('e -> int) -> 'e t -> ('e t * 'e) option =
542
 fun measure' t ->
543
  match t with
×
544
  | Empty ->
×
545
      None
546
  | Single e ->
×
547
      Some (empty, e)
548
  | Deep (_m, prefix, middle, suffix) ->
×
549
      Digit.removable_elim
550
        (fun suffix' ->
551
          let Mk_any_r liat, deah = Digit.unsnoc suffix' in
×
552
          Some (deep measure' prefix (force middle) liat, deah) )
×
553
        (fun (One e) ->
554
          match unsnoc' Node.measure (force middle) with
×
555
          | None ->
×
556
              Some (tree_of_digit measure' prefix, e)
×
557
          | Some (rest, node) ->
×
558
              Some (deep measure' prefix rest (Node.to_digit node), e) )
×
559
        suffix
560

561
(** Mirror of uncons. *)
562
let unsnoc : 'e t -> ('e t * 'e) option = fun t -> unsnoc' (Fn.const 1) t
×
563

564
let head_exn : 'e t -> 'e = fun t -> Option.value_exn (uncons t) |> Tuple2.get1
×
565

566
let last_exn : 'e t -> 'e = fun t -> unsnoc t |> Option.value_exn |> Tuple2.get2
×
567

568
let rec foldl : ('a -> 'e -> 'a) -> 'a -> 'e t -> 'a =
569
 fun f acc t ->
570
  match uncons t with
×
571
  | None ->
×
572
      acc
573
  | Some (head, tail) ->
×
574
      foldl f (f acc head) tail
×
575

576
let rec foldr : ('e -> 'a -> 'a) -> 'a -> 'e t -> 'a =
577
 fun f acc t ->
578
  match uncons t with
×
579
  | None ->
×
580
      acc
581
  | Some (head, tail) ->
×
582
      f head (foldr f acc tail)
×
583

584
module C = Container.Make (struct
585
  type nonrec 'a t = 'a t
586

587
  let fold : 'a t -> init:'accum -> f:('accum -> 'a -> 'accum) -> 'accum =
588
   fun t ~init ~f -> foldl f init t
×
589

590
  let iter = `Define_using_fold
591

592
  let length = `Custom (fun t -> measure (Fn.const 1) t)
×
593
end)
594

595
let is_empty = C.is_empty
596

597
let length = C.length
598

599
let iter = C.iter
600

601
let find = C.find
602

603
let findi t ~f =
604
  match
×
605
    C.fold t ~init:(`Not_found 0) ~f:(fun acc x ->
606
        match acc with
×
607
        | `Not_found i ->
×
608
            if f x then `Found i else `Not_found (i + 1)
×
609
        | `Found i ->
×
610
            `Found i )
611
  with
612
  | `Not_found _ ->
×
613
      None
614
  | `Found i ->
×
615
      Some i
616

617
let to_seq : 'e t -> 'e Sequence.t = fun t -> Sequence.unfold ~init:t ~f:uncons
×
618

619
let sexp_of_t : ('e -> Sexp.t) -> 'e t -> Sexp.t =
620
 fun sexp_inner -> Fn.compose (Sequence.sexp_of_t sexp_inner) to_seq
×
621

622
let rec equal : ('e -> 'e -> bool) -> 'e t -> 'e t -> bool =
623
 fun eq_inner xs ys ->
624
  match (uncons xs, uncons ys) with
×
625
  | Some (x, xs_tail), Some (y, ys_tail) ->
×
626
      eq_inner x y && equal eq_inner xs_tail ys_tail
×
627
  | _ ->
×
628
      false
629

630
let to_list : 'e t -> 'e list = fun fseq -> Sequence.to_list (to_seq fseq)
×
631

632
let of_list : 'e list -> 'e t = List.fold_left ~init:empty ~f:snoc
633

634
(* Split a tree into the elements before a given index, the element at that
635
   index and the elements after it. The index must exist in the tree. *)
636
let rec split : 'e. ('e -> int) -> 'e t -> int -> int -> 'e t * 'e * 'e t =
637
 fun measure' t target acc ->
638
  match t with
×
639
  | Empty ->
×
640
      failwith "FSequence.split index out of bounds (1)"
641
  | Single x ->
×
642
      if acc + measure' x >= target then (Empty, x, Empty)
×
643
      else failwith "FSequence.split index out of bounds (2)"
×
644
  | Deep (_m, prefix, middle, suffix) ->
×
645
      let acc_p = acc + Digit.measure measure' prefix in
×
646
      if acc_p >= target then
647
        (* split point is in left finger *)
648
        let dl, m, dr = Digit.split measure' target acc prefix in
×
649
        ( (* left part of digit split *)
×
650
          ( match dl with
651
          | None ->
×
652
              Empty
653
          | Some (Mk_any_ar dig) ->
×
654
              tree_of_digit measure' dig )
×
655
        , (* middle of digit split *) m
656
        , (* right part of digit split + subtree + suffix *)
657
          match dr with
658
          | None -> (
×
659
              match uncons' Node.measure @@ force middle with
×
660
              | None ->
×
661
                  tree_of_digit measure' suffix
×
662
              | Some (head, tail) ->
×
663
                  deep measure' (Node.to_digit head) tail suffix )
×
664
          | Some (Mk_any_ar dig) ->
×
665
              deep measure' dig (force middle) suffix )
×
666
      else
667
        let acc_m = acc_p + measure Node.measure (force middle) in
×
668
        if acc_m >= target then
669
          (* split point is in subtree *)
670
          let lhs, m, rhs = split Node.measure (force middle) target acc_p in
×
671
          (* The subtree is made of nodes, so the midpoint we got from the
672
             recursive call is a node, so split that. *)
673
          let m_lhs, m_m, m_rhs =
×
674
            Node.split_to_digits measure' target
675
              (measure Node.measure lhs + acc_p)
×
676
              m
677
          in
678
          ( (* prefix + lhs of the split of the subtree + lhs of the split of
×
679
               them midpoint of the subtree *)
680
            ( match m_lhs with
681
            | None -> (
×
682
                match unsnoc' Node.measure lhs with
683
                | None ->
×
684
                    tree_of_digit measure' prefix
×
685
                | Some (liat, deah) ->
×
686
                    deep measure' prefix liat (Node.to_digit deah) )
×
687
            | Some (Mk_any_ar dig) ->
×
688
                deep measure' prefix lhs dig )
×
689
          , (* midpoint of the split of the subtree *)
690
            m_m
691
          , (* rhs of the split of the midpoint of the subtree + rhs of the
692
               split of the subtree + suffix *)
693
            match m_rhs with
694
            | None -> (
×
695
                match uncons' Node.measure rhs with
696
                | None ->
×
697
                    tree_of_digit measure' suffix
×
698
                | Some (head, tail) ->
×
699
                    deep measure' (Node.to_digit head) tail suffix )
×
700
            | Some (Mk_any_ar dig) ->
×
701
                deep measure' dig rhs suffix )
×
702
        else
703
          let acc_s = acc_m + Digit.measure measure' suffix in
×
704
          if acc_s >= target then
705
            (* split point is in right finger *)
706
            let dl, m, dr = Digit.split measure' target acc_m suffix in
×
707
            ( (* prefix + subtree + left part of digit split *)
×
708
              ( match dl with
709
              | None -> (
×
710
                  match unsnoc' Node.measure (force middle) with
×
711
                  | None ->
×
712
                      tree_of_digit measure' prefix
×
713
                  | Some (liat, deah) ->
×
714
                      deep measure' prefix liat (Node.to_digit deah) )
×
715
              | Some (Mk_any_ar dig) ->
×
716
                  deep measure' prefix (force middle) dig )
×
717
            , (* midpoint of digit split *)
718
              m
719
            , (* right part of digit split *)
720
              match dr with
721
              | None ->
×
722
                  Empty
723
              | Some (Mk_any_ar dig) ->
×
724
                  tree_of_digit measure' dig )
×
725
          else failwith "FSequence.split index out of bounds (3)"
×
726

727
(* Split a tree into the elements before some index and the elements >= that
728
   index. split_at works when the index is out of range and returns a pair while
729
   split throws an exception if the index is out of range and returns a triple.
730
   The contract is that split_at xs i = take i xs, drop i xs.
731
*)
732
let split_at : 'e t -> int -> 'e t * 'e t =
733
 fun t idx ->
734
  match t with
×
735
  | Empty ->
×
736
      (empty, empty)
737
  | _ ->
×
738
      if idx = 0 then (empty, t)
×
739
      else if measure (Fn.const 1) t >= idx then
×
740
        match split (Fn.const 1) t idx 0 with lhs, m, rhs -> (snoc lhs m, rhs)
×
741
      else (t, empty)
×
742

743
let singleton : 'e -> 'e t = fun v -> Single v
×
744

745
(* Assert that the cached measures match the actual ones. *)
746
let rec assert_measure : type e. (e -> int) -> e t -> unit =
747
 fun measure' -> function
748
  | Empty ->
×
749
      ()
750
  | Single _ ->
×
751
      ()
752
  | Deep (cached_measure, prefix, middle, suffix) ->
×
753
      let measure_node_with_assert node =
754
        let expected = Node.measure node in
×
755
        let contents = node |> Node.to_digit |> Digit.to_list in
×
756
        [%test_eq: int] expected (List.sum (module Int) ~f:measure' contents) ;
×
757
        expected
×
758
      in
759
      let middle' = Lazy.force middle in
760
      assert_measure measure_node_with_assert middle' ;
×
761
      [%test_eq: int] cached_measure
×
762
        ( Digit.measure measure' prefix
×
763
        + measure Node.measure middle'
×
764
        + Digit.measure measure' suffix )
×
765

766
(* Quickcheck.Generator.list generates pretty small lists, which are not big
767
   enough to exercise multiple levels of the tree. So we use this instead. *)
768
let big_list : 'a Quickcheck.Generator.t -> 'a list Quickcheck.Generator.t =
769
 fun gen ->
770
  let open Quickcheck.Generator.Let_syntax in
×
771
  let%bind len = Int.gen_incl 0 1000 in
×
772
  Quickcheck.Generator.list_with_length len gen
×
773

774
let%test_unit "list isomorphism - cons" =
775
  Quickcheck.test (big_list Int.quickcheck_generator) ~f:(fun xs ->
×
776
      let xs_fseq = List.fold_right xs ~f:cons ~init:empty in
×
777
      assert_measure (Fn.const 1) xs_fseq ;
×
778
      [%test_eq: int list] xs (to_list xs_fseq) ;
×
779
      [%test_eq: int] (List.length xs) (length xs_fseq) )
×
780

781
let%test_unit "list isomorphism - snoc" =
782
  Quickcheck.test (big_list Int.quickcheck_generator) ~f:(fun xs ->
×
783
      let xs_fseq = List.fold_left xs ~init:empty ~f:snoc in
×
784
      assert_measure (Fn.const 1) xs_fseq ;
×
785
      [%test_eq: int list] xs (to_list xs_fseq) ;
×
786
      [%test_eq: int] (List.length xs) (length xs_fseq) )
×
787

788
let%test_unit "alternating cons/snoc" =
789
  Quickcheck.test
×
790
    Quickcheck.Generator.(
791
      big_list @@ variant2 (Int.gen_incl 0 500) (Int.gen_incl 0 500))
×
792
    ~f:(fun cmds ->
793
      let rec go list fseq cmds_acc =
×
794
        match cmds_acc with
×
795
        | [] ->
×
796
            assert_measure (Fn.const 1) fseq ;
×
797
            [%test_eq: int list] list (to_list fseq) ;
×
798
            [%test_eq: int] (List.length list) (length fseq)
×
799
        | `A x :: rest ->
×
800
            go (x :: list) (cons x fseq) rest
×
801
        | `B x :: rest ->
×
802
            go (list @ [ x ]) (snoc fseq x) rest
×
803
      in
804
      go [] empty cmds )
805

806
let%test_unit "split properties" =
807
  let gen =
×
808
    let open Quickcheck.Generator.Let_syntax in
809
    let%bind xs = big_list (Int.gen_incl 0 500) in
×
810
    let%bind idx = Int.gen_incl 0 (List.length xs) in
×
811
    return (xs, idx)
×
812
  in
813
  let shrinker =
814
    Quickcheck.Shrinker.create (fun (xs, idx) ->
815
        Sequence.append
×
816
          ( if List.length xs - 1 > idx then
×
817
            Sequence.singleton (List.tl_exn xs, idx)
×
818
          else Sequence.empty )
×
819
          ( Sequence.range ~start:`inclusive ~stop:`inclusive 1 5
×
820
          |> Sequence.filter_map ~f:(fun offset ->
×
821
                 let res = idx - offset in
×
822
                 if res >= 0 then Some (xs, res) else None ) ) )
×
823
  in
824
  Quickcheck.test gen ~shrink_attempts:`Exhaustive
×
825
    ~sexp_of:[%sexp_of: int list * int] ~shrinker ~f:(fun (xs, idx) ->
826
      let len = List.length xs in
×
827
      let split_l_list = List.take xs idx in
×
828
      let split_r_list = List.drop xs idx in
×
829
      let xs_fseq = of_list xs in
×
830
      let split_l_fseq, split_r_fseq = split_at xs_fseq idx in
×
831
      let split_l_fseq', split_r_fseq' =
×
832
        (to_list split_l_fseq, to_list split_r_fseq)
×
833
      in
834
      assert_measure (Fn.const 1) split_l_fseq ;
×
835
      assert_measure (Fn.const 1) split_r_fseq ;
×
836
      [%test_eq: int] (List.length split_l_list + List.length split_r_list) len ;
×
837
      [%test_eq: int list] split_l_list split_l_fseq' ;
×
838
      [%test_eq: int list] split_r_list split_r_fseq' ;
×
839
      [%test_eq: int] (List.length split_l_fseq') (length split_l_fseq) ;
×
840
      [%test_eq: int] (List.length split_r_fseq') (length split_r_fseq) ;
×
841
      [%test_eq: int] (length split_l_fseq + length split_r_fseq) len )
×
842

843
(* Exercise all the functions that generate sequences, in random combinations. *)
844
let%test_module "random sequence generation, with splits" =
845
  ( module struct
846
    type action =
×
847
      [ `Cons of int
848
      | `Snoc of int
849
      | `Split_take_left of int
850
      | `Split_take_right of int ]
851
    [@@deriving sexp_of]
852

853
    let%test_unit _ =
854
      let rec gen_actions xs len n =
×
855
        let open Quickcheck.Generator in
×
856
        let open Quickcheck.Generator.Let_syntax in
857
        if n = 0 then return @@ List.rev xs
×
858
        else
859
          match%bind
860
            variant3 (Int.gen_incl 0 500) (Int.gen_incl 0 500)
×
861
              (Int.gen_uniform_incl 0 len)
×
862
          with
863
          | `A v ->
×
864
              gen_actions (`Cons v :: xs) (len + 1) (n - 1)
865
          | `B v ->
×
866
              gen_actions (`Snoc v :: xs) (len + 1) (n - 1)
867
          | `C idx -> (
×
868
              match%bind bool with
869
              | true ->
×
870
                  gen_actions (`Split_take_left idx :: xs) idx (n - 1)
871
              | false ->
×
872
                  gen_actions (`Split_take_right idx :: xs) (len - idx) (n - 1)
873
              )
874
      in
875
      let gen =
876
        let open Quickcheck.Generator.Let_syntax in
877
        let%bind len = Int.gen_incl 0 50 in
×
878
        gen_actions [] 0 len
×
879
      in
880
      let shrinker =
881
        Quickcheck.Shrinker.create (function
882
          | [] ->
×
883
              Sequence.empty
884
          | _ :: _ as acts ->
×
885
              Sequence.of_list
886
                [ List.take acts (List.length acts / 2)
×
887
                ; List.take acts (List.length acts - 1)
×
888
                ; List.map acts ~f:(function `Snoc x -> `Cons x | x -> x)
×
889
                ; List.map acts ~f:(function `Cons x -> `Snoc x | x -> x)
×
890
                ] )
891
      in
892
      Quickcheck.test gen ~trials:100_000 ~shrinker
×
893
        ~sexp_of:(List.sexp_of_t sexp_of_action) ~f:(fun acts ->
×
894
          let rec go fseq =
×
895
            let assert_m fseq' =
×
896
              assert_measure (Fn.const 1) fseq' ;
×
897
              fseq'
×
898
            in
899
            function
900
            | [] ->
×
901
                ()
902
            | `Cons x :: acts_rest ->
×
903
                go (assert_m @@ cons x fseq) acts_rest
×
904
            | `Snoc x :: acts_rest ->
×
905
                go (assert_m @@ snoc fseq x) acts_rest
×
906
            | `Split_take_left idx :: acts_rest ->
×
907
                go (assert_m @@ Tuple2.get1 @@ split_at fseq idx) acts_rest
×
908
            | `Split_take_right idx :: acts_rest ->
×
909
                go (assert_m @@ Tuple2.get2 @@ split_at fseq idx) acts_rest
×
910
          in
911
          go empty acts )
912
  end )
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc