diff -r 000000000000 -r 804d8803eade ucx/avl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ucx/avl.c Wed Dec 09 11:32:01 2020 +0100 @@ -0,0 +1,373 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2017 Mike Becker, Olaf Wintermann All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "ucx/avl.h" + +#include + +#define ptrcast(ptr) ((void*)(ptr)) +#define alloc_tree(al) (UcxAVLTree*) almalloc((al), sizeof(UcxAVLTree)) +#define alloc_node(al) (UcxAVLNode*) almalloc((al), sizeof(UcxAVLNode)) + +static void ucx_avl_connect(UcxAVLTree *tree, + UcxAVLNode *node, UcxAVLNode *child, intptr_t nullkey) { + if (child) { + child->parent = node; + } + // if child is NULL, nullkey decides if left or right pointer is cleared + if (tree->cmpfunc( + ptrcast(child ? child->key : nullkey), + ptrcast(node->key), tree->userdata) > 0) { + node->right = child; + } else { + node->left = child; + } + size_t lh = node->left ? node->left->height : 0; + size_t rh = node->right ? node->right->height : 0; + node->height = 1 + (lh > rh ? lh : rh); +} + +#define avlheight(node) ((node) ? (node)->height : 0) + +static UcxAVLNode* avl_rotright(UcxAVLTree *tree, UcxAVLNode *l0) { + UcxAVLNode *p = l0->parent; + UcxAVLNode *l1 = l0->left; + if (p) { + ucx_avl_connect(tree, p, l1, 0); + } else { + l1->parent = NULL; + } + ucx_avl_connect(tree, l0, l1->right, l1->key); + ucx_avl_connect(tree, l1, l0, 0); + return l1; +} + +static UcxAVLNode* avl_rotleft(UcxAVLTree *tree, UcxAVLNode *l0) { + UcxAVLNode *p = l0->parent; + UcxAVLNode *l1 = l0->right; + if (p) { + ucx_avl_connect(tree, p, l1, 0); + } else { + l1->parent = NULL; + } + ucx_avl_connect(tree, l0, l1->left, l1->key); + ucx_avl_connect(tree, l1, l0, 0); + return l1; +} + +static void ucx_avl_balance(UcxAVLTree *tree, UcxAVLNode *n) { + int lh = avlheight(n->left); + int rh = avlheight(n->right); + n->height = 1 + (lh > rh ? lh : rh); + + if (lh - rh == 2) { + UcxAVLNode *c = n->left; + if (avlheight(c->right) - avlheight(c->left) == 1) { + avl_rotleft(tree, c); + } + n = avl_rotright(tree, n); + } else if (rh - lh == 2) { + UcxAVLNode *c = n->right; + if (avlheight(c->left) - avlheight(c->right) == 1) { + avl_rotright(tree, c); + } + n = avl_rotleft(tree, n); + } + + if (n->parent) { + ucx_avl_balance(tree, n->parent); + } else { + tree->root = n; + } +} + +UcxAVLTree *ucx_avl_new(cmp_func cmpfunc) { + return ucx_avl_new_a(cmpfunc, ucx_default_allocator()); +} + +UcxAVLTree *ucx_avl_new_a(cmp_func cmpfunc, UcxAllocator *allocator) { + UcxAVLTree* tree = alloc_tree(allocator); + if (tree) { + tree->allocator = allocator; + tree->cmpfunc = cmpfunc; + tree->root = NULL; + tree->userdata = NULL; + } + + return tree; +} + +static void ucx_avl_free_node(UcxAllocator *al, UcxAVLNode *node) { + if (node) { + ucx_avl_free_node(al, node->left); + ucx_avl_free_node(al, node->right); + alfree(al, node); + } +} + +void ucx_avl_free(UcxAVLTree *tree) { + UcxAllocator *al = tree->allocator; + ucx_avl_free_node(al, tree->root); + alfree(al, tree); +} + +static void ucx_avl_free_content_node(UcxAllocator *al, UcxAVLNode *node, + ucx_destructor destr) { + if (node) { + ucx_avl_free_content_node(al, node->left, destr); + ucx_avl_free_content_node(al, node->right, destr); + if (destr) { + destr(node->value); + } else { + alfree(al, node->value); + } + } +} + +void ucx_avl_free_content(UcxAVLTree *tree, ucx_destructor destr) { + ucx_avl_free_content_node(tree->allocator, tree->root, destr); +} + +UcxAVLNode *ucx_avl_get_node(UcxAVLTree *tree, intptr_t key) { + UcxAVLNode *n = tree->root; + int cmpresult; + while (n && (cmpresult = tree->cmpfunc( + ptrcast(key), ptrcast(n->key), tree->userdata))) { + n = cmpresult > 0 ? n->right : n->left; + } + return n; +} + +void *ucx_avl_get(UcxAVLTree *tree, intptr_t key) { + UcxAVLNode *n = ucx_avl_get_node(tree, key); + return n ? n->value : NULL; +} + +UcxAVLNode *ucx_avl_find_node(UcxAVLTree *tree, intptr_t key, + distance_func dfnc, int mode) { + UcxAVLNode *n = tree->root; + UcxAVLNode *closest = NULL; + + intmax_t cmpresult; + intmax_t closest_dist; + closest_dist = mode == UCX_AVL_FIND_LOWER_BOUNDED ? INTMAX_MIN : INTMAX_MAX; + + while (n && (cmpresult = dfnc( + ptrcast(key), ptrcast(n->key), tree->userdata))) { + if (mode == UCX_AVL_FIND_CLOSEST) { + intmax_t dist = cmpresult; + if (dist < 0) dist *= -1; + if (dist < closest_dist) { + closest_dist = dist; + closest = n; + } + } else if (mode == UCX_AVL_FIND_LOWER_BOUNDED && cmpresult <= 0) { + if (cmpresult > closest_dist) { + closest_dist = cmpresult; + closest = n; + } + } else if (mode == UCX_AVL_FIND_UPPER_BOUNDED && cmpresult >= 0) { + if (cmpresult < closest_dist) { + closest_dist = cmpresult; + closest = n; + } + } + n = cmpresult > 0 ? n->right : n->left; + } + return n ? n : closest; +} + +void *ucx_avl_find(UcxAVLTree *tree, intptr_t key, + distance_func dfnc, int mode) { + UcxAVLNode *n = ucx_avl_find_node(tree, key, dfnc, mode); + return n ? n->value : NULL; +} + +int ucx_avl_put(UcxAVLTree *tree, intptr_t key, void *value) { + return ucx_avl_put_s(tree, key, value, NULL); +} + +int ucx_avl_put_s(UcxAVLTree *tree, intptr_t key, void *value, + void **oldvalue) { + if (tree->root) { + UcxAVLNode *n = tree->root; + int cmpresult; + while ((cmpresult = tree->cmpfunc( + ptrcast(key), ptrcast(n->key), tree->userdata))) { + UcxAVLNode *m = cmpresult > 0 ? n->right : n->left; + if (m) { + n = m; + } else { + break; + } + } + + if (cmpresult) { + UcxAVLNode* e = alloc_node(tree->allocator); + if (e) { + e->key = key; e->value = value; e->height = 1; + e->parent = e->left = e->right = NULL; + ucx_avl_connect(tree, n, e, 0); + ucx_avl_balance(tree, n); + return 0; + } else { + return 1; + } + } else { + if (oldvalue) { + *oldvalue = n->value; + } + n->value = value; + return 0; + } + } else { + tree->root = alloc_node(tree->allocator); + if (tree->root) { + tree->root->key = key; tree->root->value = value; + tree->root->height = 1; + tree->root->parent = tree->root->left = tree->root->right = NULL; + + if (oldvalue) { + *oldvalue = NULL; + } + + return 0; + } else { + return 1; + } + } +} + +int ucx_avl_remove(UcxAVLTree *tree, intptr_t key) { + return ucx_avl_remove_s(tree, key, NULL, NULL); +} + +int ucx_avl_remove_node(UcxAVLTree *tree, UcxAVLNode *node) { + return ucx_avl_remove_s(tree, node->key, NULL, NULL); +} + +int ucx_avl_remove_s(UcxAVLTree *tree, intptr_t key, + intptr_t *oldkey, void **oldvalue) { + + UcxAVLNode *n = tree->root; + int cmpresult; + while (n && (cmpresult = tree->cmpfunc( + ptrcast(key), ptrcast(n->key), tree->userdata))) { + n = cmpresult > 0 ? n->right : n->left; + } + if (n) { + if (oldkey) { + *oldkey = n->key; + } + if (oldvalue) { + *oldvalue = n->value; + } + + UcxAVLNode *p = n->parent; + if (n->left && n->right) { + UcxAVLNode *s = n->right; + while (s->left) { + s = s->left; + } + ucx_avl_connect(tree, s->parent, s->right, s->key); + n->key = s->key; n->value = s->value; + p = s->parent; + alfree(tree->allocator, s); + } else { + if (p) { + ucx_avl_connect(tree, p, n->right ? n->right:n->left, n->key); + } else { + tree->root = n->right ? n->right : n->left; + if (tree->root) { + tree->root->parent = NULL; + } + } + alfree(tree->allocator, n); + } + + if (p) { + ucx_avl_balance(tree, p); + } + + return 0; + } else { + return 1; + } +} + +static size_t ucx_avl_countn(UcxAVLNode *node) { + if (node) { + return 1 + ucx_avl_countn(node->left) + ucx_avl_countn(node->right); + } else { + return 0; + } +} + +size_t ucx_avl_count(UcxAVLTree *tree) { + return ucx_avl_countn(tree->root); +} + +UcxAVLNode* ucx_avl_pred(UcxAVLNode* node) { + if (node->left) { + UcxAVLNode* n = node->left; + while (n->right) { + n = n->right; + } + return n; + } else { + UcxAVLNode* n = node; + while (n->parent) { + if (n->parent->right == n) { + return n->parent; + } else { + n = n->parent; + } + } + return NULL; + } +} + +UcxAVLNode* ucx_avl_succ(UcxAVLNode* node) { + if (node->right) { + UcxAVLNode* n = node->right; + while (n->left) { + n = n->left; + } + return n; + } else { + UcxAVLNode* n = node; + while (n->parent) { + if (n->parent->left == n) { + return n->parent; + } else { + n = n->parent; + } + } + return NULL; + } +}