diff options
Diffstat (limited to 'leptonica/src/rbtree.c')
-rw-r--r-- | leptonica/src/rbtree.c | 905 |
1 files changed, 905 insertions, 0 deletions
diff --git a/leptonica/src/rbtree.c b/leptonica/src/rbtree.c new file mode 100644 index 00000000..75def3d2 --- /dev/null +++ b/leptonica/src/rbtree.c @@ -0,0 +1,905 @@ +/*====================================================================* + - Copyright (C) 2001 Leptonica. 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 ANY + - 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. + *====================================================================*/ + +/* + * Modified from the excellent code here: + * http://en.literateprograms.org/Red-black_tree_(C)?oldid=19567 + * which has been placed in the public domain under the Creative Commons + * CC0 1.0 waiver (http://creativecommons.org/publicdomain/zero/1.0/). + */ + +/*! + * \file rbtree.c + * <pre> + * + * Basic functions for using red-black trees. These are "nearly" balanced + * sorted trees with ordering by key that allows insertion, lookup and + * deletion of key/value pairs in log(n) time. + * + * We use red-black trees to implement our version of: + * * a map: a function that maps keys to values (e.g., int64 --> int64). + * * a set: a collection that is sorted by unique keys (without + * associated values) + * + * There are 5 invariant properties of RB trees: + * (1) Each node is either red or black. + * (2) The root node is black. + * (3) All leaves are black and contain no data (null). + * (4) Every red node has two children and both are black. This is + * equivalent to requiring the parent of every red node to be black. + * (5) All paths from any given node to its leaf nodes contain the + * same number of black nodes. + * + * Interface to red-black tree + * L_RBTREE *l_rbtreeCreate() + * RB_TYPE *l_rbtreeLookup() + * void l_rbtreeInsert() + * void l_rbtreeDelete() + * void l_rbtreeDestroy() + * L_RBTREE_NODE *l_rbtreeGetFirst() + * L_RBTREE_NODE *l_rbtreeGetNext() + * L_RBTREE_NODE *l_rbtreeGetLast() + * L_RBTREE_NODE *l_rbtreeGetPrev() + * l_int32 l_rbtreeGetCount() + * void l_rbtreePrint() + * + * General comparison function + * static l_int32 compareKeys() + * </pre> + */ + +#ifdef HAVE_CONFIG_H +#include <config_auto.h> +#endif /* HAVE_CONFIG_H */ + +#include "allheaders.h" + + /* The node color enum is only needed in the rbtree implementation */ +enum { + L_RED_NODE = 1, + L_BLACK_NODE = 2 +}; + + /* This makes it simpler to read the code */ +typedef L_RBTREE_NODE node; + + /* Lots of static helper functions */ +static void destroy_helper(node *n); +static void count_helper(node *n, l_int32 *pcount); +static void print_tree_helper(FILE *fp, node *n, l_int32 keytype, + l_int32 indent); + +static l_int32 compareKeys(l_int32 keytype, RB_TYPE left, RB_TYPE right); + +static node *grandparent(node *n); +static node *sibling(node *n); +static node *uncle(node *n); +static l_int32 node_color(node *n); +static node *new_node(RB_TYPE key, RB_TYPE value, l_int32 node_color, + node *left, node *right); +static node *lookup_node(L_RBTREE *t, RB_TYPE key); +static void rotate_left(L_RBTREE *t, node *n); +static void rotate_right(L_RBTREE *t, node *n); +static void replace_node(L_RBTREE *t, node *oldn, node *newn); +static void insert_case1(L_RBTREE *t, node *n); +static void insert_case2(L_RBTREE *t, node *n); +static void insert_case3(L_RBTREE *t, node *n); +static void insert_case4(L_RBTREE *t, node *n); +static void insert_case5(L_RBTREE *t, node *n); +static node *maximum_node(node *root); +static void delete_case1(L_RBTREE *t, node *n); +static void delete_case2(L_RBTREE *t, node *n); +static void delete_case3(L_RBTREE *t, node *n); +static void delete_case4(L_RBTREE *t, node *n); +static void delete_case5(L_RBTREE *t, node *n); +static void delete_case6(L_RBTREE *t, node *n); +static void verify_properties(L_RBTREE *t); + +#ifndef NO_CONSOLE_IO +#define VERIFY_RBTREE 0 /* only for debugging */ +#endif /* ~NO_CONSOLE_IO */ + +/* ------------------------------------------------------------- * + * Interface to Red-black Tree * + * ------------------------------------------------------------- */ +/*! + * \brief l_rbtreeCreate() + * + * \param[in] keytype defined by an enum for an RB_TYPE union + * \return rbtree container with empty ptr to the root + */ +L_RBTREE * +l_rbtreeCreate(l_int32 keytype) +{ +L_RBTREE *t; + + PROCNAME("l_rbtreeCreate"); + + if (keytype != L_INT_TYPE && keytype != L_UINT_TYPE && + keytype != L_FLOAT_TYPE && keytype) + return (L_RBTREE *)ERROR_PTR("invalid keytype", procName, NULL); + + t = (L_RBTREE *)LEPT_CALLOC(1, sizeof(L_RBTREE)); + t->keytype = keytype; + verify_properties(t); + return t; +} + +/*! + * \brief l_rbtreeLookup() + * + * \param[in] t rbtree, including root node + * \param[in] key find a node with this key + * \return &value a pointer to a union, if the node exists; else NULL + */ +RB_TYPE * +l_rbtreeLookup(L_RBTREE *t, + RB_TYPE key) +{ +node *n; + + PROCNAME("l_rbtreeLookup"); + + if (!t) + return (RB_TYPE *)ERROR_PTR("tree is null\n", procName, NULL); + + n = lookup_node(t, key); + return n == NULL ? NULL : &n->value; +} + +/*! + * \brief l_rbtreeInsert() + * + * \param[in] t rbtree, including root node + * \param[in] key insert a node with this key, if the key does not + * already exist in the tree + * \param[in] value typically an int, used for an index + * \return void + * + * <pre> + * Notes: + * (1) If a node with the key already exists, this just updates the value. + * </pre> + */ +void +l_rbtreeInsert(L_RBTREE *t, + RB_TYPE key, + RB_TYPE value) +{ +node *n, *inserted_node; + + PROCNAME("l_rbtreeInsert"); + + if (!t) { + L_ERROR("tree is null\n", procName); + return; + } + + inserted_node = new_node(key, value, L_RED_NODE, NULL, NULL); + if (t->root == NULL) { + t->root = inserted_node; + } else { + n = t->root; + while (1) { + int comp_result = compareKeys(t->keytype, key, n->key); + if (comp_result == 0) { + n->value = value; + LEPT_FREE(inserted_node); + return; + } else if (comp_result < 0) { + if (n->left == NULL) { + n->left = inserted_node; + break; + } else { + n = n->left; + } + } else { /* comp_result > 0 */ + if (n->right == NULL) { + n->right = inserted_node; + break; + } else { + n = n->right; + } + } + } + inserted_node->parent = n; + } + insert_case1(t, inserted_node); + verify_properties(t); +} + +/*! + * \brief l_rbtreeDelete() + * + * \param[in] t rbtree, including root node + * \param[in] key delete the node with this key + * \return void + */ +void +l_rbtreeDelete(L_RBTREE *t, + RB_TYPE key) +{ +node *n, *child; + + PROCNAME("l_rbtreeDelete"); + + if (!t) { + L_ERROR("tree is null\n", procName); + return; + } + + n = lookup_node(t, key); + if (n == NULL) return; /* Key not found, do nothing */ + if (n->left != NULL && n->right != NULL) { + /* Copy key/value from predecessor and then delete it instead */ + node *pred = maximum_node(n->left); + n->key = pred->key; + n->value = pred->value; + n = pred; + } + + /* n->left == NULL || n->right == NULL */ + child = n->right == NULL ? n->left : n->right; + if (node_color(n) == L_BLACK_NODE) { + n->color = node_color(child); + delete_case1(t, n); + } + replace_node(t, n, child); + if (n->parent == NULL && child != NULL) /* root should be black */ + child->color = L_BLACK_NODE; + LEPT_FREE(n); + + verify_properties(t); +} + +/*! + * \brief l_rbtreeDestroy() + * + * \param[in] pt pointer to tree; will be wet to null before returning + * \return void + * + * <pre> + * Notes: + * (1) Destroys the tree and nulls the input tree ptr. + * </pre> + */ +void +l_rbtreeDestroy(L_RBTREE **pt) +{ +node *n; + + if (!pt) return; + if (*pt == NULL) return; + n = (*pt)->root; + destroy_helper(n); + LEPT_FREE(*pt); + *pt = NULL; +} + + /* postorder DFS */ +static void +destroy_helper(node *n) +{ + if (!n) return; + destroy_helper(n->left); + destroy_helper(n->right); + LEPT_FREE(n); +} + +/*! + * \brief l_rbtreeGetFirst() + * + * \param[in] t rbtree, including root node + * \return first node, or NULL on error or if the tree is empty + * + * <pre> + * Notes: + * (1) This is the first node in an in-order traversal. + * </pre> + */ +L_RBTREE_NODE * +l_rbtreeGetFirst(L_RBTREE *t) +{ +node *n; + + PROCNAME("l_rbtreeGetFirst"); + + if (!t) + return (L_RBTREE_NODE *)ERROR_PTR("tree is null", procName, NULL); + if (t->root == NULL) { + L_INFO("tree is empty\n", procName); + return NULL; + } + + /* Just go down the left side as far as possible */ + n = t->root; + while (n && n->left) + n = n->left; + return n; +} + +/*! + * \brief l_rbtreeGetNext() + * + * \param[in] n current node + * \return next node, or NULL if it's the last node + * + * <pre> + * Notes: + * (1) This finds the next node, in an in-order traversal, from + * the current node. + * (2) It is useful as an iterator for a map. + * (3) Call l_rbtreeGetFirst() to get the first node. + * </pre> + */ +L_RBTREE_NODE * +l_rbtreeGetNext(L_RBTREE_NODE *n) +{ + PROCNAME("l_rbtreeGetNext"); + + if (!n) + return (L_RBTREE_NODE *)ERROR_PTR("n not defined", procName, NULL); + + /* If there is a right child, go to it, and then go left all the + * way to the end. Otherwise go up to the parent; continue upward + * as long as you're on the right branch, but stop at the parent + * when you hit it from the left branch. */ + if (n->right) { + n = n->right; + while (n->left) + n = n->left; + return n; + } else { + while (n->parent && n->parent->right == n) + n = n->parent; + return n->parent; + } +} + +/*! + * \brief l_rbtreeGetLast() + * + * \param[in] t rbtree, including root node + * \return last node, or NULL on error or if the tree is empty + * + * <pre> + * Notes: + * (1) This is the last node in an in-order traversal. + * </pre> + */ +L_RBTREE_NODE * +l_rbtreeGetLast(L_RBTREE *t) +{ +node *n; + + PROCNAME("l_rbtreeGetLast"); + + if (!t) + return (L_RBTREE_NODE *)ERROR_PTR("tree is null", procName, NULL); + if (t->root == NULL) { + L_INFO("tree is empty\n", procName); + return NULL; + } + + /* Just go down the right side as far as possible */ + n = t->root; + while (n && n->right) + n = n->right; + return n; +} + +/*! + * \brief l_rbtreeGetPrev() + * + * \param[in] n current node + * \return next node, or NULL if it's the first node + * + * <pre> + * Notes: + * (1) This finds the previous node, in an in-order traversal, from + * the current node. + * (2) It is useful as an iterator for a map. + * (3) Call l_rbtreeGetLast() to get the last node. + * </pre> + */ +L_RBTREE_NODE * +l_rbtreeGetPrev(L_RBTREE_NODE *n) +{ + PROCNAME("l_rbtreeGetPrev"); + + if (!n) + return (L_RBTREE_NODE *)ERROR_PTR("n not defined", procName, NULL); + + /* If there is a left child, go to it, and then go right all the + * way to the end. Otherwise go up to the parent; continue upward + * as long as you're on the left branch, but stop at the parent + * when you hit it from the right branch. */ + if (n->left) { + n = n->left; + while (n->right) + n = n->right; + return n; + } else { + while (n->parent && n->parent->left == n) + n = n->parent; + return n->parent; + } +} + +/*! + * \brief l_rbtreeGetCount() + * + * \param[in] t rbtree + * \return count the number of nodes in the tree, or 0 on error + */ +l_int32 +l_rbtreeGetCount(L_RBTREE *t) +{ +l_int32 count = 0; +node *n; + + if (!t) return 0; + n = t->root; + count_helper(n, &count); + return count; +} + + /* preorder DFS */ +static void +count_helper(node *n, l_int32 *pcount) +{ + if (n) + (*pcount)++; + else + return; + + count_helper(n->left, pcount); + count_helper(n->right, pcount); +} + + +/*! + * \brief l_rbtreePrint() + * + * \param[in] fp file stream + * \param[in] t rbtree + * \return void + */ +void +l_rbtreePrint(FILE *fp, + L_RBTREE *t) +{ + PROCNAME("l_rbtreePrint"); + if (!fp) { + L_ERROR("stream not defined\n", procName); + return; + } + if (!t) { + L_ERROR("tree not defined\n", procName); + return; + } + + print_tree_helper(fp, t->root, t->keytype, 0); + fprintf(fp, "\n"); +} + +#define INDENT_STEP 4 + +static void +print_tree_helper(FILE *fp, + node *n, + l_int32 keytype, + l_int32 indent) +{ +l_int32 i; + + if (n == NULL) { + fprintf(fp, "<empty tree>"); + return; + } + if (n->right != NULL) { + print_tree_helper(fp, n->right, keytype, indent + INDENT_STEP); + } + for (i = 0; i < indent; i++) + fprintf(fp, " "); + if (n->color == L_BLACK_NODE) { + if (keytype == L_INT_TYPE) + fprintf(fp, "%lld\n", n->key.itype); + else if (keytype == L_UINT_TYPE) + fprintf(fp, "%llx\n", n->key.utype); + else if (keytype == L_FLOAT_TYPE) + fprintf(fp, "%f\n", n->key.ftype); + } else { + if (keytype == L_INT_TYPE) + fprintf(fp, "<%lld>\n", n->key.itype); + else if (keytype == L_UINT_TYPE) + fprintf(fp, "<%llx>\n", n->key.utype); + else if (keytype == L_FLOAT_TYPE) + fprintf(fp, "<%f>\n", n->key.ftype); + } + if (n->left != NULL) { + print_tree_helper(fp, n->left, keytype, indent + INDENT_STEP); + } +} + + +/* ------------------------------------------------------------- * + * Static key comparison function * + * ------------------------------------------------------------- */ +static l_int32 +compareKeys(l_int32 keytype, + RB_TYPE left, + RB_TYPE right) +{ +static char procName[] = "compareKeys"; + + if (keytype == L_INT_TYPE) { + if (left.itype < right.itype) + return -1; + else if (left.itype > right.itype) + return 1; + else { /* equality */ + return 0; + } + } else if (keytype == L_UINT_TYPE) { + if (left.utype < right.utype) + return -1; + else if (left.utype > right.utype) + return 1; + else { /* equality */ + return 0; + } + } else if (keytype == L_FLOAT_TYPE) { + if (left.ftype < right.ftype) + return -1; + else if (left.ftype > right.ftype) + return 1; + else { /* equality */ + return 0; + } + } else { + L_ERROR("unknown keytype %d\n", procName, keytype); + return 0; + } +} + + +/* ------------------------------------------------------------- * + * Static red-black tree helpers * + * ------------------------------------------------------------- */ +static node *grandparent(node *n) { + if (!n || !n->parent || !n->parent->parent) { + L_ERROR("root and child of root have no grandparent\n", "grandparent"); + return NULL; + } + return n->parent->parent; +} + +static node *sibling(node *n) { + if (!n || !n->parent) { + L_ERROR("root has no sibling\n", "sibling"); + return NULL; + } + if (n == n->parent->left) + return n->parent->right; + else + return n->parent->left; +} + +static node *uncle(node *n) { + if (!n || !n->parent || !n->parent->parent) { + L_ERROR("root and child of root have no uncle\n", "uncle"); + return NULL; + } + return sibling(n->parent); +} + +static l_int32 node_color(node *n) { + return n == NULL ? L_BLACK_NODE : n->color; +} + + +static node *new_node(RB_TYPE key, RB_TYPE value, l_int32 node_color, + node *left, node *right) { + node *result = (node *)LEPT_CALLOC(1, sizeof(node)); + result->key = key; + result->value = value; + result->color = node_color; + result->left = left; + result->right = right; + if (left != NULL) left->parent = result; + if (right != NULL) right->parent = result; + result->parent = NULL; + return result; +} + +static node *lookup_node(L_RBTREE *t, RB_TYPE key) { + node *n = t->root; + while (n != NULL) { + int comp_result = compareKeys(t->keytype, key, n->key); + if (comp_result == 0) { + return n; + } else if (comp_result < 0) { + n = n->left; + } else { /* comp_result > 0 */ + n = n->right; + } + } + return n; +} + +static void rotate_left(L_RBTREE *t, node *n) { + node *r = n->right; + replace_node(t, n, r); + n->right = r->left; + if (r->left != NULL) { + r->left->parent = n; + } + r->left = n; + n->parent = r; +} + +static void rotate_right(L_RBTREE *t, node *n) { + node *L = n->left; + replace_node(t, n, L); + n->left = L->right; + if (L->right != NULL) { + L->right->parent = n; + } + L->right = n; + n->parent = L; +} + +static void replace_node(L_RBTREE *t, node *oldn, node *newn) { + if (oldn->parent == NULL) { + t->root = newn; + } else { + if (oldn == oldn->parent->left) + oldn->parent->left = newn; + else + oldn->parent->right = newn; + } + if (newn != NULL) { + newn->parent = oldn->parent; + } +} + +static void insert_case1(L_RBTREE *t, node *n) { + if (n->parent == NULL) + n->color = L_BLACK_NODE; + else + insert_case2(t, n); +} + +static void insert_case2(L_RBTREE *t, node *n) { + if (node_color(n->parent) == L_BLACK_NODE) + return; /* Tree is still valid */ + else + insert_case3(t, n); +} + +static void insert_case3(L_RBTREE *t, node *n) { + if (node_color(uncle(n)) == L_RED_NODE) { + n->parent->color = L_BLACK_NODE; + uncle(n)->color = L_BLACK_NODE; + grandparent(n)->color = L_RED_NODE; + insert_case1(t, grandparent(n)); + } else { + insert_case4(t, n); + } +} + +static void insert_case4(L_RBTREE *t, node *n) { + if (n == n->parent->right && n->parent == grandparent(n)->left) { + rotate_left(t, n->parent); + n = n->left; + } else if (n == n->parent->left && n->parent == grandparent(n)->right) { + rotate_right(t, n->parent); + n = n->right; + } + insert_case5(t, n); +} + +static void insert_case5(L_RBTREE *t, node *n) { + n->parent->color = L_BLACK_NODE; + grandparent(n)->color = L_RED_NODE; + if (n == n->parent->left && n->parent == grandparent(n)->left) { + rotate_right(t, grandparent(n)); + } else if (n == n->parent->right && n->parent == grandparent(n)->right) { + rotate_left(t, grandparent(n)); + } else { + L_ERROR("identity confusion\n", "insert_case5"); + } +} + +static node *maximum_node(node *n) { + if (!n) { + L_ERROR("n not defined\n", "maximum_node"); + return NULL; + } + while (n->right != NULL) { + n = n->right; + } + return n; +} + +static void delete_case1(L_RBTREE *t, node *n) { + if (n->parent == NULL) + return; + else + delete_case2(t, n); +} + +static void delete_case2(L_RBTREE *t, node *n) { + if (node_color(sibling(n)) == L_RED_NODE) { + n->parent->color = L_RED_NODE; + sibling(n)->color = L_BLACK_NODE; + if (n == n->parent->left) + rotate_left(t, n->parent); + else + rotate_right(t, n->parent); + } + delete_case3(t, n); +} + +static void delete_case3(L_RBTREE *t, node *n) { + if (node_color(n->parent) == L_BLACK_NODE && + node_color(sibling(n)) == L_BLACK_NODE && + node_color(sibling(n)->left) == L_BLACK_NODE && + node_color(sibling(n)->right) == L_BLACK_NODE) { + sibling(n)->color = L_RED_NODE; + delete_case1(t, n->parent); + } else { + delete_case4(t, n); + } +} + +static void delete_case4(L_RBTREE *t, node *n) { + if (node_color(n->parent) == L_RED_NODE && + node_color(sibling(n)) == L_BLACK_NODE && + node_color(sibling(n)->left) == L_BLACK_NODE && + node_color(sibling(n)->right) == L_BLACK_NODE) { + sibling(n)->color = L_RED_NODE; + n->parent->color = L_BLACK_NODE; + } else { + delete_case5(t, n); + } +} + +static void delete_case5(L_RBTREE *t, node *n) { + if (n == n->parent->left && + node_color(sibling(n)) == L_BLACK_NODE && + node_color(sibling(n)->left) == L_RED_NODE && + node_color(sibling(n)->right) == L_BLACK_NODE) { + sibling(n)->color = L_RED_NODE; + sibling(n)->left->color = L_BLACK_NODE; + rotate_right(t, sibling(n)); + } else if (n == n->parent->right && + node_color(sibling(n)) == L_BLACK_NODE && + node_color(sibling(n)->right) == L_RED_NODE && + node_color(sibling(n)->left) == L_BLACK_NODE) { + sibling(n)->color = L_RED_NODE; + sibling(n)->right->color = L_BLACK_NODE; + rotate_left(t, sibling(n)); + } + delete_case6(t, n); +} + +static void delete_case6(L_RBTREE *t, node *n) { + sibling(n)->color = node_color(n->parent); + n->parent->color = L_BLACK_NODE; + if (n == n->parent->left) { + if (node_color(sibling(n)->right) != L_RED_NODE) { + L_ERROR("right sibling is not RED", "delete_case6"); + return; + } + sibling(n)->right->color = L_BLACK_NODE; + rotate_left(t, n->parent); + } else { + if (node_color(sibling(n)->left) != L_RED_NODE) { + L_ERROR("left sibling is not RED", "delete_case6"); + return; + } + sibling(n)->left->color = L_BLACK_NODE; + rotate_right(t, n->parent); + } +} + + +/* ------------------------------------------------------------- * + * Debugging: verify if tree is valid * + * ------------------------------------------------------------- */ +#if VERIFY_RBTREE +static void verify_property_1(node *root); +static void verify_property_2(node *root); +static void verify_property_4(node *root); +static void verify_property_5(node *root); +static void verify_property_5_helper(node *n, int black_count, + int* black_count_path); +#endif + +static void verify_properties(L_RBTREE *t) { +#if VERIFY_RBTREE + verify_property_1(t->root); + verify_property_2(t->root); + /* Property 3 is implicit */ + verify_property_4(t->root); + verify_property_5(t->root); +#endif +} + +#if VERIFY_RBTREE +static void verify_property_1(node *n) { + if (node_color(n) != L_RED_NODE && node_color(n) != L_BLACK_NODE) { + L_ERROR("color neither RED nor BLACK\n", "verify_property_1"); + return; + } + if (n == NULL) return; + verify_property_1(n->left); + verify_property_1(n->right); +} + +static void verify_property_2(node *root) { + if (node_color(root) != L_BLACK_NODE) + L_ERROR("root is not black!\n", "verify_property_2"); +} + +static void verify_property_4(node *n) { + if (node_color(n) == L_RED_NODE) { + if (node_color(n->left) != L_BLACK_NODE || + node_color(n->right) != L_BLACK_NODE || + node_color(n->parent) != L_BLACK_NODE) { + L_ERROR("children & parent not all BLACK", "verify_property_4"); + return; + } + } + if (n == NULL) return; + verify_property_4(n->left); + verify_property_4(n->right); +} + +static void verify_property_5(node *root) { + int black_count_path = -1; + verify_property_5_helper(root, 0, &black_count_path); +} + +static void verify_property_5_helper(node *n, int black_count, + int* path_black_count) { + if (node_color(n) == L_BLACK_NODE) { + black_count++; + } + if (n == NULL) { + if (*path_black_count == -1) { + *path_black_count = black_count; + } else if (*path_black_count != black_count) { + L_ERROR("incorrect black count", "verify_property_5_helper"); + } + return; + } + verify_property_5_helper(n->left, black_count, path_black_count); + verify_property_5_helper(n->right, black_count, path_black_count); +} +#endif |