Packages

Data structures in pure Gleam! Including tree, heap, non empty list, map, set, and priority queue.

Current section

Files

Jump to
gleamy_structures src gleamy red_black_tree_set.gleam
Raw

src/gleamy/red_black_tree_set.gleam

//// This module provides an implementation of a red-black tree set, a self-balancing
//// binary search tree data structure that maintains a balanced shape which ensures
//// tree operations stay efficient.
//// This is an ordered set implementation, meaning the tree will contains values that
//// are unique and ordered according to the comparison function.
// Based on "Deletion: The curse of the red-black tree" by Germane (2014)
import gleam/order.{type Order, Eq, Gt, Lt}
type Color {
R
B
BB
}
type Node(a) {
E
EE
T(c: Color, l: Node(a), k: a, r: Node(a))
}
pub opaque type Set(a) {
Set(root: Node(a), compare: fn(a, a) -> Order)
}
/// Creates a new empty set with the provided comparison function.
pub fn new(compare: fn(a, a) -> Order) -> Set(a) {
Set(E, compare)
}
/// Removes all elements from the set, resulting in an empty set.
/// Time complexity: O(1)
pub fn clear(tree: Set(a)) -> Set(a) {
Set(E, tree.compare)
}
// TODO is this O(1) amortised?
/// Inserts a new element into the set, preserving the set property (no duplicates).
/// Time complexity: O(log n)
pub fn insert(tree: Set(a), key: a) -> Set(a) {
Set(blacken(ins(tree.root, key, tree.compare)), tree.compare)
}
// TODO is this O(1) amortised?
/// Removes an element from the set, if it exists.
/// Time complexity: O(log n)
pub fn delete(tree: Set(a), key: a) -> Set(a) {
Set(del(redden(tree.root), key, tree.compare), tree.compare)
}
/// Searches for an element in the set and returns it if found.
/// Time complexity: O(log n)
pub fn find(tree: Set(a), key: a) -> Result(a, Nil) {
do_find(tree.root, key, tree.compare)
}
/// Applies a function to every element in the set, accumulating
/// the results with the provided initial accumulator value.
/// Time complexity: O(n)
pub fn fold(tree: Set(a), acc: b, fun: fn(b, a) -> b) -> b {
do_fold(tree.root, acc, fun)
}
/// Applies a function to every element in set, accumulating the results with
/// the provided initial accumulator value, but in reverse order.
/// Time complexity: O(n)
pub fn foldr(tree: Set(a), acc: b, fun: fn(b, a) -> b) -> b {
do_foldr(tree.root, acc, fun)
}
fn ins(node, x, compare) {
case node {
E -> T(R, E, x, E)
T(c, a, y, b) ->
case compare(x, y) {
Lt -> balance(c, ins(a, x, compare), y, b)
Gt -> balance(c, a, y, ins(b, x, compare))
Eq -> T(c, a, x, b)
}
_ -> node
}
}
fn blacken(node: Node(a)) -> Node(a) {
case node {
T(R, T(R, _, _, _) as l, y, c) -> T(B, l, y, c)
T(R, a, x, T(R, _, _, _) as r) -> T(B, a, x, r)
t -> t
}
}
fn balance(c: Color, l: Node(a), v: a, r: Node(a)) -> Node(a) {
case c, l, v, r {
B, T(R, T(R, a, x, b), y, c), z, d -> T(R, T(B, a, x, b), y, T(B, c, z, d))
B, T(R, a, x, T(R, b, y, c)), z, d -> T(R, T(B, a, x, b), y, T(B, c, z, d))
B, a, x, T(R, T(R, b, y, c), z, d) -> T(R, T(B, a, x, b), y, T(B, c, z, d))
B, a, x, T(R, b, y, T(R, c, z, d)) -> T(R, T(B, a, x, b), y, T(B, c, z, d))
BB, a, x, T(R, T(R, b, y, c), z, d) -> T(B, T(B, a, x, b), y, T(B, c, z, d))
BB, T(R, a, x, T(R, b, y, c)), z, d -> T(B, T(B, a, x, b), y, T(B, c, z, d))
c, a, x, b -> T(c, a, x, b)
}
}
fn redden(node: Node(a)) -> Node(a) {
case node {
T(B, T(B, _, _, _) as l, y, T(B, _, _, _) as r) -> T(R, l, y, r)
t -> t
}
}
fn del(node, x, compare) {
case node {
E -> node
T(R, E, y, E) ->
case compare(x, y) {
Eq -> E
_ -> node
}
T(B, E, y, E) ->
case compare(x, y) {
Eq -> EE
_ -> node
}
T(B, T(R, E, y, E) as l, z, E) ->
case compare(x, z) {
Lt -> T(B, del(l, x, compare), z, E)
Gt -> node
Eq -> T(B, E, y, E)
}
T(c, a, y, b) ->
case compare(x, y) {
Lt -> rotate(c, del(a, x, compare), y, b)
Gt -> rotate(c, a, y, del(b, x, compare))
Eq ->
case min_del(b) {
Min(y1, b1) -> rotate(c, a, y1, b1)
None -> E
}
}
_ -> node
}
}
fn rotate(c: Color, l: Node(a), v: a, r: Node(a)) -> Node(a) {
case c, l, v, r {
R, T(BB, a, x, b), y, T(B, c, z, d) ->
balance(B, T(R, T(B, a, x, b), y, c), z, d)
R, EE, y, T(B, c, z, d) -> balance(B, T(R, E, y, c), z, d)
R, T(B, a, x, b), y, T(BB, c, z, d) ->
balance(B, a, x, T(R, b, y, T(B, c, z, d)))
R, T(B, a, x, b), y, EE -> balance(B, a, x, T(R, b, y, E))
B, T(BB, a, x, b), y, T(B, c, z, d) ->
balance(BB, T(R, T(B, a, x, b), y, c), z, d)
B, EE, y, T(B, c, z, d) -> balance(BB, T(R, E, y, c), z, d)
B, T(B, a, x, b), y, T(BB, c, z, d) ->
balance(BB, a, x, T(R, b, y, T(B, c, z, d)))
B, T(B, a, x, b), y, EE -> balance(BB, a, x, T(R, b, y, E))
B, T(BB, a, w, b), x, T(R, T(B, c, y, d), z, e) ->
T(B, balance(B, T(R, T(B, a, w, b), x, c), y, d), z, e)
B, EE, x, T(R, T(B, c, y, d), z, e) ->
T(B, balance(B, T(R, E, x, c), y, d), z, e)
B, T(R, a, w, T(B, b, x, c)), y, T(BB, d, z, e) ->
T(B, a, w, balance(B, b, x, T(R, c, y, T(B, d, z, e))))
B, T(R, a, w, T(B, b, x, c)), y, EE ->
T(B, a, w, balance(B, b, x, T(R, c, y, E)))
c, a, x, b -> T(c, a, x, b)
}
}
type MinDel(a) {
Min(a, Node(a))
None
}
fn min_del(node) -> MinDel(a) {
case node {
T(R, E, x, E) -> Min(x, E)
T(B, E, x, E) -> Min(x, EE)
T(B, E, x, T(R, E, y, E)) -> Min(x, T(B, E, y, E))
T(c, a, x, b) ->
case min_del(a) {
Min(x1, a1) -> Min(x1, rotate(c, a1, x, b))
None -> None
}
_ -> None
}
}
fn do_find(node, key, compare) {
case node {
T(_, l, k, r) ->
case compare(key, k) {
Lt -> do_find(l, key, compare)
Gt -> do_find(r, key, compare)
Eq -> Ok(k)
}
_ -> Error(Nil)
}
}
fn do_fold(node, acc, fun) {
case node {
T(_, r, v, l) -> {
let acc = do_fold(r, acc, fun)
let acc = fun(acc, v)
let acc = do_fold(l, acc, fun)
acc
}
_ -> acc
}
}
fn do_foldr(node, acc, fun) {
case node {
T(_, r, v, l) -> {
let acc = do_foldr(l, acc, fun)
let acc = fun(acc, v)
let acc = do_foldr(r, acc, fun)
acc
}
_ -> acc
}
}
fn do_indent(acc, i) {
case i {
0 -> acc
i -> do_indent(". " <> acc, i - 1)
}
}