#include "rbtree.h"
#include "assert.h"
#include "string.h"
#define BLACK 0
#define RED 1
#define LEFT 0
#define left children[LEFT]
#define RIGHT 1
#define right children[RIGHT]
#define CHILD_DIR(c) (c->parent->children[LEFT] == c ? LEFT : RIGHT)
/**Given a root and a direction, rotate the subtree such that the order of the
* elements are unchanged.
* The opposite direction child of the given root is treated as the pivot point
* The dir-most child of the pivot becomes the opposite dir child of the root.
* The pivot becomes the root and vice-versa.
* @param tree to do rotation on.
* @param root of rotation.
* @param dir to rotate by.
* @return new root node.*/
rbnode_t*
s_node_rotate(rbtree_t *tree, rbnode_t *root, bool dir)
{
assert(root != NULL);
rbnode_t *gparent = root->parent;
rbnode_t *sibling = root->children[1-dir];
rbnode_t *child = NULL;
assert(sibling != NULL);
child = sibling->children[dir];
/* Opposite child of root is dir-most child of sibling.*/
root->children[1-dir] = child;
if(child != NULL) child->parent = root;
/* Child of sibling is the root.*/
sibling->children[ dir] = root;
root->parent = sibling;
/* Parent of sibling is the grandparent. */
sibling->parent = gparent;
if(gparent != NULL)
gparent->children[CHILD_DIR(root)] = sibling;
else
tree->root = sibling;
return sibling;
}
/**Search the rbtree for a node that matches the key.
* If no such node is found, returns NULL.
* @param tree to search through.
* @param key to search for.
* @return found node.*/
rbnode_t*
s_find(rbtree_t *tree, intmax_t key)
{
rbnode_t *node = tree->root;
while(node != NULL && node->key != key) {
node = node->children[node->key > key];
}
return node;
}
rbnode_t*
s_closest(rbtree_t *tree, intmax_t key)
{
rbnode_t *node = tree->root;
while(node != NULL && node->key != key) {
rbnode_t *child = node->children[node->key > key];
if(child == NULL) return node;
node = child;
}
return node;
}
/**Find the left-most node for this subtree.
* @param node root of the subtree to search through.
* @return left-most node.*/
rbnode_t*
s_node_min(rbnode_t *node)
{
while(node->left != NULL) node = node->left;
return node;
}
/**Find the successor for the specified node.
* The successor is defined as the left-most node of the right sub-tree.
* (the smallest node larger than the root)
* @param node to search for.
* @return the successor.*/
rbnode_t*
s_node_successor(rbnode_t *node)
{
if(node->right != NULL)
return s_node_min(node->right);
/* If the node has no left child (meaning that the node is larger than it's
* parents), we cycle up through the RHS of the parent tree (such that each
* parent is smaller than the child) until we reach a point by which the
* child node is smaller than the parent.*/
rbnode_t *parent = node->parent;
while(parent != NULL && node == parent->right) {
node = parent;
parent = node->parent;
}
return parent;
}
/**Copy the data from one node onto another, keeping parent and child relations.
* @prarm tree for metadata purposes.
* @param dest destination node.
* @param src source node.*/
void
s_node_copy_value(rbtree_t *tree, rbnode_t *dest, rbnode_t *src)
{
dest->key = src->key;
memcpy(dest->data, src->data, tree->objw);
}
/**Perform tree adjustments after insertion to rebalance the rbtree.
* @param tree to rebalance.
* @param node that was inserted.
* @param parent of the node.
* @param dir that the node was inserted in.*/
void
s_node_insert_fix(
rbtree_t *tree,
rbnode_t *node,
rbnode_t *parent,
bool dir)
{
rbnode_t *gparent;
rbnode_t *uncle;
while((parent = node->parent) != NULL) {
if(parent->color == BLACK) return; /* Tree is valid. no fixes needed.*/
/*Parent is known to be red.*/
if((gparent = parent->parent) == NULL) {
/*Parent is red and the root. We can just fix it and return.*/
parent->color = BLACK;
return;
}
/* Parent is red and has a parent.
* We now need to fix the parent.*/
dir = CHILD_DIR(parent);
uncle = gparent->children[1-dir];
if(uncle == NULL || uncle->color == BLACK) {
/* Parent is red, but uncle (opposite child of gparent) is black.
* We need to rotate the gparent node such that the parent takes
* its place. However, if the current node is an inner child of
* gparent, we need to rotate N into P.*/
if(node == parent->children[1-dir]) {
/* Node is an inner child. We need to rotate N and P first.*/
s_node_rotate(tree, parent, dir);
node = parent;
parent = gparent->children[dir];
}
/* N is not an inner child, we can rotate the tree properly.*/
s_node_rotate(tree, gparent, 1-dir);
parent->color = BLACK;
gparent->color = RED;
return;
}
}
}
/**Insert a node into the rbtree on the parent and direction given.
* @param tree to insert into.
* @param node to insert.
* @param parent node to insert onto.
* @param dir direction to insert node.*/
void
s_node_insert(rbtree_t *tree, rbnode_t *node, rbnode_t *parent, bool dir)
{
node->color = RED;
node->left = NULL;
node->right = NULL;
node->parent = parent;
if(parent == NULL) {
tree->root = node;
return;
}
parent->children[dir] = node;
s_node_insert_fix(tree, node, parent, dir);
}
void
s_node_remove_fixup(rbtree_t *tree, rbnode_t *node)
{
bool dir;
rbnode_t *parent = node->parent;
rbnode_t *sibling;
rbnode_t *cnephew;
rbnode_t *dnephew;
dir = CHILD_DIR(node);
parent->children[dir] = NULL;
while((parent = node->parent) != NULL)
{
dir = CHILD_DIR(node);
sibling = parent->children[1-dir];
cnephew = parent->children[ dir];
dnephew = parent->children[1-dir];
if(sibling->color == RED)
goto case3;
if(dnephew != NULL && dnephew->color == RED)
goto case6;
if(cnephew != NULL && cnephew->color == RED)
goto case5;
if(parent->color == RED)
goto case4;
/* The parent, both nephews, and sibling are black. We need to paint
* the sibling black and move up a level.*/
sibling->color = RED;
node = parent;
}
return;
case3:
/* The sibling is red, which means both nephews and the parent are black.
* We rotate the subtree so that the parent moves to become the siblings
* left child. The tree is then repainted.*/
s_node_rotate(tree, parent, dir);
parent->color = RED;
sibling->color = BLACK;
/* Now we are working on the sibling subtree.*/
sibling = cnephew;
dnephew = sibling->children[1-dir];
if(dnephew != NULL && dnephew->color == BLACK)
goto case6;
cnephew = sibling->children[dir];
if(cnephew != NULL && cnephew->color == RED)
goto case5;
case4:
sibling->color = RED;
parent->color = BLACK;
return;
case5:
s_node_rotate(tree, sibling, 1-dir);
sibling->color = RED;
cnephew->color = BLACK;
dnephew = sibling;
sibling = cnephew;
case6:
s_node_rotate(tree, parent, dir);
sibling->color = parent->color;
parent->color = BLACK;
dnephew->color = BLACK;
}
void
s_node_remove(rbtree_t *tree, rbnode_t *node)
{
rbnode_t *parent = node->parent;
if(node->left != NULL && node->right != NULL) {
/* Node is a full tree. We can replace the node with it's successor to
* keep the balance.
* Needs to recurse if the successor has a right-most child, which is
* dealt with in the next case.*/
rbnode_t *successor = s_node_successor(node);
s_node_copy_value(tree, node, successor);
s_node_remove(tree, successor);
return;
} else if(node->left == NULL && node->right == NULL) {
if(parent == NULL) {
tree->root = NULL;
kfree(node);
return;
}
if(node->color == RED) {
parent->children[CHILD_DIR(node)] = NULL;
kfree(node);
return;
}
s_node_remove_fixup(tree, node);
} else {
/* Node has a single child. Because the child must be red as per spec,
* and this node must be black, we can simply replace the node
* with it's child and color it black.*/
rbnode_t *child = node->left == NULL ? node->right : node->left;
memcpy(node, child, sizeof(rbnode_t) + tree->objw);
node->parent = parent;
node->color = BLACK;
kfree(child);
return;
}
}
void
__rbtree_new(rbtree_t *tree, size_t objw)
{
*tree = (rbtree_t) {
.root = NULL,
.objw = objw,
};
}
void*
rbtree_find(rbtree_t *tree, intmax_t key)
{
rbnode_t *found = s_find(tree, key);
if(found == NULL) return NULL;
return found->data;
}
rbnode_t*
s_node_new(rbtree_t *tree, intmax_t key, void *data)
{
rbnode_t *node = kmalloc(sizeof(rbnode_t) + tree->objw);
if(data != NULL) memcpy(node->data, data, tree->objw);
node->key = key;
return node;
}
void*
rbtree_insert(rbtree_t *tree, intmax_t key, void *data)
{
assert(tree != NULL);
rbnode_t *node = s_closest(tree, key);
if(node != NULL && node->key == key) {
memcpy(node->data, data, tree->objw);
} else if(node != NULL) {
rbnode_t *child = s_node_new(tree, key, data);
s_node_insert(tree, child, node, node->key > key);
return child->data;
} else {
node = s_node_new(tree, key, data);
node->parent = NULL;
node->children[0] = node->children[1] = NULL;
node->color = BLACK;
tree->root = node;
}
return node->data;
}
void *rbtree_reserve(rbtree_t *tree, intmax_t key)
{
assert(tree != NULL);
rbnode_t *node = s_closest(tree, key);
if(node != NULL && node->key == key) {
return node->data;
} else if(node != NULL) {
rbnode_t *child = s_node_new(tree, key, NULL);
s_node_insert(tree, child, node, node->key > key);
return child->data;
} else {
node = s_node_new(tree, key, NULL);
tree->root = node;
}
return node->data;
}