redblacktree_invariants.hpp

Back to Red-black tree

pastel/sys/redblacktree/

#ifndef PASTELSYS_REDBLACKTREE_INVARIANTS_HPP
#define PASTELSYS_REDBLACKTREE_INVARIANTS_HPP

#include "pastel/sys/redblacktree.h"
#include "pastel/sys/sequence/pairwise_all_of.h"

#include <iterator>

namespace Pastel
{

    namespace RedBlackTree_
    {

        template <typename Settings, template <typename> class Customization>
        bool testInvariants(
            const RedBlackTree<Settings, Customization>& tree,
            const typename RedBlackTree<Settings, Customization>::ConstIterator& iter,
            integer& blackHeight)
        {
            using Less = typename Settings::Less;

            if (iter.isSentinel())
            {
                if (iter != tree.cbottom() &&
                    iter != tree.cend())
                {
                    // If the node is a sentinel, it must be either
                    // the bottom node or the end node.
                    return false;
                }

                blackHeight = 0;
                return true;
            }

            if (!iter.validPropagation())
            {
                return false;
            }

            if (iter == tree.cbegin() &&
                iter.left() != tree.cend())
            {
                // The minimum node's left child must be
                // the end node.
                return false;
            }

            if (iter != tree.cbegin() && 
                iter.left().isSentinel() &&
                iter.left() != tree.cbottom())
            {
                // All other nodes must denote a missing
                // left child by the bottom node.
                return false;
            }

            if (iter == tree.clast() &&
                iter.right() != tree.cend())
            {
                // The maximum node's right child must be
                // the end node.
                return false;
            }

            if (iter != tree.clast() && 
                iter.right().isSentinel() &&
                iter.right() != tree.cbottom())
            {
                // All other nodes must denote a missing
                // right child by the bottom node.
                return false;
            }

            if (!iter.left().isSentinel() &&
                iter.left().parent() != iter)
            {
                // The parent of the left child must be
                // the node itself, except for the sentinel
                // node.
                return false;
            }

            if (!iter.right().isSentinel() &&
                iter.right().parent() != iter)
            {
                // The parent of the right child must be
                // the node itself, except for the sentinel
                // node.
                return false;
            }

            if (iter.red() && 
                (iter.left().red() || iter.right().red()))
            {
                // No two consecutive reds are allowed
                // on a path.
                return false;
            }

            if (!iter.left().isSentinel())
            {
                if (Settings::MultipleKeys)
                {
                    if (Less()(iter.key(), iter.left().key()))
                    {
                        // In the case multiple keys are allowed,
                        // the key on the left must be <= 
                        // the current key.
                        return false;
                    }
                }
                else if (!Less()(iter.left().key(), iter.key()))
                {
                    // In the case of multiple keys are not allowed,
                    // the key on the left must be < the current key.
                    return false;
                }
            }

            if (!iter.right().isSentinel() &&
                Less()(iter.right().key(), iter.key()))
            {
                // The key on the right must be
                // greater-than-or-equal-to the 
                // current key.
                return false;
            }

            integer leftBlackHeight = blackHeight;
            if (!testInvariants(tree, iter.left(), leftBlackHeight))
            {
                return false;
            }

            integer rightBlackHeight = blackHeight;
            if (!testInvariants(tree, iter.right(), rightBlackHeight))
            {
                return false;
            }

            if (leftBlackHeight != rightBlackHeight)
            {
                // The black heights must equal on every
                // path.
                return false;
            }

            blackHeight = leftBlackHeight;

            if (iter.black())
            {
                ++blackHeight;
            }

            return true;
        }

    }

    template <typename Settings, template <typename> class Customization>
    bool testInvariants(const RedBlackTree<Settings, Customization>& tree)
    {
        if (!tree.croot().black())
        {
            // The root node must be black. We maintain
            // this invariant, although this is not part
            // of our definition of a red-black tree.
            return false;
        }

        if (tree.clast().right() != tree.cend())
        {
            // The right child of the  maximum node must be
            // the end node.
            return false;
        }

        if (std::next(tree.cend()) != tree.cbegin())
        {
            // The next node from the end-node must be 
            // the first element.
            return false;
        }

        if (std::next(tree.clast()) != tree.cend())
        {
            // The next element from the last element
            // must be the end-node.
            return false;
        }

        if (tree.cbegin().left() != tree.cend())
        {
            // The left child of the the minimum node must be 
            // the end node.
            return false;
        }

        if (std::prev(tree.cend()) != tree.clast())
        {
            // The previous element from the end-node must
            // be the last element.
            return false;
        }

        if (std::prev(tree.cbegin()) != tree.cend())
        {
            // The previous element from the first element
            // must be the end-node.
            return false;
        }

        if (tree.cend().left() != tree.cend())
        {
            // The left child of the end node must be itself.
            return false;
        }

        using Fwd = Settings;
        PASTEL_FWD(Less);

        if (Settings::MultipleKeys &&
            !pairwiseAllOf(tree.crange().dereferenceKey(),
            notPredicate(transposePredicate(Less()))))
        {
            // The keys must be in sorted order.
            return false;
        }

        if (!Settings::MultipleKeys && 
            !pairwiseAllOf(tree.crange().dereferenceKey(), Less()))
        {
            // The keys must be unique when multiple keys are not allowed.
            return false;
        }

        if (std::distance(tree.cbegin(), tree.cend()) != tree.size())
        {
            // The size() must equal the number of elements in the tree.
            return false;
        }

        integer blackHeight = 0;
        if (!RedBlackTree_::testInvariants(tree, tree.croot(), blackHeight))
        {
            return false;
        }

        if (blackHeight != tree.blackHeight())
        {
            // The computed black-height must be equal to the
            // black-height which is incrementally tracked by the tree.
            return false;
        }

        return true;
    }

}

#endif