/// <summary>
        /// Adds an element with the specified key and value into the <see cref="BinarySearchTreeMap{TKey, TValue}"/>.
        /// </summary>
        /// <param name="key">The key of the element to add.</param>
        /// <param name="value">The value of the element to add.</param>
        public virtual void Add(TKey key, TValue value)
        {
            if (Root == null)
            {
                Root = new BinarySearchTreeMapNode <TKey, TValue>(key, value);
                Count++;
                return;
            }

            var  curNode         = Root;
            var  lastNode        = Root;
            bool addedToLeftSide = true;

            while (curNode != null)
            {
                int cmp = comparer.Compare(key, curNode.Key);

                if (cmp < 0)
                {
                    lastNode        = curNode;
                    curNode         = curNode.Left;
                    addedToLeftSide = true;
                }
                else if (cmp > 0)
                {
                    lastNode        = curNode;
                    curNode         = curNode.Right;
                    addedToLeftSide = false;
                }
                else
                {
                    throw new ArgumentException("Tried to insert duplicate key!");
                }
            }

            if (addedToLeftSide)
            {
                lastNode.Left = new BinarySearchTreeMapNode <TKey, TValue>(key, value);
                Count++;
            }
            else
            {
                lastNode.Right = new BinarySearchTreeMapNode <TKey, TValue>(key, value);
                Count++;
            }
        }
        /// <summary>
        /// Determines whether an element with the specified value is contained in the <see cref="BinarySearchTreeMap{TKey, TValue}"/>.
        /// </summary>
        /// <param name="value">The value to check.</param>
        /// <returns>true if value is found; otherwise false.</returns>
        public virtual bool ContainsValue(TValue value)
        {
            if (Root == null)
            {
                return(false);
            }

            HashSet <BinarySearchTreeMapNode <TKey, TValue> > cleared = new HashSet <BinarySearchTreeMapNode <TKey, TValue> >();
            Stack <BinarySearchTreeMapNode <TKey, TValue> >   stack   = new Stack <BinarySearchTreeMapNode <TKey, TValue> >();

            stack.Push(Root);
            while (stack.Count > 0)
            {
                BinarySearchTreeMapNode <TKey, TValue> curNode = stack.Peek();

                if (curNode.Left == null || cleared.Contains(curNode.Left))
                {
                    cleared.Add(curNode);
                    stack.Pop();

                    if (object.Equals(value, curNode.Value))
                    {
                        return(true);
                    }

                    if (curNode.Right != null)
                    {
                        stack.Push(curNode.Right);
                    }
                }
                else
                {
                    stack.Push(curNode.Left);
                }
            }

            return(false);
        }
        /// <summary>
        /// Gets or sets the value associated with the specified key in the <see cref="BinarySearchTreeMap{TKey, TValue}"/>.
        /// </summary>
        /// <param name="key">The key of the value to get or set.</param>
        /// <returns>The value associated with the specified key. If the specified key is not found,
        /// the get operation throws a <see cref="KeyNotFoundException"/>, and
        /// the set operation creates a new element with the specified key.</returns>
        public virtual TValue this[TKey key]
        {
            get
            {
                TValue value;
                if (TryGetValue(key, out value))
                {
                    return(value);
                }
                else
                {
                    throw new KeyNotFoundException();
                }
            }
            set
            {
                if (Root == null)
                {
                    Root = new BinarySearchTreeMapNode <TKey, TValue>(key, value);
                    Count++;
                    return;
                }

                var  curNode         = Root;
                var  lastNode        = Root;
                bool addedToLeftSide = true;

                while (curNode != null)
                {
                    int cmp = comparer.Compare(key, curNode.Key);

                    if (cmp < 0)
                    {
                        lastNode        = curNode;
                        curNode         = curNode.Left;
                        addedToLeftSide = true;
                    }
                    else if (cmp > 0)
                    {
                        lastNode        = curNode;
                        curNode         = curNode.Right;
                        addedToLeftSide = false;
                    }
                    else // found the key
                    {
                        curNode.Value = value;
                        return;
                    }
                }

                if (addedToLeftSide)
                {
                    lastNode.Left = new BinarySearchTreeMapNode <TKey, TValue>(key, value);
                    Count++;
                }
                else
                {
                    lastNode.Right = new BinarySearchTreeMapNode <TKey, TValue>(key, value);
                    Count++;
                }
            }
        }
        /// <summary>
        /// Finds the min node in the subtree and returns the root of the new subtree
        /// </summary>
        private BinarySearchTreeMapNode <TKey, TValue> FindAndRemoveMin(BinarySearchTreeMapNode <TKey, TValue> subtreeRoot, ref BinarySearchTreeMapNode <TKey, TValue> min)
        {
            if (subtreeRoot.Left == null)
            {
                if (subtreeRoot.Right == null)
                {
                    min = subtreeRoot;
                    return(null);
                }
                else
                {
                    min = subtreeRoot;
                    return(subtreeRoot.Right);
                }
            }

            var curNode  = subtreeRoot;
            var lastNode = subtreeRoot;

            while (curNode.Left != null)
            {
                lastNode = curNode;
                curNode  = curNode.Left;
            }

            lastNode.Left = curNode.Right;
            min           = curNode;

            return(subtreeRoot);
        }
        /// <summary>
        /// Removes an element with the specified key from the <see cref="BinarySearchTreeMap{TKey, TValue}"/>.
        /// </summary>
        /// <param name="key">The key of the element to remove.</param>
        /// <returns>true if the element is successfully removed; otherwise false. Also returns false if element is not found.</returns>
        public virtual bool Remove(TKey key)
        {
            if (Root == null)
            {
                return(false);
            }

            var  curNode         = Root;
            var  lastNode        = Root;
            bool lastWasLeftSide = true;

            while (curNode != null)
            {
                int cmp = comparer.Compare(key, curNode.Key);

                if (cmp < 0)
                {
                    lastNode        = curNode;
                    curNode         = curNode.Left;
                    lastWasLeftSide = true;
                }
                else if (cmp > 0)
                {
                    lastNode        = curNode;
                    curNode         = curNode.Right;
                    lastWasLeftSide = false;
                }
                else
                {
                    if (curNode.Right == null)
                    {
                        if (lastWasLeftSide)
                        {
                            if (curNode == Root)
                            {
                                Root = curNode.Left;
                            }
                            else
                            {
                                lastNode.Left = curNode.Left;
                            }
                        }
                        else
                        {
                            lastNode.Right = curNode.Left;
                        }
                    }
                    else
                    {
                        BinarySearchTreeMapNode <TKey, TValue> min = null;
                        var rightNode = FindAndRemoveMin(curNode.Right, ref min);
                        min.Right = rightNode;
                        min.Left  = curNode.Left;

                        if (lastWasLeftSide)
                        {
                            if (curNode == Root)
                            {
                                Root = min;
                            }
                            else
                            {
                                lastNode.Left = min;
                            }
                        }
                        else
                        {
                            lastNode.Right = min;
                        }
                    }

                    curNode.Invalidate();
                    Count--;
                    return(true);
                }
            }
            return(false);
        }