]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[2750] Add methods to check RB tree properties (and use them in tests)
authorMukund Sivaraman <muks@isc.org>
Tue, 3 Sep 2013 03:07:46 +0000 (08:37 +0530)
committerMukund Sivaraman <muks@isc.org>
Tue, 3 Sep 2013 03:07:46 +0000 (08:37 +0530)
src/lib/datasrc/memory/domaintree.h
src/lib/datasrc/tests/memory/domaintree_unittest.cc

index 713f703c3fd4758779670a9e18aca41ff8ac534a..776603aca542192c61d3aca92dffc1e62959650d 100644 (file)
@@ -362,7 +362,7 @@ private:
     }
 
     /// \brief Static variant of isBlack() that also allows NULL nodes.
-    static bool isBlack(DomainTreeNode<T>* node) {
+    static bool isBlack(const DomainTreeNode<T>* node) {
         if (!node) {
             // NULL nodes are black.
             return (true);
@@ -377,7 +377,7 @@ private:
     }
 
     /// \brief Static variant of isRed() that also allows NULL nodes.
-    static bool isRed(DomainTreeNode<T>* node) {
+    static bool isRed(const DomainTreeNode<T>* node) {
         return (!isBlack(node));
     }
 
@@ -1610,6 +1610,22 @@ public:
     /// must not use it.
     size_t getHeight() const;
 
+private:
+    /// \brief Helper method for checkProperties()
+    bool checkPropertiesHelper(const DomainTreeNode<T>* node) const;
+
+    /// \brief Helper for checkProperties()
+    bool checkBlackDistanceHelper(const DomainTreeNode<T>* node,
+                                  size_t* distance)
+        const;
+
+public:
+    /// \brief Check red-black properties of the DomainTree.
+    ///
+    /// Note: This method exists for testing purposes. Non-test code
+    /// must not use it.
+    bool checkProperties() const;
+
     /// \name Debug function
     //@{
     /// \brief Print the nodes in the trees.
@@ -3042,6 +3058,82 @@ DomainTree<T>::getHeight() const {
     return (getHeightHelper(root_.get()));
 }
 
+template <typename T>
+bool
+DomainTree<T>::checkPropertiesHelper(const DomainTreeNode<T>* node) const {
+    if (node == NULL) {
+        return (true);
+    }
+
+    // Root nodes should be BLACK.
+    if (node->isSubTreeRoot() && node->isRed()) {
+        return (false);
+    }
+
+    // Both children of RED nodes must be BLACK.
+    if (node->isRed()) {
+        if (DomainTreeNode<T>::isRed(node->getLeft()) ||
+            DomainTreeNode<T>::isRed(node->getRight()))
+        {
+            return (false);
+        }
+    }
+
+    // Repeat tests with this node's children.
+    return (checkPropertiesHelper(node->getLeft()) &&
+            checkPropertiesHelper(node->getRight()) &&
+            checkPropertiesHelper(node->getDown()));
+}
+
+template <typename T>
+bool
+DomainTree<T>::checkBlackDistanceHelper(const DomainTreeNode<T>* node,
+                                        size_t* distance) const
+{
+    if (node == NULL) {
+        *distance = 1;
+        return (true);
+    }
+
+    size_t dl, dr, dd;
+    if (!checkBlackDistanceHelper(node->getLeft(), &dl)) {
+        return (false);
+    }
+    if (!checkBlackDistanceHelper(node->getRight(), &dr)) {
+        return (false);
+    }
+    if (!checkBlackDistanceHelper(node->getDown(), &dd)) {
+        return (false);
+    }
+
+    if (dl != dr) {
+        return (false);
+    }
+
+    if (node->isBlack()) {
+        ++dl;
+    }
+
+    *distance = dl;
+
+    return (true);
+}
+
+template <typename T>
+bool
+DomainTree<T>::checkProperties() const {
+    if (!checkPropertiesHelper(root_.get())) {
+        return (false);
+    }
+
+    // Path from a given node to all its leaves must contain the same
+    // number of BLACK child nodes. This is done separately here instead
+    // of inside checkPropertiesHelper() as it would take (n log n)
+    // complexity otherwise.
+    size_t dd;
+    return (checkBlackDistanceHelper(root_.get(), &dd));
+}
+
 template <typename T>
 void
 DomainTree<T>::dumpTree(std::ostream& os, unsigned int depth) const {
index 5a8b6edea0a62183cb9338bf98f485edd689b94f..6058e44989a3c0eceb71b72d269f98f3236af5c3 100644 (file)
@@ -234,6 +234,9 @@ TEST_F(DomainTreeTest, checkDistanceRandom) {
     // The distance from each node to its sub-tree root must be less
     // than 2 * log(n).
     EXPECT_GE(2 * log_num_nodes, mytree.getHeight());
+
+    // Also check RB tree properties
+    EXPECT_TRUE(mytree.checkProperties());
 }
 
 TEST_F(DomainTreeTest, checkDistanceSorted) {
@@ -265,6 +268,9 @@ TEST_F(DomainTreeTest, checkDistanceSorted) {
     // The distance from each node to its sub-tree root must be less
     // than 2 * log(n).
     EXPECT_GE(2 * log_num_nodes, mytree.getHeight());
+
+    // Also check RB tree properties
+    EXPECT_TRUE(mytree.checkProperties());
 }
 
 TEST_F(DomainTreeTest, setGetData) {
@@ -396,6 +402,9 @@ TEST_F(DomainTreeTest, remove) {
                   tree.find(Name(ordered_names[j]), &node));
         tree.remove(mem_sgmt_, node, deleteData);
 
+        // Check RB tree properties
+        EXPECT_TRUE(tree.checkProperties());
+
         // Now, walk through nodes in order.
         TestDomainTreeNodeChain node_path;
         const TestDomainTreeNode* cnode;