aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Utilities.ml
blob: d6b9446b61fefe1d5b99cfbe8f7bc1d312067d4e (plain) (blame)
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
module Ordering = struct
  type t = Equal | Smaller | Greater

  let of_int x = if x < 0 then Smaller else if x > 0 then Greater else Equal
end

module Option = struct
  let ( let* ) = Option.bind
  let ( |>> ) = Option.bind
  let sequence = function None -> Ok None | Some x -> Result.map Option.some x
end

module Result = struct
  let ( let* ) = Result.bind
  let ( |>> ) = Result.bind
end

module type SEQUENCE = sig
  type 'a t

  val empty : 'a t
  val cons : 'a -> 'a t -> 'a t
  val fold_right : ('a -> 'b -> 'b) -> 'a t -> 'b -> 'b
  val fold_left : ('b -> 'a -> 'b) -> 'b -> 'a t -> 'b
end

module Generic (S : SEQUENCE) = struct
  let sequence ms =
    let open Result in
    S.fold_right
      (fun m acc ->
        let* acc = acc in
        let* m = m in
        Ok (S.cons m acc) )
      ms (Ok S.empty)

  let foldm_left f init ms =
    let open Result in
    S.fold_left
      (fun acc m ->
        let* acc = acc in
        f acc m )
      (Ok init) ms
end

module List = struct
  module ListSequence : SEQUENCE with type 'a t = 'a list = struct
    type 'a t = 'a list

    let empty = []
    let cons x xs = x :: xs
    let fold_right = List.fold_right
    let fold_left = List.fold_left
  end

  include Generic (ListSequence)

  let concat_mapm f xs = List.map f xs |> sequence |> Stdlib.Result.map List.concat
  let hd_opt = function x :: _ -> Some x | [] -> None
end

module FingerTree = struct
  module FingerTreeSequence : SEQUENCE with type 'a t = 'a BatFingerTree.t = struct
    type 'a t = 'a BatFingerTree.t

    let empty = BatFingerTree.empty
    let cons x xs = BatFingerTree.cons xs x
    let fold_right f xs acc = BatFingerTree.fold_right (Fun.flip f) acc xs
    let fold_left = BatFingerTree.fold_left
  end

  include Generic (FingerTreeSequence)

  let map2 f xs ys =
    let rec frob xs ys acc =
      match BatFingerTree.front xs with
      | None -> acc
      | Some (xs', x) -> (
        match BatFingerTree.front ys with
        | None -> acc
        | Some (ys', y) -> f x y |> BatFingerTree.snoc acc |> frob xs' ys' )
    in
    frob xs ys BatFingerTree.empty

  let zip xs ys = map2 (fun x y -> x, y) xs ys

  let join ss sep =
    BatFingerTree.print ~first:"" ~last:"" ~sep BatIO.nwrite |> Fun.flip BatIO.to_string ss

  let filter_map f =
    BatFingerTree.fold_left
      (fun acc elem ->
        match f elem with None -> acc | Some result -> BatFingerTree.snoc acc result)
      BatFingerTree.empty

  let concat xss = BatFingerTree.fold_left BatFingerTree.append BatFingerTree.empty xss

  let concat_mapm f xs = BatFingerTree.map f xs |> sequence |> Stdlib.Result.map concat

  let rec sort (cmp : 'a -> 'a -> int) (input : 'a BatFingerTree.t) =
    let open BatFingerTree in
    let merge cmp l1 l2 =
      let rec go acc l1 l2 =
        match (front l1, front l2) with
        | None, _ -> append acc l2
        | _, None -> append acc l1
        | Some (t1, h1), Some (t2, h2) ->
           if cmp h1 h2 <= 0 then go (snoc acc h1) t1 l2
           else go (snoc acc h1) l1 t2
      in
      go empty l1 l2
    in
    match size input with
    | 0 | 1 -> input
    | n ->
       let left, right = split_at input (n / 2) in
       merge cmp (sort cmp left) (sort cmp right)

  let group (cmp : 'a -> 'a -> int) (ft : 'a BatFingerTree.t) : 'a BatFingerTree.t BatFingerTree.t =
    let open BatFingerTree in
    let sorted = sort cmp ft in
    let fold first rest =
      fold_left
        (fun (acc, agr, last) elem ->
          if cmp last elem = 0 then (acc, snoc agr elem, elem)
          else (snoc acc agr, singleton elem, elem))
        (empty, singleton first, first)
        rest
    in
    match front sorted with
    | None -> empty
    | Some (tl, hd) ->
       let groups, lastgr, _ = fold hd tl in
       snoc groups lastgr
end

module Atomic = struct
  let rec swap atom f =
    let v = Atomic.get atom in
    let v' = f v in
    if Atomic.compare_and_set atom v v' then atom else swap atom f

  let rec mswap atom f =
    let open Result in
    let v = Atomic.get atom in
    let* v' = f v in
    if Atomic.compare_and_set atom v v' then Ok atom else mswap atom f
end

module Fun = struct
  let ( |.| ) = Fun.compose
end