diff options
Diffstat (limited to 'lacos_rbtree.cc')
-rw-r--r-- | lacos_rbtree.cc | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/lacos_rbtree.cc b/lacos_rbtree.cc new file mode 100644 index 0000000..14404c0 --- /dev/null +++ b/lacos_rbtree.cc @@ -0,0 +1,418 @@ +/* Plzip - A parallel version of the lzip data compressor + Copyright (C) 2009 Laszlo Ersek. + Copyright (C) 2009 Antonio Diaz Diaz. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#define _FILE_OFFSET_BITS 64 + +#include <cstddef> + +#include "lacos_rbtree.h" + + +enum lacos_rbtree_color { + LACOS_RBTREE_RED, + LACOS_RBTREE_BLACK +}; + +struct lacos_rbtree_node { + void *data; + lacos_rbtree_node *parent, + *left, + *right; + lacos_rbtree_color color; +}; + + +lacos_rbtree_node * +lacos_rbtree_find( lacos_rbtree_node *root, const void *key, + int (*cmp)(const void *cmp_key, const void *cmp_data)) +{ + int cmp_res; + while (root && (cmp_res = (*cmp)(key, root->data))) + root = cmp_res < 0 ? root->left : root->right; + return root; +} + + +lacos_rbtree_node * +lacos_rbtree_min( lacos_rbtree_node *root) +{ + lacos_rbtree_node *tmp; + + if (!root) + return 0; + + while ((tmp = root->left)) + root = tmp; + + return root; +} + + +lacos_rbtree_node * +lacos_rbtree_max( lacos_rbtree_node *root) +{ + lacos_rbtree_node *tmp; + + if (!root) + return 0; + + while ((tmp = root->right)) + root = tmp; + + return root; +} + + +lacos_rbtree_node * +lacos_rbtree_next( lacos_rbtree_node *current) +{ + lacos_rbtree_node *tmp; + + if (!current) + return 0; + + if ((tmp = current->right)) { + while ((current = tmp->left)) + tmp = current; + return tmp; + } + + while ((tmp = current->parent) && current == tmp->right) + current = tmp; + + return tmp; +} + + +lacos_rbtree_node * +lacos_rbtree_prev( lacos_rbtree_node *current) +{ + lacos_rbtree_node *tmp; + + if (!current) + return 0; + + if ((tmp = current->left)) { + while ((current = tmp->right)) + tmp = current; + return tmp; + } + + while ((tmp = current->parent) && current == tmp->left) + current = tmp; + + return tmp; +} + + +static void +lacos_rbtree_rotate_left( lacos_rbtree_node **new_root, + lacos_rbtree_node *rotation_root) +{ + lacos_rbtree_node + *parent = rotation_root->parent, + *rc = rotation_root->right, + *rlc = rc->left; + + rotation_root->right = rlc; + if (rlc) + rlc->parent = rotation_root; + rc->parent = parent; + if (parent) + if (rotation_root == parent->left) + parent->left = rc; + else + parent->right = rc; + else + *new_root = rc; + rc->left = rotation_root; + rotation_root->parent = rc; +} + + +static void +lacos_rbtree_rotate_right( lacos_rbtree_node **new_root, + lacos_rbtree_node *rotation_root) +{ + lacos_rbtree_node + *parent = rotation_root->parent, + *lc = rotation_root->left, + *lrc = lc->right; + + rotation_root->left = lrc; + if (lrc) + lrc->parent = rotation_root; + lc->parent = parent; + if (parent) + if (rotation_root == parent->left) + parent->left = lc; + else + parent->right = lc; + else + *new_root = lc; + lc->right = rotation_root; + rotation_root->parent = lc; +} + + +int +lacos_rbtree_insert( lacos_rbtree_node **new_root, + lacos_rbtree_node **new_node, void *new_data, + int (*cmp)(const void *cmp_new_data, const void *cmp_data), + void *(*alloc)(size_t size, void *alloc_ctl), void *alloc_ctl) +{ + int cmp_res = 0; + lacos_rbtree_node *tmp = *new_root, *parent = 0; + + while (tmp && (cmp_res = (*cmp)(new_data, tmp->data))) { + parent = tmp; + tmp = cmp_res < 0 ? tmp->left : tmp->right; + } + + if (tmp) { + *new_node = tmp; + return -1; + } + + if (!(*new_node = tmp = (lacos_rbtree_node *)(*alloc)(sizeof(lacos_rbtree_node), + alloc_ctl))) + return -1; + + tmp->data = new_data; + tmp->parent = parent; + tmp->left = 0; + tmp->right = 0; + if (parent) + if (cmp_res < 0) + parent->left = tmp; + else + parent->right = tmp; + else { + *new_root = tmp; + tmp->color = LACOS_RBTREE_BLACK; + return 0; + } + + { + lacos_rbtree_node *root = *new_root, *grandparent, *uncle; + + tmp->color = LACOS_RBTREE_RED; + + while (tmp != root && LACOS_RBTREE_RED == parent->color) { + grandparent = parent->parent; + if (parent == grandparent->left) { + uncle = grandparent->right; + if (uncle && LACOS_RBTREE_RED == uncle->color) { + parent->color = LACOS_RBTREE_BLACK; + uncle->color = LACOS_RBTREE_BLACK; + grandparent->color = LACOS_RBTREE_RED; + tmp = grandparent; + parent = tmp->parent; + } + else { + if (tmp == parent->right) { + tmp = parent; + lacos_rbtree_rotate_left(&root, tmp); + parent = tmp->parent; + grandparent = parent->parent; + } + parent->color = LACOS_RBTREE_BLACK; + grandparent->color = LACOS_RBTREE_RED; + lacos_rbtree_rotate_right(&root, grandparent); + } + } + else { + uncle = grandparent->left; + if (uncle && LACOS_RBTREE_RED == uncle->color) { + parent->color = LACOS_RBTREE_BLACK; + uncle->color = LACOS_RBTREE_BLACK; + grandparent->color = LACOS_RBTREE_RED; + tmp = grandparent; + parent = tmp->parent; + } + else { + if (tmp == parent->left) { + tmp = parent; + lacos_rbtree_rotate_right(&root, tmp); + parent = tmp->parent; + grandparent = parent->parent; + } + parent->color = LACOS_RBTREE_BLACK; + grandparent->color = LACOS_RBTREE_RED; + lacos_rbtree_rotate_left(&root, grandparent); + } + } + } + + root->color = LACOS_RBTREE_BLACK; + *new_root = root; + } + return 0; +} + + +void +lacos_rbtree_delete( lacos_rbtree_node **new_root, + lacos_rbtree_node *old_node, void **old_data, + void (*dealloc)(void *ptr, void *alloc_ctl), void *alloc_ctl) +{ + lacos_rbtree_node *child, *parent, *root = *new_root, + *old_node_left = old_node->left, + *old_node_right = old_node->right, + *old_node_parent = old_node->parent; + lacos_rbtree_color color_of_firstly_unlinked; + + if (old_data) + *old_data = old_node->data; + + if (old_node_left && old_node_right) { + lacos_rbtree_node + *to_relink = old_node_right, + *tmp = to_relink->left; + + if (tmp) { + do { + to_relink = tmp; + tmp = tmp->left; + } while (tmp); + parent = to_relink->parent; + child = to_relink->right; + parent->left = child; + if (child) + child->parent = parent; + to_relink->right = old_node_right; + old_node_right->parent = to_relink; + } + else { + parent = old_node_right; + child = old_node_right->right; + } + + to_relink->left = old_node_left; + old_node_left->parent = to_relink; + + color_of_firstly_unlinked = to_relink->color; + to_relink->color = old_node->color; + + to_relink->parent = old_node_parent; + + if (old_node_parent) + if (old_node == old_node_parent->left) + old_node_parent->left = to_relink; + else + old_node_parent->right = to_relink; + else + root = to_relink; + } + else { + parent = old_node_parent; + child = old_node_left ? old_node_left : old_node_right; + color_of_firstly_unlinked = old_node->color; + + if (child) + child->parent = parent; + if (old_node_parent) + if (old_node == old_node_parent->left) + old_node_parent->left = child; + else + old_node_parent->right = child; + else + root = child; + } + + (*dealloc)(old_node, alloc_ctl); + + if (LACOS_RBTREE_BLACK == color_of_firstly_unlinked) { + lacos_rbtree_node *brother, *left_nephew, *right_nephew; + int left_black, right_black; + + while (child != root && (!child || LACOS_RBTREE_BLACK == child->color)) + if (child == parent->left) { + brother = parent->right; + if (LACOS_RBTREE_RED == brother->color) { + brother->color = LACOS_RBTREE_BLACK; + parent->color = LACOS_RBTREE_RED; + lacos_rbtree_rotate_left(&root, parent); + brother = parent->right; + } + left_nephew = brother->left; + right_nephew = brother->right; + right_black = !right_nephew + || LACOS_RBTREE_BLACK == right_nephew->color; + if ((!left_nephew || LACOS_RBTREE_BLACK == left_nephew->color) + && right_black) { + brother->color = LACOS_RBTREE_RED; + child = parent; + parent = parent->parent; + } + else { + if (right_black) { + left_nephew->color = LACOS_RBTREE_BLACK; + brother->color = LACOS_RBTREE_RED; + lacos_rbtree_rotate_right(&root, brother); + brother = parent->right; + right_nephew = brother->right; + } + brother->color = parent->color; + parent->color = LACOS_RBTREE_BLACK; + right_nephew->color = LACOS_RBTREE_BLACK; + lacos_rbtree_rotate_left(&root, parent); + child = root; + break; + } + } + else { + brother = parent->left; + if (LACOS_RBTREE_RED == brother->color) { + brother->color = LACOS_RBTREE_BLACK; + parent->color = LACOS_RBTREE_RED; + lacos_rbtree_rotate_right(&root, parent); + brother = parent->left; + } + right_nephew = brother->right; + left_nephew = brother->left; + left_black = !left_nephew || LACOS_RBTREE_BLACK == left_nephew->color; + if ((!right_nephew || LACOS_RBTREE_BLACK == right_nephew->color) + && left_black) { + brother->color = LACOS_RBTREE_RED; + child = parent; + parent = parent->parent; + } + else { + if (left_black) { + right_nephew->color = LACOS_RBTREE_BLACK; + brother->color = LACOS_RBTREE_RED; + lacos_rbtree_rotate_left(&root, brother); + brother = parent->left; + left_nephew = brother->left; + } + brother->color = parent->color; + parent->color = LACOS_RBTREE_BLACK; + left_nephew->color = LACOS_RBTREE_BLACK; + lacos_rbtree_rotate_right(&root, parent); + child = root; + break; + } + } + + if (child) + child->color = LACOS_RBTREE_BLACK; + } + + *new_root = root; +} |