/* * Copyright (c) 2015 Gilles Chanteperdrix * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #ifdef AVL_PSHARED #define __AVL(__decl) shavl_ ## __decl #define __AVLH(__decl) shavlh_ ## __decl #define __AVL_T(__type) sh ## __type #else #define __AVL(__decl) avl_ ## __decl #define __AVLH(__decl) avlh_ ## __decl #define __AVL_T(__type) __type #endif struct __AVL_T (avlh) * __AVL(inorder)(const struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * holder, const int dir) { /* Assume dir == AVL_RIGHT in comments. */ struct __AVL_T (avlh) * next; /* * If the current node is not right threaded, then go down left, * starting from its right child. */ if (__AVLH(has_child)(avl, holder, dir)) { const int opp_dir = avl_opposite(dir); holder = __AVLH(link)(avl, holder, dir); while ((next = __AVLH(child)(avl, holder, opp_dir))) holder = next; next = holder; } else { for (;;) { next = __AVLH(up)(avl, holder); if (next == __AVL(anchor)(avl)) return NULL; if (holder->type != dir) break; holder = next; } } return next; } struct __AVL_T (avlh) * __AVL(postorder)(const struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * const holder, const int dir) { /* Assume dir == AVL_RIGHT in comments. */ struct __AVL_T (avlh) * next = __AVLH(up)(avl, holder); if (holder->type != dir) /* * If the current node is not a right node, follow the nodes in * inorder until we find a right threaded node. */ while (__AVLH(has_child)(avl, next, dir)) next = __AVL(inorder)(avl, next, dir); else /* * else the current node is a right node, its parent is the * next in postorder. */ if (next == __AVL(anchor)(avl)) next = NULL; return next; } struct __AVL_T (avlh) * __AVL(preorder)(const struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * holder, const int dir) { struct __AVL_T (avlh) * next; /* Assume dir == AVL_RIGHT in comments. */ /* * If the current node has a left child (hence is not left threaded), * then return it. */ if (__AVLH(has_child)(avl, holder, avl_opposite(dir))) return __AVLH(link)(avl, holder, avl_opposite(dir)); /* * Else follow the right threads until we find a node which is not right * threaded (hence has a right child) and return its right child. */ next = holder; while (!__AVLH(has_child)(avl, next, dir)) { next = __AVL(inorder)(avl, next, dir); if (next == NULL) return NULL; } return __AVLH(link)(avl, next, dir); } static inline unsigned int avlh_thr(const struct __AVL_T (avl) * const avl, const struct __AVL_T (avlh) * h) { unsigned int result = 0; if (__AVLH(link)(avl, h, AVL_LEFT) == NULL) result |= AVL_THR_LEFT; if (__AVLH(link)(avl, h, AVL_RIGHT) == NULL) result |= AVL_THR_RIGHT; return result; } static inline void avlh_set_parent_link(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * lhs, struct __AVL_T (avlh) * rhs) { __AVLH(set_link)(avl, __AVLH(up)(avl, lhs), lhs->type, rhs); } static inline void avlh_set_left(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * lhs, struct __AVL_T (avlh) * rhs) { __AVLH(set_link)(avl, lhs, AVL_LEFT, rhs); } static inline void avlh_set_up(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * lhs, struct __AVL_T (avlh) * rhs) { __AVLH(set_link)(avl, lhs, AVL_UP, rhs); } static inline void avlh_set_right(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * lhs, struct __AVL_T (avlh) * rhs) { __AVLH(set_link)(avl, lhs, AVL_RIGHT, rhs); } static inline void avl_set_top(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * holder) { __AVLH(set_link)(avl, __AVL(anchor)(avl), AVL_RIGHT, holder); } static inline void avl_set_head(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * holder) { __AVL(set_end)(avl, AVL_LEFT, holder); } static inline void avl_set_tail(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * holder) { __AVL(set_end)(avl, AVL_RIGHT, holder); } /* Internal functions used for rebalancing (for insertion and deletion). */ static inline struct __AVL_T (avlh) * avlh_rotate(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * const holder, const int dir) { const int opp_dir = avl_opposite(dir); struct __AVL_T (avlh) * const nexttop = __AVLH(link)(avl, holder, opp_dir); struct __AVL_T (avlh) * const subtree = __AVLH(child)(avl, nexttop, dir); if (subtree) { __AVLH(set_link)(avl, holder, opp_dir, subtree); avlh_set_up(avl, subtree, holder); subtree->type = opp_dir; } else __AVLH(set_link)(avl, holder, opp_dir, NULL); __AVLH(set_link)(avl, nexttop, dir, holder); avlh_set_up(avl, nexttop, __AVLH(up)(avl, holder)); nexttop->type = holder->type; avlh_set_up(avl, holder, nexttop); holder->type = dir; avlh_set_parent_link(avl, nexttop, nexttop); return nexttop; } static inline struct __AVL_T (avlh) * avlh_dbl_rotate(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * const holder, const int dir) { const int opp = avl_opposite(dir); avlh_rotate(avl, __AVLH(link)(avl, holder, opp), opp); return avlh_rotate(avl, holder, dir); } static struct __AVL_T (avlh) * avlh_rebalance(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * holder, const int delta) { int dir = delta; struct __AVL_T (avlh) * const heavy_side = __AVLH(link)(avl, holder, dir); if (heavy_side->balance == -delta) { /* heavy_side->balance == -delta, double rotation needed. */ holder = avlh_dbl_rotate(avl, holder, avl_opposite(dir)); /* * recompute balances, there are three nodes involved, two of * which balances become null. */ dir = holder->balance ? : AVL_RIGHT; __AVLH(link)(avl, holder, dir)->balance = 0; __AVLH(link)(avl, holder, avl_opposite(dir))->balance = -holder->balance; holder->balance = 0; } else { /* * heavy_side->balance == delta or 0, simple rotation needed. * the case 0 occurs only when deleting, never when inserting. */ /* heavy_side becomes the new root. */ avlh_rotate(avl, holder, avl_opposite(dir)); /* recompute balances. */ holder->balance -= heavy_side->balance; heavy_side->balance -= delta; holder = heavy_side; } return holder; } /* * The avlh_rebalance functions was split in two parts to allow inlining in * the simplest case. */ static inline struct __AVL_T (avlh) * avlh_balance_add(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * const holder, const int delta) { if (holder->balance == delta) /* we need to rebalance the current subtree. */ return avlh_rebalance(avl, holder, delta); /* the current subtree does not need rebalancing */ holder->balance += delta; return holder; } static inline void avlh_link_child(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * const oldh, struct __AVL_T (avlh) * const newh, const int side) { struct __AVL_T (avlh) * const child = __AVLH(link)(avl, oldh, side); __AVLH(set_link)(avl, newh, side, child); if (__AVLH(has_child)(avl, oldh, side)) avlh_set_up(avl, child, newh); } static inline void avlh_replace(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * const oldh, struct __AVL_T (avlh) * const newh) { newh->type = oldh->type; /* Do not update the balance, this has to be done by the caller. */ avlh_set_up(avl, newh, __AVLH(up)(avl, oldh)); avlh_set_parent_link(avl, oldh, newh); avlh_link_child(avl, oldh, newh, AVL_LEFT); avlh_link_child(avl, oldh, newh, AVL_RIGHT); } /* Deletion helpers. */ static void avl_delete_leaf(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * const node) { /* * Node has no child at all. It disappears and its father becomes * threaded on the side id was. */ struct __AVL_T (avlh) * const new_node = __AVLH(up)(avl, node); const int dir = node->type; /* Suppress node. */ __AVLH(set_link)(avl, new_node, dir, __AVLH(link)(avl, node, dir)); if (node == __AVL(end)(avl, dir)) __AVL(set_end)(avl, dir, new_node); } static struct __AVL_T (avlh) * avl_delete_1child(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * const node, const int dir) { /* * Node is threaded on one side and has a child on the other * side. In this case, node is replaced by its child. */ struct __AVL_T (avlh) * const new_node = __AVLH(link)(avl, node, dir); /* * Change links as if new_node was suppressed before calling * avlh_replace. */ __AVLH(set_link)(avl, node, dir, __AVLH(link)(avl, new_node, dir)); avlh_replace(avl, node, new_node); if (node == __AVL(end)(avl, avl_opposite(dir))) __AVL(set_end)(avl, avl_opposite(dir), new_node); /* new_node->balance 0, which is correct. */ return new_node; } static int avl_delete_2children(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * const node); /* Insertion helpers. */ static inline void avlh_attach(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * const parent, struct __AVL_T (avlh) * const child, const int side) { avlh_set_left(avl, child, NULL); avlh_set_right(avl, child, NULL); avlh_set_up(avl, child, parent); __AVLH(set_link)(avl, parent, side, child); child->type = side; } /* * Insert a node, given its parent and the side where it should be inserted. * Helper for all insertion functions. */ static inline void avl_insert_inner(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * parent, struct __AVL_T (avlh) * const node, const int side) { avlh_attach(avl, parent ? : __AVL(anchor)(avl), node, side); ++__AVL(count)(avl); if (parent == NULL) goto insert_first_and_ret; /* Get away from fast path */ if (parent == __AVL(end)(avl, side)) __AVL(set_end)(avl, side, node); parent->balance += side; while (parent->balance) { const int delta = parent->type; parent = __AVLH(up)(avl, parent); if (parent == __AVL(anchor)(avl)) goto inc_height_and_ret; /* Get away from fast path */ parent = avlh_balance_add(avl, parent, delta); } return; insert_first_and_ret: avl_set_head(avl, node); avl_set_tail(avl, node); inc_height_and_ret: ++__AVL(height)(avl); } /* External functions. */ int __AVL(delete)(struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * node) { if (!--__AVL(count)(avl)) { goto delete_last_and_ret; } switch (avlh_thr(avl, node)) { case (AVL_THR_LEFT | AVL_THR_RIGHT): /* thr is 5 */ avl_delete_leaf(avl, node); break; case AVL_THR_LEFT: /* only AVL_LEFT bit is on, thr is 1. */ node = avl_delete_1child(avl, node, AVL_RIGHT); break; case AVL_THR_RIGHT: /* only AVL_RIGHT bit is on, thr is 4. */ node = avl_delete_1child(avl, node, AVL_LEFT); break; case 0: return avl_delete_2children(avl, node); } /* node is the first node which needs to be rebalanced. The tree is rebalanced, and contrarily to what happened for insertion, the rebalancing stops when a node which is NOT balanced is met. */ while (!node->balance) { const int delta = -node->type; node = __AVLH(up)(avl, node); if (node == __AVL(anchor)(avl)) goto dec_height_and_ret; node = avlh_balance_add(avl, node, delta); } return 0; delete_last_and_ret: avl_set_top(avl, NULL); avl_set_head(avl, NULL); avl_set_tail(avl, NULL); dec_height_and_ret: --__AVL(height)(avl); return 0; } static int avl_delete_2children(struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * const node) { const int dir = node->balance ? node->balance : 1; struct __AVL_T (avlh) * const new_node = __AVL(inorder)(avl, node, dir); __AVL(delete)(avl, new_node); ++__AVL(count)(avl); avlh_replace(avl, node, new_node); new_node->balance = node->balance; if (__AVL(end)(avl, dir) == node) __AVL(set_end)(avl, dir, new_node); return 0; } int __AVL(prepend)(struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * const holder, const struct __AVL_T(avl_searchops) * ops) { struct __AVL_T (avlh) * const parent = __AVL(head)(avl); int type = parent == NULL ? AVL_RIGHT : AVL_LEFT; if (parent == NULL || ops->cmp(holder, parent) < 0) { avl_insert_inner(avl, parent, holder, type); return 0; } return -EINVAL; } int __AVL(insert_at)(struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * parent, int dir, struct __AVL_T(avlh) * child) { if (parent == NULL) dir = AVL_RIGHT; else { if (!__AVLH(thr_tst)(avl, parent, dir)) return -EINVAL; } avl_insert_inner(avl, parent, child, dir); return 0; } int __AVL(insert)(struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * const holder, const struct __AVL_T(avl_searchops) * ops) { int delta; struct __AVL_T (avlh) * parent; parent = __AVL(search_inner)(avl, holder, &delta, ops); if (delta == 0) return -EBUSY; avl_insert_inner(avl, parent, holder, delta); return 0; } int __AVL(insert_front)(struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * const holder, const struct __AVL_T(avl_searchops) * ops) { int delta; struct __AVL_T (avlh) * parent; parent = ops->search(avl, holder, &delta, AVL_LEFT); avl_insert_inner(avl, parent, holder, delta ? : AVL_LEFT); return 0; } int __AVL(insert_back)(struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * const holder, const struct __AVL_T(avl_searchops) * ops) { int delta; struct __AVL_T (avlh) * parent; parent = ops->search(avl, holder, &delta, AVL_RIGHT); avl_insert_inner(avl, parent, holder, delta ? : AVL_RIGHT); return 0; } int __AVL(append)(struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * const holder, const struct __AVL_T(avl_searchops) * ops) { struct __AVL_T (avlh) * const parent = __AVL(tail)(avl); if (parent == NULL || ops->cmp(holder, parent) > 0) { avl_insert_inner(avl, parent, holder, AVL_RIGHT); return 0; } return -EINVAL; } /* * Special case, when we know that replacing a node with another will not change * the avl, much faster than remove + add */ int __AVL(replace)(struct __AVL_T(avl) * avl, struct __AVL_T(avlh) * oldh, struct __AVL_T(avlh) * newh, const struct __AVL_T(avl_searchops) * ops) { struct __AVL_T (avlh) * prev, *next; prev = __AVL(prev)(avl, oldh); next = __AVL(next)(avl, oldh); if ((prev && ops->cmp(newh, prev) < 0) || (next && ops->cmp(newh, next) > 0)) return -EINVAL; avlh_replace(avl, oldh, newh); if (oldh == __AVL(head)(avl)) avl_set_head(avl, newh); if (oldh == __AVL(tail)(avl)) avl_set_tail(avl, newh); newh->balance = oldh->balance; return 0; } struct __AVL_T (avlh) * __AVL(update)(struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * const holder, const struct __AVL_T(avl_searchops) * ops) { int delta; struct __AVL_T (avlh) * const oldh = __AVL(search_inner)(avl, holder, &delta, ops); if (!delta) { __AVL(replace)(avl, oldh, holder, ops); return oldh; } return NULL; } struct __AVL_T (avlh) * __AVL(set)(struct __AVL_T(avl) * const avl, struct __AVL_T(avlh) * const holder, const struct __AVL_T(avl_searchops) * ops) { int delta; struct __AVL_T (avlh) * const oldh = __AVL(search_inner)(avl, holder, &delta, ops); if (delta) { avl_insert_inner(avl, oldh, holder, delta); return NULL; } __AVL(replace)(avl, oldh, holder, ops); return oldh; } void __AVL(init)(struct __AVL_T(avl) * const avl) { __AVLH(init)(__AVL(anchor)(avl)); /* this must be first. */ __AVL(height)(avl) = 0; __AVL(count)(avl) = 0; avl_set_top(avl, NULL); avl_set_head(avl, NULL); avl_set_tail(avl, NULL); } void __AVL(destroy)(struct __AVL_T(avl) * const avl) { __AVL(init)(avl); } void __AVL(clear)(struct __AVL_T(avl) * const avl, void (*destruct)(struct __AVL_T(avlh) *)) { if (destruct) { struct __AVL_T (avlh) * next, *holder = __AVL(gethead)(avl); while (holder) { next = __AVL(postorder_next)(avl, holder); destruct(holder); holder = next; } } __AVL(init)(avl); } static inline void avl_dumper_visit(FILE * file, const struct __AVL_T (avl) * const avl, struct __AVL_T (avlh) * node, __AVL_T(avlh_prn_t) * prn, const char *blank, unsigned int blank_sz, char *buf, unsigned int indent, unsigned int len) { char bal; if (__AVLH(has_child)(avl, node, AVL_RIGHT)) { if (blank_sz >= (unsigned int)(buf - blank)) { snprintf(buf, len + 3, "%*s\n", (int)len + 1, "bug!"); fputs(buf - blank_sz, file); } else avl_dumper_visit(file, avl, __AVLH(right)(avl, node), prn, blank, blank_sz + indent, buf, indent, len); } switch (node->balance) { case 0: bal = '.'; break; case -1: bal = '-'; break; case 1: bal = '+'; break; default: bal = '?'; /* Bug. */ } (*prn)(buf, len + 1, node); buf[len] = bal; buf[len + 1] = '\n'; buf[len + 2] = '\0'; fputs(buf - blank_sz, file); if (__AVLH(has_child)(avl, node, AVL_LEFT)) { if (blank_sz >= (unsigned int)(buf - blank)) { snprintf(buf, len + 3, "%*s\n", (int)len + 1, "bug!"); fputs(buf - blank_sz, file); } else avl_dumper_visit(file, avl, __AVLH(left)(avl, node), prn, blank, blank_sz + indent, buf, indent, len); } } void __AVL(dump)(FILE * file, const struct __AVL_T(avl) * const avl, __AVL_T(avlh_prn_t) * prn, unsigned int indent, unsigned int len) { struct __AVL_T (avlh) * holder = __AVL(gettop)(avl); putc('\n', file); if (!holder) fputs("Empty.\n", file); else { size_t blank_sz = (__AVL(height)(avl) - 1) * indent; char buffer[blank_sz + len + 3]; /* 3 == balance char + sizeof("\n\0") */ memset(buffer, ' ', blank_sz); avl_dumper_visit(file, avl, holder, prn, buffer, 0, buffer + blank_sz, indent, len); } fflush(file); } static int avl_check_visit(const struct __AVL_T (avl) * avl, struct __AVL_T (avlh) * node, unsigned int level) { int err; if (!__AVLH(has_child)(avl, node, AVL_RIGHT)) goto check_balance; if (level > __AVL(height)(avl)) { fprintf(stderr, "too much recursion\n"); return -EINVAL; } err = avl_check_visit(avl, __AVLH(right)(avl, node), level + 1); if (err < 0) return err; check_balance: switch (node->balance) { case 0: break; case -1: break; case 1: break; default: fprintf(stderr, "invalid balance\n"); return -EINVAL; } if (!__AVLH(has_child)(avl, node, AVL_LEFT)) return 0; err = avl_check_visit(avl, __AVLH(left)(avl, node), level + 1); if (err < 0) return err; return 0; } int __AVL(check)(const struct __AVL_T(avl) * avl, const struct __AVL_T(avl_searchops) * ops) { struct __AVL_T (avlh) * holder = __AVL(gettop)(avl), *last; int err; if (!holder) return 0; err = avl_check_visit(avl, holder, 0); if (err < 0) return err; last = NULL; for (holder = __AVL(gethead)(avl); holder; holder = __AVL(next)(avl, holder)) { if (last != NULL) if (ops->cmp(holder, last) < 0) { fprintf(stderr, "disordered nodes\n"); return -EINVAL; } last = holder; } return 0; }