diff --git a/src/tree/vieter_tree.c b/src/tree/vieter_tree.c index 72360b0..792ad30 100644 --- a/src/tree/vieter_tree.c +++ b/src/tree/vieter_tree.c @@ -1,4 +1,4 @@ -#include "vieter_tree.h" +#include "vieter_tree_internal.h" #include "vieter_tree_node.h" struct vieter_tree { @@ -98,3 +98,23 @@ void vieter_tree_free(vieter_tree *tree) { end: free(tree); } + +int vieter_tree_validate(vieter_tree *tree) { + if (tree->size == 0) { + return 0; + } + + // DFS to get expected black nodes + uint64_t expected_black_nodes = 0; + vieter_tree_node *node = tree->root; + + while (node != NULL) { + if (vieter_tree_node_get(node, vieter_node_black)) { + expected_black_nodes++; + } + + node = node->left; + } + + return vieter_tree_node_validate(tree->root, 0, expected_black_nodes); +} diff --git a/src/tree/vieter_tree_balancing.c b/src/tree/vieter_tree_balancing.c index 56047a0..70c52e0 100644 --- a/src/tree/vieter_tree_balancing.c +++ b/src/tree/vieter_tree_balancing.c @@ -1,5 +1,43 @@ #include "vieter_tree_balancing.h" +int vieter_tree_node_validate(vieter_tree_node *node, + uint64_t passed_black_nodes, + uint64_t expected_black_nodes) { + if (vieter_tree_node_get(node, vieter_node_black)) { + passed_black_nodes++; + } else { + // A red node should have black child nodes + if ((node->left != NULL && + !vieter_tree_node_get(node->left, vieter_node_black)) || + (node->right != NULL && + !vieter_tree_node_get(node->right, vieter_node_black))) { + return 1; + } + } + + // All paths to a NULL child should have the same amount of black nodes + if ((node->left == NULL || node->right == NULL) && + passed_black_nodes != expected_black_nodes) { + return 1; + } + + if (node->left != NULL) { + if (vieter_tree_node_validate(node->left, passed_black_nodes, + expected_black_nodes) != 0) { + return 1; + } + } + + if (node->right != NULL) { + if (vieter_tree_node_validate(node->right, passed_black_nodes, + expected_black_nodes) != 0) { + return 1; + } + } + + return 0; +} + vieter_tree_node *vieter_tree_node_balance(vieter_tree_node *node) { vieter_tree_node *parent = node->parent; vieter_tree_node *grand_parent = parent->parent; diff --git a/src/tree/vieter_tree_internal.h b/src/tree/vieter_tree_internal.h new file mode 100644 index 0000000..dc4ee81 --- /dev/null +++ b/src/tree/vieter_tree_internal.h @@ -0,0 +1,8 @@ +#include "vieter_tree.h" + +/* + * Inspect whether the tree is still a valid red-black-tree. + * + * @return 0 if correct, 1 if not. + */ +int vieter_tree_validate(vieter_tree *tree); diff --git a/src/tree/vieter_tree_node.h b/src/tree/vieter_tree_node.h index 522ee59..1c88159 100644 --- a/src/tree/vieter_tree_node.h +++ b/src/tree/vieter_tree_node.h @@ -40,4 +40,6 @@ bool vieter_tree_node_get(vieter_tree_node *node, vieter_node_flag flag); void vieter_tree_node_add_child(vieter_tree_node *parent, uint64_t key, vieter_tree_node *child); +int vieter_tree_node_validate(vieter_tree_node *node, uint64_t passed_black_nodes, uint64_t expected_black_nodes); + #endif diff --git a/test/tree/test_tree.c b/test/tree/test_tree.c index d0ea661..c875137 100644 --- a/test/tree/test_tree.c +++ b/test/tree/test_tree.c @@ -1,5 +1,5 @@ #include "acutest.h" -#include "vieter_tree.h" +#include "vieter_tree_internal.h" #define TEST_SIZE(tree, size) \ TEST_CHECK(vieter_tree_size(tree) == size); \ @@ -18,6 +18,7 @@ void test_insert() { for (uint64_t i = 0; i < 250; i++) { TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok); TEST_SIZE(tree, i + 1); + TEST_CHECK(vieter_tree_validate(tree) == 0); } void *out; @@ -36,13 +37,15 @@ void test_remove() { for (uint64_t i = 0; i < 250; i++) { TEST_CHECK(vieter_tree_insert(tree, i, NULL) == vieter_tree_ok); + TEST_CHECK(vieter_tree_validate(tree) == 0); } void *out; - for (uint64_t i = 0; i < 250; i++) { + for (uint64_t i = 0; i < 25; i++) { TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_ok); TEST_CHECK(vieter_tree_remove(&out, tree, i) == vieter_tree_ok); + TEST_CHECK(vieter_tree_validate(tree) == 0); TEST_CHECK(vieter_tree_search(&out, tree, i) == vieter_tree_not_present); TEST_SIZE(tree, 250 - i - 1); }