Current section
Files
Jump to
Current section
Files
lib/rb_tree/map_curse.ex
defmodule A.RBTree.Map.CurseDeletion do
@moduledoc false
# Deletion algorithm as described in
# [Deletion: The curse of the red-black tree](http://matt.might.net/papers/germane2014deletion.pdf)
# It involves temporary trees with one more color: double-black (both nodes and leafs).
# Those should disappear once they have been rebalanced thoug to become regular red-black trees.
@typedoc """
:R -> red
:B -> black
:BB -> double black (temporary)
"""
@type tmp_color :: :R | :B | :BB
# empty | double black empty | tree
@type tmp_tree(key, value) ::
:E | :EE | {tmp_color, tmp_tree(key, value), key, value, tmp_tree(key, value)}
@type key :: term
@type value :: term
@type tmp_tree :: tmp_tree(key, value)
# Use macros rather than tuples to detect errors. No runtime overhead.
defmacrop t(color, left, key, value, right) do
quote do
{unquote(color), unquote(left), unquote(key), unquote(value), unquote(right)}
end
end
defmacrop r(left, key, value, right) do
quote do
{:R, unquote(left), unquote(key), unquote(value), unquote(right)}
end
end
defmacrop b(left, key, value, right) do
quote do
{:B, unquote(left), unquote(key), unquote(value), unquote(right)}
end
end
defmacrop bb(left, key, value, right) do
quote do
{:BB, unquote(left), unquote(key), unquote(value), unquote(right)}
end
end
@spec pop(A.RBTree.Map.tree(k, v), k) :: {v, A.RBTree.Map.tree(k, v)} | :error
when k: key, v: value
def pop(root, key) do
case root |> redden() |> do_pop(key) do
:error -> :error
{value, new_root} -> {value, make_black(new_root)}
end
end
defp do_pop(tree, x) do
case tree do
# IMPORTANT: use `==`, not `===` (ordering)
r(:E, yk, yv, :E) when x == yk ->
{yv, :E}
b(:E, yk, yv, :E) when x == yk ->
{yv, :EE}
t(_color, :E, _yk, _yv, :E) ->
:error
b(r(:E, yk, yv, :E), zk, zv, :E) ->
cond do
x < zk ->
case do_pop(r(:E, yk, yv, :E), x) do
{value, tree} -> {value, b(tree, zk, zv, :E)}
:error -> :error
end
x > zk ->
:error
true ->
{zv, b(:E, yk, yv, :E)}
end
t(color, a, yk, yv, b) ->
cond do
x < yk ->
case do_pop(a, x) do
{value, tree} -> {value, rotate(t(color, tree, yk, yv, b))}
:error -> :error
end
x > yk ->
case do_pop(b, x) do
{value, tree} -> {value, rotate(t(color, a, yk, yv, tree))}
:error -> :error
end
true ->
{yk2, yv2, b2} = min_del(b)
new_tree = rotate(t(color, a, yk2, yv2, b2))
{yv, new_tree}
end
:E ->
:error
end
end
# Private functions
@spec redden(tmp_tree(k, v)) :: tmp_tree(k, v) when k: key, v: value
defp redden(b(b(_, _, _, _) = a, xk, xv, b(_, _, _, _) = b)),
do: r(a, xk, xv, b)
defp redden(tree), do: tree
@spec make_black(tmp_tree(k, v)) :: tmp_tree(k, v) when k: key, v: value
defp make_black(t(_color, l, xk, xv, r)), do: b(l, xk, xv, r)
defp make_black(_empty), do: :E
# probably less optimized but not sure about bubble
@spec balance(tmp_tree(k, v)) :: tmp_tree(k, v) when k: key, v: value
defp balance(tree) do
case tree do
# original cases
b(r(r(a, xk, xv, b), yk, yv, c), zk, zv, d) ->
r(b(a, xk, xv, b), yk, yv, b(c, zk, zv, d))
b(r(a, xk, xv, r(b, yk, yv, c)), zk, zv, d) ->
r(b(a, xk, xv, b), yk, yv, b(c, zk, zv, d))
b(a, xk, xv, r(r(b, yk, yv, c), zk, zv, d)) ->
r(b(a, xk, xv, b), yk, yv, b(c, zk, zv, d))
b(a, xk, xv, r(b, yk, yv, r(c, zk, zv, d))) ->
r(b(a, xk, xv, b), yk, yv, b(c, zk, zv, d))
# extra deletion cases
bb(r(a, xk, xv, r(b, yk, yv, c)), zk, zv, d) ->
b(b(a, xk, xv, b), yk, yv, b(c, zk, zv, d))
bb(a, xk, xv, r(r(b, yk, yv, c), zk, zv, d)) ->
b(b(a, xk, xv, b), yk, yv, b(c, zk, zv, d))
# default
balanced ->
balanced
end
end
@spec rotate(tmp_tree(k, v)) :: tmp_tree(k, v) when k: key, v: value
defp rotate(tree) do
case tree do
# rotate R (BB a x b) y (B c z d) = balance B (R (B a x b) y c) z d
r(bb(a, xk, xv, b), yk, yv, b(c, zk, zv, d)) ->
balance(b(r(b(a, xk, xv, b), yk, yv, c), zk, zv, d))
# rotate R EE y (B c z d) = balance B (R E y c) z d
r(:EE, yk, yv, b(c, zk, zv, d)) ->
balance(b(r(:E, yk, yv, c), zk, zv, d))
# rotate R (B a x b) y (BB c z d) = balance B a x (R b y (B c z d))
r(b(a, xk, xv, b), yk, yv, bb(c, zk, zv, d)) ->
balance(b(a, xk, xv, r(b, yk, yv, b(c, zk, zv, d))))
# rotate R (B a x b) y EE = balance B a x (R b y E)
r(b(a, xk, xv, b), yk, yv, :EE) ->
balance(b(a, xk, xv, r(b, yk, yv, :E)))
# rotate B (BB a x b) y (B c z d) = balance BB (R (B a x b) y c) z d
b(bb(a, xk, xv, b), yk, yv, b(c, zk, zv, d)) ->
balance(bb(r(b(a, xk, xv, b), yk, yv, c), zk, zv, d))
# rotate B EE y (B c z d) = balance BB (R E y c) z d
b(:EE, yk, yv, b(c, zk, zv, d)) ->
balance(bb(r(:E, yk, yv, c), zk, zv, d))
# rotate B (B a x b) y (BB c z d) = balance BB a x (R b y (B c z d))
b(b(a, xk, xv, b), yk, yv, bb(c, zk, zv, d)) ->
balance(bb(a, xk, xv, r(b, yk, yv, b(c, zk, zv, d))))
# rotate B (B a x b) y EE = balance BB a x (R b y E)
b(b(a, xk, xv, b), yk, yv, :EE) ->
balance(bb(a, xk, xv, r(b, yk, yv, :E)))
# rotate B (BB a w b) x (R (B c y d) z e) = B (balance B (R (B a w b) x c) y d) z e
b(bb(a, wk, wv, b), xk, xv, r(b(c, yk, yv, d), zk, zv, e)) ->
b(balance(b(r(b(a, wk, wv, b), xk, xv, c), yk, yv, d)), zk, zv, e)
# rotate B EE x (R (B c y d) z e) = B (balance B (R E x c) y d) z e
b(:EE, xk, xv, r(b(c, yk, yv, d), zk, zv, e)) ->
b(balance(b(r(:E, xk, xv, c), yk, yv, d)), zk, zv, e)
# rotate B (R a w (B b x c)) y (BB d z e) = B a w (balance B b x (R c y (B d z e)))
b(r(a, wk, wv, b(b, xk, xv, c)), yk, yv, bb(d, zk, zv, e)) ->
b(a, wk, wv, balance(b(b, xk, xv, r(c, yk, yv, b(d, zk, zv, e)))))
# rotate B (R a w (B b x c)) y EE = B a w (balance B b x (R c y E))
b(r(a, wk, wv, b(b, xk, xv, c)), yk, yv, :EE) ->
b(a, wk, wv, balance(b(b, xk, xv, r(c, yk, yv, :E))))
# rotate color a x b = T color a x b
_ ->
tree
end
end
defp min_del(r(:E, xk, xv, :E)), do: {xk, xv, :E}
defp min_del(b(:E, xk, xv, :E)), do: {xk, xv, :EE}
defp min_del(b(:E, xk, xv, r(:E, yk, yv, :E))), do: {xk, xv, b(:E, yk, yv, :E)}
defp min_del(t(color, a, xk, xv, b)) do
{xk2, xv2, a2} = min_del(a)
{xk2, xv2, rotate(t(color, a2, xk, xv, b))}
end
end