Skip to content

Commit 9688856

Browse files
committed
style(Trie): (possibly) improve time complexity of union
1 parent 6308c39 commit 9688856

1 file changed

Lines changed: 38 additions & 7 deletions

File tree

src/Trie.ml

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ type ('data, 'tag) node = 'data data_node * 'tag tag_node
2727
2828
Non-invariants:
2929
1. The tag trie need not be minimum.
30-
2. This implementation prefers removing tag_default_child.
30+
2. This module prefers removing tag_default_child.
3131
*)
3232

3333
type ('data, 'tag) t = ('data, 'tag) node option
@@ -53,6 +53,20 @@ let mk_tag_node' d t : _ tag_node = mk_tag_node d (t, (t, SegMap.empty))
5353
let mk_node d tag_params : _ node = d, mk_tag_node d tag_params
5454
let mk_node' d tag : _ node = d, mk_tag_node' d tag
5555

56+
(* invariants: the input is already valid *)
57+
let drop_tag_default_child (d, t) =
58+
if t.tag_default_child = None then (d, t) else
59+
let tag_children =
60+
SegMap.merge
61+
(fun _ child tag_child ->
62+
match child, tag_child with
63+
| None, _ -> assert false
64+
| Some d, None -> Some (mk_tag_node' d t.tag_default_child)
65+
| Some _, Some t -> Some t)
66+
d.children t.tag_children
67+
in
68+
d, { t with tag_default_child = None; tag_children }
69+
5670
(* invariants: input tag tree must be a subset (if default tags were ignored) *)
5771
let mk_tree (root, children) tag_params : _ t =
5872
if Option.is_none root && SegMap.is_empty children
@@ -84,15 +98,15 @@ let get_children_node (d, t) =
8498
SegMap.merge
8599
(fun _ d' t' ->
86100
match d', t' with
87-
| None, _ -> None
101+
| None, _ -> assert false
88102
| Some d, None -> Some (d, mk_tag_node' d t.tag_default_child)
89103
| Some d, Some t -> Some (d, t))
90104
d.children t.tag_children
91105
let get_children_node2 (d, t1, t2) =
92106
SegMap.merge
93107
(fun _ d_t1 t2' ->
94108
match d_t1, t2' with
95-
| None, _ -> None
109+
| None, _ -> assert false
96110
| Some (d, t1), None -> Some (d, t1, mk_tag_node' d t2.tag_default_child)
97111
| Some (d, t1), Some t2 -> Some (d, t1, t2))
98112
(get_children_node (d, t1)) t2.tag_children
@@ -191,15 +205,32 @@ let union_option m r1 r2 =
191205
| Some r, None | None, Some r -> Some r
192206
| Some r1, Some r2 -> Some (m r1 r2)
193207

208+
(* this function is optimized for the cases where the merging is rare *)
194209
let rec union_node ~prefix m n1 n2 =
210+
let (nd1, nt1) as n1 = drop_tag_default_child n1
211+
and (nd2, nt2) as n2 = drop_tag_default_child n2
212+
in
195213
let root, tag_root = split_option @@
196214
union_option (m prefix) (find_root_node n1) (find_root_node n2)
197215
in
198-
let children, tag_children =
199-
split_children @@ SegMap.union
200-
(fun seg n1 n2 -> Some (union_node ~prefix:(prefix #< seg) m n1 n2))
201-
(get_children_node n1) (get_children_node n2)
216+
let tag_exclusive_children =
217+
SegMap.union
218+
(fun _seg _t1 _t2 -> None)
219+
nt1.tag_children nt2.tag_children
220+
in
221+
let tag_overlapping_children = ref SegMap.empty in
222+
let children =
223+
SegMap.union
224+
(fun seg d1 d2 ->
225+
let t1 = SegMap.find seg nt1.tag_children
226+
and t2 = SegMap.find seg nt2.tag_children
227+
in
228+
let d, t = union_node ~prefix:(prefix #< seg) m (d1, t1) (d2, t2) in
229+
tag_overlapping_children := SegMap.add seg t !tag_overlapping_children;
230+
Some d)
231+
nd1.children nd2.children
202232
in
233+
let tag_children = SegMap.union (fun _ _ _ -> assert false) tag_exclusive_children !tag_overlapping_children in
203234
{root; children}, {tag_root; tag_default_child = None; tag_children}
204235

205236
let union_ ~prefix m = union_option (union_node ~prefix m)

0 commit comments

Comments
 (0)