package core_kernel

  1. Overview
  2. Docs
Industrial strength alternative to OCaml's standard library

Install

Dune Dependency

Authors

Maintainers

Sources

core_kernel-v0.16.0.tar.gz
sha256=e37370bad978cfb71fdaf2b1a25ab1506b98ef0b91e0dbd189ffd9d853245ce2

doc/src/core_kernel.balanced_reducer/balanced_reducer.ml.html

Source file balanced_reducer.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
open! Base

(* The [data] array is an implicit binary tree with [children_length * 2 - 1] nodes,
   with each node being the sum of the two child nodes and the root node being the 0th
   node.  The leaves of the tree are the last [num_leaves] nodes.

   The children are not necessarily all at the same level of the tree. For instance if
   you have 3 children [| a; b; c |]:

   {v
          o
         / \
        o   c
       / \
      a   b
   v}

   We want this tree to be representated as [| o; o; c; a; b |], i.e. we need to apply
   first a rotation then a translation to convert an index in [| a; b; c |] to a (leaf)
   index in [| o; o; c; a; b |]. *)
type 'a t =
  { data : 'a Option_array.t
  ; num_leaves : int
  ; num_leaves_not_in_bottom_level : int
  ; reduce : 'a -> 'a -> 'a
  ; sexp_of_a : 'a -> Sexp.t
  }

let length t = t.num_leaves

(* {v
     parent:      0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 ...
     left child:  1  3  5  7  9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 ...
     right child: 2  4  6  8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 ... v} *)
let parent_index ~child_index = (child_index - 1) / 2
let left_child_index ~parent_index = (parent_index * 2) + 1
let right_child_index ~left_child_index = left_child_index + 1

(* The first [num_leaves-1] elements are internal nodes of the tree.  The next
   [num_leaves] elements are the leaves. *)
let num_branches t = t.num_leaves - 1
let index_is_leaf t i = i >= num_branches t

(* The tree is complete, but not necessarily perfect, so we perform some rotation of the
   leaves to ensure that our reductions preserve ordering. *)
let leaf_index t i =
  (* The tree layout is level order.  Any leaves in the second to last level need to occur
     in the array before the leaves in the bottom level. *)
  let rotated_index =
    let offset_from_start_of_leaves_in_array = i + t.num_leaves_not_in_bottom_level in
    if offset_from_start_of_leaves_in_array < t.num_leaves
    then offset_from_start_of_leaves_in_array
    else offset_from_start_of_leaves_in_array - t.num_leaves
  in
  (* The leaves occur after the branches in the array. *)
  rotated_index + num_branches t
;;

let get_leaf t i = Option_array.get t.data (leaf_index t i)
let to_list t = List.init (length t) ~f:(fun i -> get_leaf t i)
let sexp_of_t sexp_of_a t = [%sexp (to_list t : a option list)]

let invariant invariant_a t =
  let data = t.data in
  for i = 0 to Option_array.length data - 1 do
    match Option_array.get data i with
    | None -> ()
    | Some a -> invariant_a a
  done;
  for i = 0 to num_branches t - 1 do
    let left = left_child_index ~parent_index:i in
    let right = right_child_index ~left_child_index:left in
    let left_is_none = Option_array.is_none data left in
    let right_is_none = Option_array.is_none data right in
    if Option_array.is_some data i
    then assert (not (left_is_none || right_is_none))
    else
      assert (
        index_is_leaf t left || index_is_leaf t right || left_is_none || right_is_none)
  done
;;

let create_exn ?(sexp_of_a = [%sexp_of: _]) () ~len:num_leaves ~reduce =
  if num_leaves < 1
  then
    raise_s
      [%message "non-positive number of leaves in balanced reducer" (num_leaves : int)];
  let num_branches = num_leaves - 1 in
  let num_leaves_not_in_bottom_level = Int.ceil_pow2 num_leaves - num_leaves in
  let data = Option_array.create ~len:(num_branches + num_leaves) in
  { data; num_leaves; num_leaves_not_in_bottom_level; reduce; sexp_of_a }
;;

let validate_index t i =
  if i < 0
  then
    raise_s
      [%message "attempt to access negative index in balanced reducer" ~index:(i : int)];
  let length = t.num_leaves in
  if i >= length
  then
    raise_s
      [%message
        "attempt to access out of bounds index in balanced reducer"
          ~index:(i : int)
          (length : int)]
;;

let set_exn t i a =
  validate_index t i;
  let data = t.data in
  let i = ref (leaf_index t i) in
  Option_array.set_some data !i a;
  while !i <> 0 do
    let parent = parent_index ~child_index:!i in
    if Option_array.is_none data parent
    then i := 0
    else (
      Option_array.unsafe_set_none data parent;
      i := parent)
  done
;;

let get_exn t i =
  validate_index t i;
  Option_array.get_some_exn t.data (leaf_index t i)
;;

let rec compute_exn t i =
  if Option_array.is_some t.data i
  then Option_array.unsafe_get_some_exn t.data i
  else (
    let left = left_child_index ~parent_index:i in
    let right = right_child_index ~left_child_index:left in
    if left >= Option_array.length t.data
    then (
      (* If we get here, the parent was an unset leaf. *)
      let sexp_of_a = t.sexp_of_a in
      raise_s
        [%message
          "attempt to compute balanced reducer with unset elements"
            ~balanced_reducer:(t : a t)]);
    let a = t.reduce (compute_exn t left) (compute_exn t right) in
    Option_array.unsafe_set_some t.data i a;
    a)
;;

let compute_exn t = compute_exn t 0
OCaml

Innovation. Community. Security.