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

MinaProtocol / mina / 2903

15 Nov 2024 01:59PM UTC coverage: 36.723% (-25.0%) from 61.682%
2903

Pull #16342

buildkite

dkijania
Merge branch 'dkijania/remove_publish_job_from_pr_comp' into dkijania/remove_publish_job_from_pr_dev
Pull Request #16342: [DEV] Publish debians only on nightly and stable

15 of 40 new or added lines in 14 files covered. (37.5%)

15175 existing lines in 340 files now uncovered.

24554 of 66863 relevant lines covered (36.72%)

20704.91 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
4✔
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 ->
UNCOV
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 ->
UNCOV
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 =
UNCOV
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 =
UNCOV
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 ->
UNCOV
104
    match d with
×
UNCOV
105
    | One a ->
×
106
        Mk_any_a (Two (v, a))
UNCOV
107
    | Two (a, b) ->
×
108
        Mk_any_a (Three (v, a, b))
UNCOV
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 ->
UNCOV
114
    match d with
×
UNCOV
115
    | One a ->
×
116
        Mk_any_a (Two (a, v))
UNCOV
117
    | Two (a, b) ->
×
118
        Mk_any_a (Three (a, b, v))
UNCOV
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
UNCOV
124
    | Two (a, b) ->
×
125
        (a, Mk_any_r (One b))
UNCOV
126
    | Three (a, b, c) ->
×
127
        (a, Mk_any_r (Two (b, c)))
UNCOV
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
UNCOV
133
    | Two (a, b) ->
×
134
        (Mk_any_r (One a), b)
UNCOV
135
    | Three (a, b, c) ->
×
136
        (Mk_any_r (Two (a, b)), c)
UNCOV
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 ->
UNCOV
142
    match d with
×
UNCOV
143
    | One a ->
×
144
        f a z
UNCOV
145
    | Two (a, b) ->
×
UNCOV
146
        f a (f b z)
×
UNCOV
147
    | Three (a, b, c) ->
×
UNCOV
148
        f a (f b (f c z))
×
UNCOV
149
    | Four (a, b, c, d) ->
×
UNCOV
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 ->
UNCOV
154
    match d with
×
UNCOV
155
    | One a ->
×
156
        f z a
UNCOV
157
    | Two (a, b) ->
×
UNCOV
158
        f (f z a) b
×
UNCOV
159
    | Three (a, b, c) ->
×
UNCOV
160
        f (f (f z a) b) c
×
UNCOV
161
    | Four (a, b, c, d) ->
×
UNCOV
162
        f (f (f (f z a) b) c) d
×
163

164
  let to_list : type a r. (a, r, 'e) t -> 'e list =
UNCOV
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
4✔
172
        gen_measure
173
    in
UNCOV
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 =
UNCOV
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. *)
UNCOV
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 ->
UNCOV
205
      removable_elim
×
206
        (fun t' ->
UNCOV
207
          let head, Mk_any_r tail = uncons t' in
×
UNCOV
208
          if acc + measure' head >= target then
×
UNCOV
209
            (None, head, Some (Mk_any_r tail))
×
210
          else
UNCOV
211
            match split_addable (acc + measure' head) tail with
×
UNCOV
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
                *)
UNCOV
220
                let cons_res' : (addable, removable, 'e) t =
×
221
                  Obj.magic cons_res
222
                in
223
                (Some (Mk_any_r cons_res'), m, rhs)
UNCOV
224
            | None, m, rhs ->
×
225
                (Some (Mk_any_r (One head)), m, rhs) )
226
        (fun (One a) ->
UNCOV
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' ->
UNCOV
233
        let lhs, m, rhs = split_addable acc t' in
×
UNCOV
234
        (Option.map ~f:broaden_any_r lhs, m, Option.map ~f:broaden_any_r rhs) )
×
235
      (fun t' ->
UNCOV
236
        let head, Mk_any_r tail = uncons t' in
×
UNCOV
237
        if acc + measure' head >= target then (None, head, Some (Mk_any_ar tail))
×
238
        else
UNCOV
239
          let lhs, m, rhs = split_addable (acc + measure' head) tail in
×
UNCOV
240
          match lhs with
×
UNCOV
241
          | None ->
×
UNCOV
242
              (Some (Mk_any_ar (One head)), m, Option.map ~f:broaden_any_r rhs)
×
UNCOV
243
          | Some (Mk_any_r lhs') ->
×
UNCOV
244
              ( Some (broaden_any_a (cons head lhs'))
×
245
              , m
UNCOV
246
              , Option.map ~f:broaden_any_r rhs ) )
×
247
      t
248

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

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

266
  let%test_unit "Digit.split matches list implementation" =
UNCOV
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
UNCOV
275
      let%bind idx = Int.gen_incl 1 (List.length @@ to_list dig) in
×
UNCOV
276
      return (Mk_any_ar dig, idx))
×
277
      ~f:(fun (Mk_any_ar dig, idx) ->
UNCOV
278
        let as_list = to_list dig in
×
UNCOV
279
        let lhs_list = List.take as_list (idx - 1) in
×
UNCOV
280
        let m_list = List.nth_exn as_list (idx - 1) in
×
UNCOV
281
        let rhs_list = List.drop as_list idx in
×
UNCOV
282
        [%test_eq: int list] (lhs_list @ (m_list :: rhs_list)) as_list ;
×
UNCOV
283
        let lhs_fseq, m_fseq, rhs_fseq = split (Fn.const 1) idx 0 dig in
×
UNCOV
284
        let lhs_fseq', rhs_fseq' =
×
UNCOV
285
          (opt_to_list lhs_fseq, opt_to_list rhs_fseq)
×
286
        in
UNCOV
287
        [%test_eq: int list] (lhs_fseq' @ (m_fseq :: rhs_fseq')) (to_list dig) ;
×
UNCOV
288
        [%test_eq: int list] lhs_list lhs_fseq' ;
×
UNCOV
289
        [%test_eq: int] m_list m_fseq ;
×
UNCOV
290
        [%test_eq: int list] rhs_list rhs_fseq' ;
×
UNCOV
291
        [%test_eq: int] (List.length lhs_fseq') (idx - 1) ;
×
UNCOV
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" =
UNCOV
296
    Quickcheck.test
×
UNCOV
297
      (Quickcheck.Generator.tuple2 gen_any_ar (Int.gen_incl 0 200))
×
298
      ~f:(fun (Mk_any_ar dig, acc) ->
UNCOV
299
        let as_list = to_list dig in
×
UNCOV
300
        let lhs, m, rhs = split Fn.id acc acc dig in
×
UNCOV
301
        assert (Option.is_none lhs) ;
×
UNCOV
302
        [%test_eq: int] m (List.hd_exn as_list) ;
×
UNCOV
303
        match rhs with
×
UNCOV
304
        | None ->
×
305
            [%test_eq: int list] [] (List.tl_exn as_list)
×
UNCOV
306
        | Some (Mk_any_ar rhs') ->
×
UNCOV
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 _ =
UNCOV
313
    match split Fn.id 5 0 (Three (0, 2, 4)) with
×
UNCOV
314
    | Some (Mk_any_ar (Two (0, 2))), 4, None ->
×
315
        true
316
    | _ ->
×
317
        false
318

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

326
  let%test _ =
UNCOV
327
    match split Fn.id 7 0 (Four (2, 4, 3, 2)) with
×
UNCOV
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)
UNCOV
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 =
UNCOV
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 =
UNCOV
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 ->
UNCOV
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 ->
UNCOV
449
  Deep
×
UNCOV
450
    ( Digit.measure measure' prefix
×
UNCOV
451
      + measure Node.measure middle
×
UNCOV
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 ->
UNCOV
462
  match t with
×
UNCOV
463
  | Empty ->
×
464
      Single v
UNCOV
465
  | Single v' ->
×
466
      deep measure' (Digit.One v) Empty (Digit.One v')
UNCOV
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' ->
UNCOV
479
          let (Mk_any_a prefix'') = Digit.cons v prefix' in
×
UNCOV
480
          deep measure' prefix'' (Lazy.force middle) suffix )
×
481
        (fun (Four (a, b, c, d)) ->
UNCOV
482
          deep measure'
×
483
            (Digit.Two (v, a))
UNCOV
484
            (cons' Node.measure (Node.mk_3 measure' b c d) @@ Lazy.force middle)
×
485
            suffix )
486
        prefix
487

UNCOV
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 ->
UNCOV
493
  match t with
×
UNCOV
494
  | Empty ->
×
495
      Single v
UNCOV
496
  | Single v' ->
×
497
      deep measure' (Digit.One v') Empty (Digit.One v)
UNCOV
498
  | Deep (_, prefix, middle, suffix) ->
×
499
      Digit.addable_elim
500
        (fun digit ->
UNCOV
501
          let (Mk_any_a digit') = Digit.snoc digit v in
×
UNCOV
502
          deep measure' prefix (Lazy.force middle) digit' )
×
503
        (fun (Four (a, b, c, d)) ->
UNCOV
504
          deep measure' prefix
×
UNCOV
505
            (snoc' Node.measure (Lazy.force middle) @@ Node.mk_3 measure' a b c)
×
506
            (Digit.Two (d, v)) )
507
        suffix
508

UNCOV
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 =
UNCOV
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 ->
UNCOV
519
  match t with
×
UNCOV
520
  | Empty ->
×
521
      None
UNCOV
522
  | Single e ->
×
523
      Some (e, empty)
UNCOV
524
  | Deep (_m, prefix, middle, suffix) ->
×
525
      Digit.removable_elim
526
        (fun prefix' ->
UNCOV
527
          let head, Mk_any_r prefix_rest = Digit.uncons prefix' in
×
UNCOV
528
          Some (head, deep measure' prefix_rest (force middle) suffix) )
×
529
        (fun (One e) ->
UNCOV
530
          match uncons' Node.measure (force middle) with
×
UNCOV
531
          | None ->
×
UNCOV
532
              Some (e, tree_of_digit measure' suffix)
×
UNCOV
533
          | Some (node, rest) ->
×
UNCOV
534
              Some (e, deep measure' (Node.to_digit node) rest suffix) )
×
535
        prefix
536

537
(** Uncons for the top level trees. *)
UNCOV
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 ->
UNCOV
543
  match t with
×
UNCOV
544
  | Empty ->
×
545
      None
UNCOV
546
  | Single e ->
×
547
      Some (empty, e)
UNCOV
548
  | Deep (_m, prefix, middle, suffix) ->
×
549
      Digit.removable_elim
550
        (fun suffix' ->
UNCOV
551
          let Mk_any_r liat, deah = Digit.unsnoc suffix' in
×
UNCOV
552
          Some (deep measure' prefix (force middle) liat, deah) )
×
553
        (fun (One e) ->
UNCOV
554
          match unsnoc' Node.measure (force middle) with
×
UNCOV
555
          | None ->
×
UNCOV
556
              Some (tree_of_digit measure' prefix, e)
×
UNCOV
557
          | Some (rest, node) ->
×
UNCOV
558
              Some (deep measure' prefix rest (Node.to_digit node), e) )
×
559
        suffix
560

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

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

UNCOV
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 ->
UNCOV
570
  match uncons t with
×
UNCOV
571
  | None ->
×
572
      acc
UNCOV
573
  | Some (head, tail) ->
×
UNCOV
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 =
UNCOV
588
   fun t ~init ~f -> foldl f init t
×
589

590
  let iter = `Define_using_fold
591

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

UNCOV
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 =
UNCOV
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

UNCOV
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 ->
UNCOV
638
  match t with
×
639
  | Empty ->
×
640
      failwith "FSequence.split index out of bounds (1)"
UNCOV
641
  | Single x ->
×
UNCOV
642
      if acc + measure' x >= target then (Empty, x, Empty)
×
643
      else failwith "FSequence.split index out of bounds (2)"
×
UNCOV
644
  | Deep (_m, prefix, middle, suffix) ->
×
UNCOV
645
      let acc_p = acc + Digit.measure measure' prefix in
×
646
      if acc_p >= target then
647
        (* split point is in left finger *)
UNCOV
648
        let dl, m, dr = Digit.split measure' target acc prefix in
×
UNCOV
649
        ( (* left part of digit split *)
×
650
          ( match dl with
UNCOV
651
          | None ->
×
652
              Empty
UNCOV
653
          | Some (Mk_any_ar dig) ->
×
UNCOV
654
              tree_of_digit measure' dig )
×
655
        , (* middle of digit split *) m
656
        , (* right part of digit split + subtree + suffix *)
657
          match dr with
UNCOV
658
          | None -> (
×
UNCOV
659
              match uncons' Node.measure @@ force middle with
×
UNCOV
660
              | None ->
×
UNCOV
661
                  tree_of_digit measure' suffix
×
UNCOV
662
              | Some (head, tail) ->
×
UNCOV
663
                  deep measure' (Node.to_digit head) tail suffix )
×
UNCOV
664
          | Some (Mk_any_ar dig) ->
×
UNCOV
665
              deep measure' dig (force middle) suffix )
×
666
      else
UNCOV
667
        let acc_m = acc_p + measure Node.measure (force middle) in
×
668
        if acc_m >= target then
669
          (* split point is in subtree *)
UNCOV
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. *)
UNCOV
673
          let m_lhs, m_m, m_rhs =
×
674
            Node.split_to_digits measure' target
UNCOV
675
              (measure Node.measure lhs + acc_p)
×
676
              m
677
          in
UNCOV
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
UNCOV
681
            | None -> (
×
682
                match unsnoc' Node.measure lhs with
UNCOV
683
                | None ->
×
UNCOV
684
                    tree_of_digit measure' prefix
×
UNCOV
685
                | Some (liat, deah) ->
×
UNCOV
686
                    deep measure' prefix liat (Node.to_digit deah) )
×
UNCOV
687
            | Some (Mk_any_ar dig) ->
×
UNCOV
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
UNCOV
694
            | None -> (
×
695
                match uncons' Node.measure rhs with
UNCOV
696
                | None ->
×
UNCOV
697
                    tree_of_digit measure' suffix
×
UNCOV
698
                | Some (head, tail) ->
×
UNCOV
699
                    deep measure' (Node.to_digit head) tail suffix )
×
UNCOV
700
            | Some (Mk_any_ar dig) ->
×
UNCOV
701
                deep measure' dig rhs suffix )
×
702
        else
UNCOV
703
          let acc_s = acc_m + Digit.measure measure' suffix in
×
704
          if acc_s >= target then
705
            (* split point is in right finger *)
UNCOV
706
            let dl, m, dr = Digit.split measure' target acc_m suffix in
×
UNCOV
707
            ( (* prefix + subtree + left part of digit split *)
×
708
              ( match dl with
UNCOV
709
              | None -> (
×
UNCOV
710
                  match unsnoc' Node.measure (force middle) with
×
UNCOV
711
                  | None ->
×
UNCOV
712
                      tree_of_digit measure' prefix
×
UNCOV
713
                  | Some (liat, deah) ->
×
UNCOV
714
                      deep measure' prefix liat (Node.to_digit deah) )
×
UNCOV
715
              | Some (Mk_any_ar dig) ->
×
UNCOV
716
                  deep measure' prefix (force middle) dig )
×
717
            , (* midpoint of digit split *)
718
              m
719
            , (* right part of digit split *)
720
              match dr with
UNCOV
721
              | None ->
×
722
                  Empty
UNCOV
723
              | Some (Mk_any_ar dig) ->
×
UNCOV
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 ->
UNCOV
734
  match t with
×
UNCOV
735
  | Empty ->
×
736
      (empty, empty)
UNCOV
737
  | _ ->
×
UNCOV
738
      if idx = 0 then (empty, t)
×
UNCOV
739
      else if measure (Fn.const 1) t >= idx then
×
UNCOV
740
        match split (Fn.const 1) t idx 0 with lhs, m, rhs -> (snoc lhs m, rhs)
×
741
      else (t, empty)
×
742

UNCOV
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
UNCOV
748
  | Empty ->
×
749
      ()
UNCOV
750
  | Single _ ->
×
751
      ()
UNCOV
752
  | Deep (cached_measure, prefix, middle, suffix) ->
×
753
      let measure_node_with_assert node =
UNCOV
754
        let expected = Node.measure node in
×
UNCOV
755
        let contents = node |> Node.to_digit |> Digit.to_list in
×
UNCOV
756
        [%test_eq: int] expected (List.sum (module Int) ~f:measure' contents) ;
×
UNCOV
757
        expected
×
758
      in
759
      let middle' = Lazy.force middle in
UNCOV
760
      assert_measure measure_node_with_assert middle' ;
×
UNCOV
761
      [%test_eq: int] cached_measure
×
UNCOV
762
        ( Digit.measure measure' prefix
×
UNCOV
763
        + measure Node.measure middle'
×
UNCOV
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 ->
UNCOV
770
  let open Quickcheck.Generator.Let_syntax in
×
UNCOV
771
  let%bind len = Int.gen_incl 0 1000 in
×
UNCOV
772
  Quickcheck.Generator.list_with_length len gen
×
773

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

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

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

806
let%test_unit "split properties" =
UNCOV
807
  let gen =
×
808
    let open Quickcheck.Generator.Let_syntax in
UNCOV
809
    let%bind xs = big_list (Int.gen_incl 0 500) in
×
UNCOV
810
    let%bind idx = Int.gen_incl 0 (List.length xs) in
×
UNCOV
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
UNCOV
824
  Quickcheck.test gen ~shrink_attempts:`Exhaustive
×
825
    ~sexp_of:[%sexp_of: int list * int] ~shrinker ~f:(fun (xs, idx) ->
UNCOV
826
      let len = List.length xs in
×
UNCOV
827
      let split_l_list = List.take xs idx in
×
UNCOV
828
      let split_r_list = List.drop xs idx in
×
UNCOV
829
      let xs_fseq = of_list xs in
×
UNCOV
830
      let split_l_fseq, split_r_fseq = split_at xs_fseq idx in
×
UNCOV
831
      let split_l_fseq', split_r_fseq' =
×
UNCOV
832
        (to_list split_l_fseq, to_list split_r_fseq)
×
833
      in
UNCOV
834
      assert_measure (Fn.const 1) split_l_fseq ;
×
UNCOV
835
      assert_measure (Fn.const 1) split_r_fseq ;
×
UNCOV
836
      [%test_eq: int] (List.length split_l_list + List.length split_r_list) len ;
×
UNCOV
837
      [%test_eq: int list] split_l_list split_l_fseq' ;
×
UNCOV
838
      [%test_eq: int list] split_r_list split_r_fseq' ;
×
UNCOV
839
      [%test_eq: int] (List.length split_l_fseq') (length split_l_fseq) ;
×
UNCOV
840
      [%test_eq: int] (List.length split_r_fseq') (length split_r_fseq) ;
×
UNCOV
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 _ =
UNCOV
854
      let rec gen_actions xs len n =
×
UNCOV
855
        let open Quickcheck.Generator in
×
856
        let open Quickcheck.Generator.Let_syntax in
UNCOV
857
        if n = 0 then return @@ List.rev xs
×
858
        else
859
          match%bind
UNCOV
860
            variant3 (Int.gen_incl 0 500) (Int.gen_incl 0 500)
×
UNCOV
861
              (Int.gen_uniform_incl 0 len)
×
862
          with
UNCOV
863
          | `A v ->
×
864
              gen_actions (`Cons v :: xs) (len + 1) (n - 1)
UNCOV
865
          | `B v ->
×
866
              gen_actions (`Snoc v :: xs) (len + 1) (n - 1)
UNCOV
867
          | `C idx -> (
×
868
              match%bind bool with
UNCOV
869
              | true ->
×
870
                  gen_actions (`Split_take_left idx :: xs) idx (n - 1)
UNCOV
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
UNCOV
877
        let%bind len = Int.gen_incl 0 50 in
×
UNCOV
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
UNCOV
892
      Quickcheck.test gen ~trials:100_000 ~shrinker
×
UNCOV
893
        ~sexp_of:(List.sexp_of_t sexp_of_action) ~f:(fun acts ->
×
UNCOV
894
          let rec go fseq =
×
UNCOV
895
            let assert_m fseq' =
×
UNCOV
896
              assert_measure (Fn.const 1) fseq' ;
×
UNCOV
897
              fseq'
×
898
            in
899
            function
UNCOV
900
            | [] ->
×
901
                ()
UNCOV
902
            | `Cons x :: acts_rest ->
×
UNCOV
903
                go (assert_m @@ cons x fseq) acts_rest
×
UNCOV
904
            | `Snoc x :: acts_rest ->
×
UNCOV
905
                go (assert_m @@ snoc fseq x) acts_rest
×
UNCOV
906
            | `Split_take_left idx :: acts_rest ->
×
UNCOV
907
                go (assert_m @@ Tuple2.get1 @@ split_at fseq idx) acts_rest
×
UNCOV
908
            | `Split_take_right idx :: acts_rest ->
×
UNCOV
909
                go (assert_m @@ Tuple2.get2 @@ split_at fseq idx) acts_rest
×
910
          in
911
          go empty acts )
912
  end )
4✔
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