private void InsertNode(IBSTNode currNode, IBSTNode node)
        {
            // neither current node nor node can be null
            if (currNode == null)
            {
                throw new ArgumentNullException("currNode");
            }
            else if (node == null)
            {
                throw new ArgumentNullException("node");
            }

            // if current node is a leaf, convert it to internal and repeat process
            if (currNode.isLeaf())
            {
                if (currNode.isRoot())
                {
                    // current node is root and leaf
                    // convert root to internal node (no parent reference)
                    this._root = new BSTInternalNode(this._root.Key, this._root.Value);
                    // add node to it
                    this.InsertNode(this._root, node);
                }
                else
                {
                    // unbox parent
                    BSTInternalNode parent = (BSTInternalNode)currNode.Parent;

                    // create new internal
                    BSTInternalNode newIntNode = new BSTInternalNode(currNode.Key, currNode.Value, parent);

                    // add child to right side of the tree
                    if (parent.Left == currNode)
                    {
                        parent.Left = newIntNode;
                    }
                    else
                    {
                        parent.Right = newIntNode;
                    }

                    // add node to it
                    this.InsertNode(newIntNode, node);
                }
            }
            else
            {
                // unbox current node
                BSTInternalNode currIntNode = (BSTInternalNode)currNode;

                if (node.Key > currIntNode.Key)
                {
                    // check if left child is empty
                    if (currIntNode.Left == null)
                    {
                        // location found
                        currIntNode.Left = node;
                        // add parent reference
                        node.Parent = currIntNode;
                    }
                    else
                    {
                        // curent node is left
                        this.InsertNode(currIntNode.Left, node);
                    }
                }
                else
                {
                    // check if right child is empty
                    if (currIntNode.Right == null)
                    {
                        // location found
                        currIntNode.Right = node;
                        // add parent reference
                        node.Parent = currIntNode;
                    }
                    else
                    {
                        // curent node is right
                        this.InsertNode(currIntNode.Right, node);
                    }
                }
            }
        }
        /// <summary>
        /// Removes element associated to the key argument.
        /// </summary>
        /// <param name="key">The key of the element to be removed.</param>
        /// <returns>True if element is removed, falso if key not found.</returns>
        public bool Remove(int key)
        {
            // removes element from list based on key

            int iter = 0; // no use in this context

            // first stpe, find node to be removed
            IBSTNode node = this.SearchNode(this._root, key, ref iter);

            // key not found
            if (node == null)
            {
                return(false);
            }

            // if node is leaf, remove reference from parent and that's all
            // or if it's root, set to null
            if (node.isLeaf())
            {
                if (node.Status == BSTNodeStatus.Root)
                {
                    this._root = null;
                }
                else if (node.Status == BSTNodeStatus.Left)
                {
                    ((BSTInternalNode)node.Parent).Left = null;
                }
                else
                {
                    ((BSTInternalNode)node.Parent).Right = null;
                }
            }
            else if (node.isRoot()) // check if node is root
            {
                // add reference to children
                IBSTNode leftSubtree  = ((BSTInternalNode)this._root).Left;
                IBSTNode rightSubtree = ((BSTInternalNode)this._root).Right;

                // check if left subtree is empty, if not, it becomes the new tree
                if (leftSubtree != null)
                {
                    leftSubtree.Parent = null; // erase reference to old parent
                    this._root         = leftSubtree;

                    // find an empty spot to accomodate the right subtree
                    // if it is not null
                    if (rightSubtree != null)
                    {
                        this.InsertNode(this._root, rightSubtree);
                    }
                }
                else
                {
                    // right subtree is the new tree
                    // at this point it can't be null
                    rightSubtree.Parent = null;
                    this._root          = rightSubtree;
                }
            }
            else
            {
                // add reference to children
                IBSTNode leftSubtree  = ((BSTInternalNode)node).Left;
                IBSTNode rightSubtree = ((BSTInternalNode)node).Right;

                // remove reference from parent
                if (node.Status == BSTNodeStatus.Left)
                {
                    ((BSTInternalNode)node.Parent).Left = null;
                }
                else
                {
                    ((BSTInternalNode)node.Parent).Right = null;
                }

                // find new location for subtees
                if (leftSubtree != null)
                {
                    this.InsertNode(this._root, leftSubtree);
                }
                if (rightSubtree != null)
                {
                    this.InsertNode(this._root, rightSubtree);
                }
            }

            // decrement count
            this._count--;

            return(true);
        }
        private void InsertNewLeafNode(IBSTNode currNode, int key, string value)
        {
            // current node cannot be null
            if (currNode == null)
            {
                throw new ArgumentNullException("currNode");
            }

            // if value is the same, duplicate key found, replace value.
            if (currNode.Key == key)
            {
                currNode.Value = value;
                return;
            }

            // check if node is a leaf
            if (currNode.isLeaf())
            {
                // check if current node is root
                if (currNode.isRoot())
                {
                    // convert leaf into internal with the same parameters
                    this._root = new BSTInternalNode(this._root.Key, this._root.Value);

                    // insert new leaf node
                    this.InsertNewLeafNode(this._root, key, value);
                }
                else
                {
                    // unbox parent
                    BSTInternalNode parent = (BSTInternalNode)currNode.Parent;

                    // create new internal node using current value
                    BSTInternalNode newIntNode = new BSTInternalNode(currNode.Key, currNode.Value, parent);

                    // replace old leaf with new internal
                    // add reference to correct branch of parent
                    if (parent.Left == currNode)
                    {
                        parent.Left = newIntNode;
                    }
                    else
                    {
                        parent.Right = newIntNode;
                    }

                    // insert new leaf node
                    this.InsertNewLeafNode(newIntNode, key, value);
                }
            }
            else
            {
                // current node not leaf, unbox it
                BSTInternalNode node = (BSTInternalNode)currNode;

                // redirect to correct branch
                if (key > node.Key)
                {
                    // check if left child is empty, if so add new leaf
                    if (node.Left == null)
                    {
                        node.Left = new BSTLeafNode(key, value, node);
                        this._count++;
                    }
                    else
                    {
                        this.InsertNewLeafNode(node.Left, key, value);  // redirect to left branch
                    }
                }
                else
                {
                    // check if right child is empty, if so add new leaf
                    if (node.Right == null)
                    {
                        node.Right = new BSTLeafNode(key, value, node);
                        this._count++;
                    }
                    else
                    {
                        this.InsertNewLeafNode(node.Right, key, value);  // redirect to right branch
                    }
                }
            }
        }