Source file path.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
open! Core
open! Import

module Elem = struct
  let keyed ~compare id = stage (fun key -> Keyed.create ~key ~id ~compare)

  type t =
    | Subst_from
    | Subst_into
    | Assoc of Keyed.t
    | Switch of int
  [@@deriving sexp_of, compare]

  let to_string =
    let offset = Char.to_int 'a' in
    let lower_nibble_to_alpha c = Int.bit_and c 0b1111 + offset |> Char.of_int_exn in
    let char_to_alpha buf c =
      let c = Char.to_int c in
      let lower = lower_nibble_to_alpha c in
      let upper = lower_nibble_to_alpha (Int.shift_right c 4) in
      Buffer.add_char buf upper;
      Buffer.add_char buf lower
    in
    let keyed_to_string k =
      let buf = Buffer.create 10 in
      Sexp.to_buffer_gen
        (Keyed.sexp_of_t k)
        ~buf
        ~add_char:char_to_alpha
        ~add_string:(fun buf string -> String.iter string ~f:(char_to_alpha buf));
      Buffer.contents buf
    in
    let int_to_string i =
      let buf = Buffer.create 4 in
      String.iter (Int.to_string i) ~f:(char_to_alpha buf);
      Buffer.contents buf
    in
    function
    | Subst_from -> "x"
    | Subst_into -> "y"
    | Assoc k -> keyed_to_string k
    | Switch i -> int_to_string i
  ;;
end

module Run_length_encoding = struct
  type run =
    { element : Elem.t
    ; count : int
    }
  [@@deriving sexp_of]

  let sexp_of_run { element; count } =
    if count = 1
    then [%sexp_of: Elem.t] element
    else [%sexp ((element, count) : Elem.t * int)]
  ;;

  type t = run list [@@deriving sexp_of]

  (* We can't just use the [@@deriving compare] on [t] because of the custom logic when
     the two runs at the front of the encoding are the same. *)
  let rec compare a b =
    match a, b with
    | [], [] -> 0
    | [], _ -> -1
    | _, [] -> 1
    | a :: al, b :: bl ->
      let c = [%compare: Elem.t] a.element b.element in
      if c = 0
      then (
        (* If the RLE is done correctly, these recursive calls will not recurse again
           (otherwise, we must have two of the same [Elem.t]s back-to-back). *)
        match Int.sign (a.count - b.count) with
        | Zero -> compare al bl
        | Pos -> compare ({ a with count = a.count - b.count } :: al) bl
        | Neg -> compare al ({ b with count = b.count - a.count } :: bl))
      else c
  ;;

  let of_elem_list (l : Elem.t list) =
    let rec helper ~(acc : run Reversed_list.t) (l : Elem.t list) : run Reversed_list.t =
      match acc, l with
      | [], first :: tl -> helper ~acc:Reversed_list.[ { element = first; count = 1 } ] tl
      | acc, [] -> acc
      | curr_acc :: acc_tl, curr_element :: tl ->
        (match [%compare.equal: Elem.t] curr_acc.element curr_element with
         | true ->
           let acc =
             Reversed_list.({ curr_acc with count = succ curr_acc.count } :: acc_tl)
           in
           helper ~acc tl
         | false ->
           let acc =
             Reversed_list.({ element = curr_element; count = 1 } :: curr_acc :: acc_tl)
           in
           helper ~acc tl)
    in
    helper ~acc:Reversed_list.[] l |> Reversed_list.rev
  ;;
end

module A = struct
  type 'a t =
    | Stringified of string
    | Parts of
        { parent : 'a
        ; ele : Elem.t
        }
end

type t =
  { items_rev : Elem.t Reversed_list.t
  ; mutable items_for_testing : Elem.t list Uopt.t
  ; mutable string_repr : t A.t
  ; mutable run_length_encoded_items : Run_length_encoding.t Uopt.t
  }

let run_length_encoding t =
  match Uopt.to_option t.run_length_encoded_items with
  | Some items -> items
  | None ->
    let run_length_encoded_items =
      Run_length_encoding.of_elem_list (Reversed_list.rev t.items_rev)
    in
    t.run_length_encoded_items <- Uopt.some run_length_encoded_items;
    run_length_encoded_items
;;

let sexp_of_t t = [%sexp_of: Run_length_encoding.t] (run_length_encoding t)

let compare a b =
  if phys_equal a b
  then 0
  else [%compare: Run_length_encoding.t] (run_length_encoding a) (run_length_encoding b)
;;

let empty =
  { items_for_testing = Uopt.some []
  ; items_rev = []
  ; string_repr = Stringified "bonsai_path"
  ; run_length_encoded_items = Uopt.some []
  }
;;

let append t ele =
  let items_rev = Reversed_list.(ele :: t.items_rev) in
  let items_for_testing = Uopt.none in
  let string_repr = A.Parts { parent = t; ele } in
  let run_length_encoded_items = Uopt.none in
  { items_rev; items_for_testing; string_repr; run_length_encoded_items }
;;

include Comparable.Make_plain (struct
  type nonrec t = t [@@deriving compare, sexp_of]
end)

let rec to_unique_identifier_string t =
  match t.string_repr with
  | Stringified s -> s
  | Parts { ele; parent } ->
    let parent_s = to_unique_identifier_string parent in
    let string_repr = parent_s ^ "_" ^ Elem.to_string ele in
    t.string_repr <- Stringified string_repr;
    string_repr
;;

let raise_duplicate path =
  raise_s
    [%message
      "BUG: [Bonsai.Path.t] should be unique for all components, but duplicate paths \
       were discovered."
        (path : t)]
;;

module For_testing = struct
  let items t =
    match Uopt.to_option t.items_for_testing with
    | Some items -> items
    | None ->
      let items = Reversed_list.rev t.items_rev in
      t.items_for_testing <- Uopt.some items;
      items
  ;;

  let slow_but_correct_compare_for_bisimulation a b =
    if phys_equal a b then 0 else [%compare: Elem.t list] (items a) (items b)
  ;;
end