/// <summary>
        /// Rotate the subtree right around the given node, as per the mirror of the
        /// LEFT-ROTATE algorithm in [1].  O(1).
        /// </summary>
        /// <param name="node">The node (x) to rotate around.</param>
        private void RotateRight(WeightedRedBlackTreeNode <K, V> node)
        {
            WeightedRedBlackTreeNode <K, V> child = node.Left;

            node.Left = child.Right;

            if (child.Right != null)
            {
                child.Right.Parent = node;
            }

            child.Parent = node.Parent;

            if (node.Parent == null)
            {
                Root = child;
            }
            else if (node.Parent.Right == node)
            {
                node.Parent.Right = child;
            }
            else
            {
                node.Parent.Left = child;
            }

            child.Right  = node;
            node.Parent  = child;
            child.Weight = node.Weight;
            node.Weight  = (node.Left?.Weight ?? 0) + (node.Right?.Weight ?? 0) + 1;
        }
        /// <summary>
        /// Search the tree for the given key.  If it does not exist, create a new node
        /// with the given key and value; but if it does exist, do nothing, and simply
        /// return the preexisting node. O(lg n).
        /// </summary>
        /// <param name="key">The key to search for or create.</param>
        /// <param name="value">The value to assign to that key, if and only if is node is
        /// newly-created.</param>
        /// <param name="resultingNode">The resulting node, either found or created.</param>
        /// <returns>True if the node was created, false if the node was found.  If the node
        /// was found, not created, the node will *not* be altered by this method.</returns>
        public override bool FindOrInsert(K key, V value, out WeightedRedBlackTreeNode <K, V> resultingNode)
        {
            int cmp = 0;
            WeightedRedBlackTreeNode <K, V> node = Root, parent = null;

            // Search for it, keeping track of any parents we pass through.
            while (node != null)
            {
                if ((cmp = Compare(key, node.Key)) == 0)
                {
                    resultingNode = node;
                    return(false);
                }
                parent = node;
                node   = cmp < 0 ? node.Left : node.Right;
            }

            // Didn't find it, so create it and attach it to its new parent.
            node = new WeightedRedBlackTreeNode <K, V>(key, value);
            if (parent == null)
            {
                Root = node;
            }
            else if (cmp > 0)
            {
                parent.Right = node;
            }
            else
            {
                parent.Left = node;
            }
            node.Parent = parent;

            // Increase the weights up the tree.
            if (parent != null)
            {
                for (WeightedRedBlackTreeNode <K, V> temp = parent; temp != null; temp = temp.Parent)
                {
                    temp.Weight++;
                }
            }

            // Rotate the tree to keep it balanced.
            InsertFixup(node);

            // And return the new node.
            resultingNode = node;
            return(true);
        }
        /// <summary>
        /// Find the node whose key matches the given key. O(lg n).
        /// </summary>
        /// <param name="key">The key to search for.</param>
        /// <returns>The matching node, or null if no node matches.</returns>
        public override WeightedRedBlackTreeNode <K, V> Find(K key)
        {
            int cmp;

            for (WeightedRedBlackTreeNode <K, V> node = Root; node != null;
                 node = cmp < 0 ? node.Left : node.Right)
            {
                if ((cmp = Compare(key, node.Key)) == 0)
                {
                    return(node);
                }
            }

            return(null);
        }
        /// <summary>
        /// Validate the correctness of the red-black tree according to the four
        /// red-black properties [1]:
        ///
        /// 1. Every node is either red or black.
        /// 2. Every leaf (null) is black.
        /// 3. If a node is red, then both its children are black.
        /// 4. Every simple path from a node to a descendant leaf contains the
        ///    same number of black nodes.
        ///
        /// In addition, we also test that:
        ///
        /// 5. Only the Root node may have a null Parent pointer.
        /// 6. For all non-null Left and Right pointers, Left.Parent == parent,
        ///     and Right.Parent == parent.
        /// 7. The weight of each node equals the weight of its Left child plus
        ///     the weight of its Right child plus one.
        ///
        /// The first three are trivial to test.  Properties 5 and 6 are also
        /// easy, and ensure that the tree is a valid binary search tree.  Property
        /// 4 is the only one that's interesting; to test it, we recursively walk
        /// the tree inorder after everything else has been checked, and compare
        /// the count of black nodes each time we reach 'null' against the count of
        /// black nodes from a strict walk of Left pointers.
        /// </summary>
        /// <throws cref="InvalidOperationException">Thrown if the tree is invalid
        /// or damaged.</throws>
        public override void Validate()
        {
            void Assert(bool result)
            {
                if (!result)
                {
                    throw new InvalidOperationException("Tree is invalid.");
                }
            }

            // Test #2. Every leaf (null) is black.
            Assert(((WeightedRedBlackTreeNode <K, V>)null).IsBlack());
            Assert(!((WeightedRedBlackTreeNode <K, V>)null).IsRed());

            // Count up the black nodes in a simple path from root to leaf (null).
            int expectedBlackCount = 0;

            for (WeightedRedBlackTreeNode <K, V> node = Root; node != null; node = node.Left)
            {
                if (node.IsBlack())
                {
                    expectedBlackCount++;
                }
            }
            expectedBlackCount++;                       // Count up one more for the null.

            // Recursively walk the entire tree, validating each node.
            void RecursivelyValidate(WeightedRedBlackTreeNode <K, V> node, int currentBlackCount)
            {
                // Count up an additional black node in this path, if this node is black.
                if (node.IsBlack())
                {
                    currentBlackCount++;
                }

                // Test #1. Every node is either red or black.
                Assert(node.IsRed() || node.IsBlack());

                // Test #3. If a node is red, then both its children are black.
                if (node.IsRed())
                {
                    Assert(node.Left.IsBlack());
                    Assert(node.Right.IsBlack());
                }

                // Test #5. Only the Root node may have a null Parent pointer.
                if (node != null && node.Parent == null)
                {
                    Assert(node == Root);
                }

                // Test #6. For all non-null Left and Right pointers,
                //     Left.Parent == parent, and Right.Parent == parent.
                if (node != null && node.Left != null)
                {
                    Assert(node.Left.Parent == node);
                }
                if (node != null && node.Right != null)
                {
                    Assert(node.Right.Parent == node);
                }

                // Test #4. Every simple path from a node to a descendant leaf
                // contains the same number of black nodes.
                if (currentBlackCount == expectedBlackCount)
                {
                    Assert(node == null);
                }

                if (node != null)
                {
                    // Test #7. The weight of each node equals the weight of its Left child plus
                    // the weight of its Right child plus one.
                    int expectedWeight = (node.Left?.Weight ?? 0) + (node.Right?.Weight ?? 0) + 1;
                    Assert(node.Weight == expectedWeight);
                }

                // Recurse to test the rest of the tree.
                if (node != null)
                {
                    RecursivelyValidate(node.Left, currentBlackCount);
                    RecursivelyValidate(node.Right, currentBlackCount);
                }
            }

            // Recursively check the entire tree, starting at the root.
            RecursivelyValidate(Root, 0);
        }
        /// <summary>
        /// Perform rebalancing on the red-black tree for a deletion.  O(lg n).
        /// If we broke the tree by moving a black node, we need to reorder
        /// and/or recolor part of it.  This is not pretty, and is basically
        /// derived from the algorithm in Cormen/Leiserson/Rivest, just like
        /// every other red-black tree implementation.  We have, however, gone
        /// to the necessary work to drop out the required sentinel value in
        /// their version, so this is a little more flexible.
        /// </summary>
        /// <param name="child">The child that was removed.</param>
        /// <param name="parent">The parent it was removed from.</param>
        /// <param name="rightSide">Which side of the parent it was removed from.</param>
        private void DetachFixup(WeightedRedBlackTreeNode <K, V> child, WeightedRedBlackTreeNode <K, V> parent, bool rightSide)
        {
            while (child != Root && child.IsBlack())
            {
                if (!rightSide)
                {
                    WeightedRedBlackTreeNode <K, V> other = parent.Right;
                    if (other.IsRed())
                    {
                        other.MakeNodeBlack();
                        parent.MakeNodeRed();
                        RotateLeft(parent);
                        other = parent.Right;
                    }
                    if (other == null ||
                        (other.Left.IsBlack() &&
                         other.Right.IsBlack()))
                    {
                        other.MakeNodeRed();
                        child = parent;
                    }
                    else
                    {
                        if (other.Right.IsBlack())
                        {
                            other.Left.MakeNodeBlack();
                            other.MakeNodeRed();
                            RotateRight(other);
                            other = parent.Right;
                        }
                        if (parent.IsRed())
                        {
                            other.MakeNodeRed();
                        }
                        else
                        {
                            other.MakeNodeBlack();
                        }
                        parent.MakeNodeBlack();
                        other.Right.MakeNodeBlack();
                        RotateLeft(parent);
                        child = Root;
                    }
                }
                else
                {
                    WeightedRedBlackTreeNode <K, V> other = parent.Left;
                    if (other.IsRed())
                    {
                        other.MakeNodeBlack();
                        parent.MakeNodeRed();
                        RotateRight(parent);
                        other = parent.Left;
                    }
                    if (other == null ||
                        (other.Right.IsBlack() &&
                         other.Left.IsBlack()))
                    {
                        other.MakeNodeRed();
                        child = parent;
                    }
                    else
                    {
                        if (other.Left.IsBlack())
                        {
                            other.Right.MakeNodeBlack();
                            other.MakeNodeRed();
                            RotateLeft(other);
                            other = parent.Left;
                        }
                        if (parent.IsRed())
                        {
                            other.MakeNodeRed();
                        }
                        else
                        {
                            other.MakeNodeBlack();
                        }
                        parent.MakeNodeBlack();
                        other.Left.MakeNodeBlack();
                        RotateRight(parent);
                        child = Root;
                    }
                }
                parent = child.Parent;
                if (child != Root)
                {
                    rightSide = (child == parent.Right);
                }
            }

            child.MakeNodeBlack();
        }
        /// <summary>
        /// Delete (detach) a node from the given tree.
        ///
        /// This uses standard red-black deletion, with auxiliary routine to
        /// reorder and recolor nodes if necessary.  However, unlike most RB delete
        /// routines, we do this (A) without sentinel nodes and (B) by only changing
        /// pointers and colors --- we never copy the data, so the pointers of the
        /// tree remain valid.  Still O(lg n).
        /// </summary>
        /// <param name="node"></param>
        public override void DeleteNode(WeightedRedBlackTreeNode <K, V> node)
        {
            if (node == null || Root == null)
            {
                return;
            }

            WeightedRedBlackTreeNode <K, V> next;              // Will take the place of "atom"
            WeightedRedBlackTreeNode <K, V> child;             // First non-null child of "next", if any
            WeightedRedBlackTreeNode <K, V> parent;            // The supposed parent of "child"
            bool rightSide;                                    // Which side of the parent "child" is on
            bool needFixup;                                    // Do we need to call fixup()?

            if (node.Left == null || node.Right == null)
            {
                next = node;
            }
            else
            {
                next = node.Next();
            }

            child  = (next.Left != null ? next.Left : next.Right);
            parent = next.Parent;

            if (child != null)
            {
                child.Parent = parent;
            }

            if (parent == null)
            {
                Root      = child;
                rightSide = false;
            }
            else
            {
                if (next == parent.Left)
                {
                    parent.Left = child;
                    rightSide   = false;
                }
                else
                {
                    parent.Right = child;
                    rightSide    = true;
                }

                for (WeightedRedBlackTreeNode <K, V> temp = parent; temp != null; temp = temp.Parent)
                {
                    temp.Weight = (temp.Left?.Weight ?? 0) + (temp.Right?.Weight ?? 0) + 1;
                }
            }

            needFixup = next.IsBlack();

            if (node != next)
            {
                // Surreptitiously, node.contents <- next.contents by
                // just changing around a lot of pointers.

                if (parent == node)
                {
                    parent = next;                          // Special case
                }
                next.Parent = node.Parent;
                next.Left   = node.Left;
                next.Right  = node.Right;

                if (node.Parent == null)
                {
                    Root = next;
                }
                else if (node == node.Parent.Left)
                {
                    node.Parent.Left = next;
                }
                else
                {
                    node.Parent.Right = next;
                }

                if (next.Left != null)
                {
                    next.Left.Parent = next;
                }
                if (next.Right != null)
                {
                    next.Right.Parent = next;
                }

                if (node.IsBlack())
                {
                    next.MakeNodeBlack();
                }
                else
                {
                    next.MakeNodeRed();
                }

                int temp = node.Weight;
                node.Weight = next.Weight;
                next.Weight = temp;
            }

            // Nulling the pointers on the node ensures that any active enumerations
            // or traversals that still reference this node will stop.
            node.Left = node.Right = node.Parent = null;

            if (needFixup)
            {
                DetachFixup(child, parent, rightSide);
            }
        }
        /// <summary>
        /// This corrects the tree for a newly-inserted node, rotating it as
        /// necessary to keep it balanced.  The algorithm is the same as RB-INSERT in [1],
        /// minus the first line that performs TREE-INSERT. O(lg n).
        /// </summary>
        /// <param name="node">The newly-inserted node.</param>
        protected void InsertFixup(WeightedRedBlackTreeNode <K, V> node)
        {
            node.MakeNodeRed();
            WeightedRedBlackTreeNode <K, V> parent = node.Parent;

            while (node != Root && parent.IsRed())
            {
                if (parent == parent.Parent.Left)
                {
                    WeightedRedBlackTreeNode <K, V> uncle = parent.Parent.Right;
                    if (uncle.IsRed())
                    {
                        parent.MakeNodeBlack();
                        uncle.MakeNodeBlack();
                        parent.Parent.MakeNodeRed();
                        node   = parent.Parent;
                        parent = node.Parent;
                    }
                    else
                    {
                        if (node == parent.Right)
                        {
                            node = parent;
                            RotateLeft(node);
                            parent = node.Parent;
                        }
                        parent.MakeNodeBlack();
                        parent.Parent.MakeNodeRed();
                        RotateRight(parent.Parent);
                        parent = node.Parent;
                    }
                }
                else
                {
                    WeightedRedBlackTreeNode <K, V> uncle = parent.Parent.Left;
                    if (uncle.IsRed())
                    {
                        parent.MakeNodeBlack();
                        uncle.MakeNodeBlack();
                        parent.Parent.MakeNodeRed();
                        node   = parent.Parent;
                        parent = node.Parent;
                    }
                    else
                    {
                        if (node == parent.Left)
                        {
                            node = parent;
                            RotateRight(node);
                            parent = node.Parent;
                        }
                        parent.MakeNodeBlack();
                        parent.Parent.MakeNodeRed();
                        RotateLeft(parent.Parent);
                        parent = node.Parent;
                    }
                }
            }

            Root.MakeNodeBlack();
        }