A.RBTree (Aja v0.2.0) View Source

A low-level implementation of a Red-Black Tree, used under the hood in A.RBMap, A.RBSet and A.OrdMap.

Implementation following Chris Okasaki's "Purely Functional Data Structures", with the delete method as described in Deletion: The curse of the red-black tree from German and Might.

It should perform significantly better than built-in :gb_trees and :gb_sets (see benchmarks).

Disclaimer

This module is the low-level implementation behind other data structures, it is NOT meant to be used directly.

If you want something ready to use, you should check A.RBMap and A.RBSet (or maybe A.OrdMap).

Probably the only case you might be interested in A.RBTree itself is if you want to implement your own data structures on the top of it, or out of curiosity.

It implements both a Map API and a Set API, both should not be mixed.

Map API

iex> A.RBTree.map_new([])
:E
iex> map = A.RBTree.map_new([b: "B", c: "C", a: "A"])
{:B, {:R, :E, {:a, "A"}, :E}, {:b, "B"}, {:R, :E, {:c, "C"}, :E}}
iex> A.RBTree.map_fetch(map, :c)
{:ok, "C"}
iex> {:new, _new_map} = A.RBTree.map_insert(map, :bar, "BAR")
{:new, {:B, {:B, {:R, :E, {:a, "A"}, :E}, {:b, "B"}, :E}, {:bar, "BAR"}, {:B, :E, {:c, "C"}, :E}}}
iex> {:ok, "B", _new_map} = A.RBTree.map_pop(map, :b)
{:ok, "B", {:B, {:R, :E, {:a, "A"}, :E}, {:c, "C"}, :E}}
iex> A.RBTree.map_pop(map, :bar)
:error
iex> A.RBTree.map_new([b: "B", x: "X", c: "C", a: "A"]) |> A.RBTree.to_list()
[a: "A", b: "B", c: "C", x: "X"]

Set API

iex> A.RBTree.set_new([])
:E
iex> set = A.RBTree.set_new([2.0, 3, 2, 1, 3, 3])
{:B, {:R, :E, 1, :E}, 2, {:R, :E, 3, :E}}
iex> A.RBTree.set_member?(set, 3)
true
iex> {:new, _new_set} = A.RBTree.set_insert(set, 2.5)
{:new, {:B, {:B, {:R, :E, 1, :E}, 2, :E}, 2.5, {:B, :E, 3, :E}}}
iex> {:ok, _new_set} = A.RBTree.set_delete(set, 2)
{:ok, {:B, {:R, :E, 1, :E}, 3, :E}}
iex> A.RBTree.set_delete(set, 4)
:error
iex> A.RBTree.set_new([9, 8, 8, 7, 4, 1, 1, 2, 3, 3, 3, 9, 5, 6]) |> A.RBTree.to_list()
[1, 2, 3, 4, 5, 6, 7, 8, 9]

For the curious reader: more about deletion

Insertion is easy enough in an immutable Red-Black Tree, deletion however is pretty tricky. Two implementations have been tried:

  1. this approach from Matt Might
  2. Deletion: The curse of the red-black tree from Germane and Might

1. The Haskell implementation used as a reference has a bug and seems not to be respecting the Red-Black invariant, as suggested here.

2. was retained and it was confirmed that the Red-Black invariant was maintained.

Finally, a third approach from Kahr's (example Haskell implementation) seems to be faster and might be tried in future iterations.

Note about numbers

Unlike regular maps, A.RBTrees only uses ordering for key comparisons, meaning integers and floats are indistiguinshable as keys.

iex> %{1 => "一", 2 => "二"} |> Map.fetch(2)
{:ok, "二"}
iex> %{1 => "一", 2 => "二"} |> Map.fetch(2.0)
:error
iex> A.RBTree.map_new(%{1 => "一", 2 => "二"}) |> A.RBTree.map_fetch(2)
{:ok, "二"}
iex> A.RBTree.map_new(%{1 => "一", 2 => "二"}) |> A.RBTree.map_fetch(2.0)
{:ok, "二"}

Erlang's :gb_trees module works the same.

Link to this section Summary

Functions

Checks the red-black invariant is respected

Folds (reduces) the given tree from the left with a function. Requires an accumulator.

Folds (reduces) the given tree from the right with a function. Requires an accumulator.

Returns an iterator looping on a tree from left-to-right.

Finds the value corresponding to the given key if exists.

Inserts the key-value pair in a map tree and returns the updated tree.

Adds many key-values to an existing map tree, and returns both the new tree and the number of new entries created.

Initializes a map tree from an enumerable.

Finds and removes the value corresponding for the given key if exists in a map tree, and returns both that value and the new tree.

Finds and removes the rightmost (largest) key in a map tree.

Finds and removes the leftmost (smallest) key in a map tree.

Finds the leftmost (smallest) element of a tree

Finds the rightmost (largest) element of a tree

Walk a tree using an iterator yielded by iterator/1.

Computes the "length" of the tree by looping and counting each node.

Helper to implement Enumerable.reduce/3 in data structures using the underlying tree.

Finds and removes the given value if exists, and returns the new tree.

Inserts the value in a set tree and returns the updated tree.

Adds many values to an existing set tree, and returns both the new tree and the number of newly inserted values.

Checks the presence of a value in a set.

Initializes a set tree from an enumerable.

Finds and removes the rightmost (largest) element in a set tree.

Finds and removes the leftmost (smallest) element in a set tree.

Returns the tree as a list.

Link to this section Types

Specs

color() :: :R | :B

Specs

elem() :: term()

Specs

iterator(elem) :: [tree(elem)]

Specs

key() :: term()

Specs

tree() :: tree(elem())

Specs

tree(elem) :: :E | {color(), tree(elem), {key(), value()}, tree(elem)}

Specs

value() :: term()

Link to this section Functions

Specs

check_invariant(tree()) :: {:ok, non_neg_integer()} | {:error, String.t()}

Checks the red-black invariant is respected:

Each tree is either red or black. The root is black. This rule is sometimes omitted. Since the root can always be changed from red to black, but not necessarily vice versa, this rule has little effect on analysis. (All leaves (NIL) are black.) If a tree is red, then both its children are black. Every path from a given tree to any of its descendant NIL trees goes through the same number of black trees.

Returns either an {:ok, black_height} tuple if respected and black_height is consistent, or an {:error, reason} tuple if violated.

Examples

iex> A.RBTree.check_invariant(:E)
{:ok, 0}
iex> A.RBTree.check_invariant({:B, :E, {1, nil}, :E})
{:ok, 1}
iex> A.RBTree.check_invariant({:R, :E, {1, nil}, :E})
{:error, "No red root allowed"}
iex> A.RBTree.check_invariant({:B, {:B, :E, {1, nil}, :E}, {2, nil}, :E})
{:error, "Inconsistent black length"}
iex> A.RBTree.check_invariant({:B, {:R, {:R, :E, {1, nil}, :E}, {2, nil}, :E}, {3, nil}, :E})
{:error, "Red tree has red child"}

Specs

empty() :: tree()

Folds (reduces) the given tree from the left with a function. Requires an accumulator.

Examples

iex> A.RBTree.set_new([22, 11, 33]) |> A.RBTree.foldl(0, &+/2)
66
iex> A.RBTree.set_new([22, 11, 33]) |> A.RBTree.foldl([], &([2 * &1 | &2]))
[66, 44, 22]

Folds (reduces) the given tree from the right with a function. Requires an accumulator.

Unlike linked lists, this is as efficient as foldl/3. This can typically save a call to Enum.reverse/1 on the result when building a list.

Examples

iex> A.RBTree.set_new([22, 11, 33]) |> A.RBTree.foldr(0, &+/2)
66
iex> A.RBTree.set_new([22, 11, 33]) |> A.RBTree.foldr([], &([2 * &1 | &2]))
[22, 44, 66]

Specs

iterator(tree(el)) :: iterator(el) when el: elem()
iterator(iterator(el)) :: {el, iterator(el)} | nil when el: elem()

Returns an iterator looping on a tree from left-to-right.

The resulting iterator should be looped over using next/1.

Examples

iex> iterator = A.RBTree.set_new([22, 11]) |> A.RBTree.iterator()
iex> {i1, iterator} = A.RBTree.next(iterator)
iex> {i2, iterator} = A.RBTree.next(iterator)
iex> A.RBTree.next(iterator)
nil
iex> [i1, i2]
[11, 22]

Specs

map_fetch(tree({k, v}), k) :: v when k: key(), v: value()

Finds the value corresponding to the given key if exists.

Examples

iex> tree = A.RBTree.map_new(%{a: "A", b: "B", c: "C"})
iex> A.RBTree.map_fetch(tree, :b)
{:ok, "B"}
iex> A.RBTree.map_fetch(tree, :d)
:error
Link to this function

map_insert(root, key, value)

View Source

Specs

map_insert(tree({k, v}), k, v) :: {:new | {:overwrite, v}, tree({k, v})}
when k: key(), v: value()

Inserts the key-value pair in a map tree and returns the updated tree.

Returns a {:new, new_tree} tuple when the key was newly created. Returns a {{:overwrite, previous_value}, new_tree} tuple when the key was already present with the value previous_value.

Examples

iex> tree = A.RBTree.map_new(%{1 => "A", 3 => "C"})
iex> A.RBTree.map_insert(tree, 2, "B")
{:new, {:B, {:B, :E, {1, "A"}, :E}, {2, "B"}, {:B, :E, {3, "C"}, :E}}}
iex> A.RBTree.map_insert(tree, 3, "C!!!")
{{:overwrite, "C"}, {:B, :E, {1, "A"}, {:R, :E, {3, "C!!!"}, :E}}}
Link to this function

map_insert_many(tree, list)

View Source

Specs

map_insert_many(tree({k, v}), Enumerable.t()) ::
  {non_neg_integer(), tree({k, v})}
when k: key(), v: value()

Adds many key-values to an existing map tree, and returns both the new tree and the number of new entries created.

Returns a {inserted, new_tree} tuple when inserted is the number of newly created entries. Updating existing keys do not count. This is useful to keep track of size changes.

Examples

iex> tree = A.RBTree.map_new(%{1 => "A", 2 => "B"})
iex> A.RBTree.map_insert_many(tree, %{2 => "B", 3 => "C"})
{1, {:B, {:B, :E, {1, "A"}, :E}, {2, "B"}, {:B, :E, {3, "C"}, :E}}}

Specs

map_new(Enumerable.t()) :: tree()

Initializes a map tree from an enumerable.

Examples

iex> A.RBTree.map_new(%{1 => "A", 2 => "B", 3 => "C"})
{:B, {:B, :E, {1, "A"}, :E}, {2, "B"}, {:B, :E, {3, "C"}, :E}}

Specs

map_pop(tree({k, v}), k) :: {:ok, v, tree({k, v})} | :error
when k: key(), v: value()

Finds and removes the value corresponding for the given key if exists in a map tree, and returns both that value and the new tree.

Uses the deletion algorithm as described in Deletion: The curse of the red-black tree.

Examples

iex> tree = A.RBTree.map_new(%{a: "A", b: "B", c: "C"})
iex> {:ok, "B", _new_tree} = A.RBTree.map_pop(tree, :b)
{:ok, "B", {:B, :E, {:a, "A"}, {:R, :E, {:c, "C"}, :E}}}
iex> :error = A.RBTree.map_pop(tree, :d)
:error

Specs

map_pop_max(tree({k, v})) :: {:ok, {k, v}, tree({k, v})} | :error
when k: key(), v: value()

Finds and removes the rightmost (largest) key in a map tree.

Returns both the key-value pair and the new tree.

Examples

iex> tree = A.RBTree.map_new(%{a: "A", b: "B", c: "C"})
iex> {:ok, {:c, "C"}, new_tree} = A.RBTree.map_pop_max(tree)
iex> new_tree
{:B, :E, {:a, "A"}, {:R, :E, {:b, "B"}, :E}}
iex> :error = A.RBTree.map_pop_max(A.RBTree.empty())
:error

Specs

map_pop_min(tree({k, v})) :: {:ok, {k, v}, tree({k, v})} | :error
when k: key(), v: value()

Finds and removes the leftmost (smallest) key in a map tree.

Returns both the key-value pair and the new tree.

Examples

iex> tree = A.RBTree.map_new(%{a: "A", b: "B", c: "C"})
iex> {:ok, {:a, "A"}, new_tree} = A.RBTree.map_pop_min(tree)
iex> new_tree
{:B, {:R, :E, {:b, "B"}, :E}, {:c, "C"}, :E}
iex> :error = A.RBTree.map_pop_min(A.RBTree.empty())
:error

Specs

max(tree(el)) :: {:ok, el} | :error when el: elem()

Finds the leftmost (smallest) element of a tree

Examples

iex> A.RBTree.map_new([b: "B", d: "D", a: "A", c: "C"]) |> A.RBTree.max()
{:ok, {:d, "D"}}
iex> A.RBTree.map_new([]) |> A.RBTree.max()
:error

Specs

min(tree(el)) :: {:ok, el} | :error when el: elem()

Finds the rightmost (largest) element of a tree

Examples

iex> A.RBTree.map_new([b: "B", d: "D", a: "A", c: "C"]) |> A.RBTree.min()
{:ok, {:a, "A"}}
iex> A.RBTree.map_new([]) |> A.RBTree.min()
:error

Walk a tree using an iterator yielded by iterator/1.

Examples

iex> iterator = A.RBTree.set_new([22, 11]) |> A.RBTree.iterator()
iex> {i1, iterator} = A.RBTree.next(iterator)
iex> {i2, iterator} = A.RBTree.next(iterator)
iex> A.RBTree.next(iterator)
nil
iex> [i1, i2]
[11, 22]

Specs

node_count(tree(el)) :: non_neg_integer() when el: elem()

Computes the "length" of the tree by looping and counting each node.

Examples

iex> tree = A.RBTree.set_new([1, 2, 2.0, 3, 3.0, 3])
iex> A.RBTree.node_count(tree)
3
iex> A.RBTree.node_count(A.RBTree.empty())
0

Helper to implement Enumerable.reduce/3 in data structures using the underlying tree.

Specs

set_delete(tree(el), el) :: {:ok, tree(el)} | :error when el: elem()

Finds and removes the given value if exists, and returns the new tree.

Uses the deletion algorithm as described in Deletion: The curse of the red-black tree.

Examples

iex> tree = A.RBTree.set_new([1, 2, 3, 4])
iex> {:ok, _new_tree} = A.RBTree.set_delete(tree, 3)
{:ok, {:B, {:B, :E, 1, :E}, 2, {:B, :E, 4, :E}}}
iex> :error = A.RBTree.set_delete(tree, 0)
:error

Specs

set_insert(tree(el), el) :: {:new | :overwrite, tree(el)} when el: elem()

Inserts the value in a set tree and returns the updated tree.

Returns a {:new, new_tree} tuple when the value was newly inserted. Returns a {:overwrite, new_tree} tuple when a non-striclty equal value was already present.

Because 1.0 and 1 compare as equal values, inserting 1.0 can overwrite 1 and new_tree is going to be different.

Examples

iex> tree = A.RBTree.set_new([1, 3])
iex> A.RBTree.set_insert(tree, 2)
{:new, {:B, {:B, :E, 1, :E}, 2, {:B, :E, 3, :E}}}
iex> A.RBTree.set_insert(tree, 3.0)
{:overwrite, {:B, :E, 1, {:R, :E, 3.0, :E}}}
Link to this function

set_insert_many(tree, list)

View Source

Specs

set_insert_many(tree(el), Enumerable.t()) :: {non_neg_integer(), tree(el)}
when el: elem()

Adds many values to an existing set tree, and returns both the new tree and the number of newly inserted values.

Returns a {inserted, new_tree} tuple when inserted is the number of newly inserted values. Overwriting existing values do not count. This is useful to keep track of size changes.

Examples

iex> tree = A.RBTree.set_new([1, 2])
iex> A.RBTree.set_insert_many(tree, [2, 2.0, 3, 3.0])
{1, {:B, {:B, :E, 1, :E}, 2.0, {:B, :E, 3.0, :E}}}

Specs

set_member?(tree(el), el) :: boolean() when el: elem()

Checks the presence of a value in a set.

Like all A.RBTree functions, uses ==/2 for comparison, not strict equality ===/2.

Examples

iex> tree = A.RBTree.set_new([1, 2, 3])
iex> A.RBTree.set_member?(tree, 2)
true
iex> A.RBTree.set_member?(tree, 4)
false
iex> A.RBTree.set_member?(tree, 2.0)
true

Specs

set_new(Enumerable.t()) :: tree()

Initializes a set tree from an enumerable.

Examples

iex> A.RBTree.set_new([3, 2, 1, 2, 3])
{:B, {:B, :E, 1, :E}, 2, {:B, :E, 3, :E}}

Specs

set_pop_max(tree(el)) :: {:ok, el, tree(el)} | :error when el: elem()

Finds and removes the rightmost (largest) element in a set tree.

Returns both the element and the new tree.

Examples

iex> tree = A.RBTree.set_new([1, 2, 3, 4])
iex> {:ok, 4, new_tree} = A.RBTree.set_pop_max(tree)
iex> new_tree
{:B, {:B, :E, 1, :E}, 2, {:B, :E, 3, :E}}
iex> :error = A.RBTree.set_pop_max(A.RBTree.empty())
:error

Specs

set_pop_min(tree(el)) :: {:ok, el, tree(el)} | :error when el: elem()

Finds and removes the leftmost (smallest) element in a set tree.

Returns both the element and the new tree.

Examples

iex> tree = A.RBTree.set_new([1, 2, 3, 4])
iex> {:ok, 1, new_tree} = A.RBTree.set_pop_min(tree)
iex> new_tree
{:B, {:R, :E, 2, :E}, 3, {:R, :E, 4, :E}}
iex> :error = A.RBTree.set_pop_min(A.RBTree.empty())
:error

Specs

to_list(tree(el)) :: [el] when el: elem()

Returns the tree as a list.

Examples

iex> A.RBTree.set_new([3, 2, 2.0, 3, 3.0, 1, 3]) |> A.RBTree.to_list()
[1, 2.0, 3]
iex> A.RBTree.map_new([b: "B", c: "C", a: "A"]) |> A.RBTree.to_list()
[{:a, "A"}, {:b, "B"}, {:c, "C"}]
iex> A.RBTree.empty() |> A.RBTree.to_list()
[]