/// <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>
        /// 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>
        /// 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();
        }