private void getElements(IBSTNode currNode, BSTStack stack)
        {
            // add nodes into stack to use at getEnumerator()

            // null nodes not allowed
            if (currNode == null)
            {
                return;
            }

            if (currNode.isLeaf())
            {
                // add element to stack
                stack.Push(currNode);
            }
            else
            {
                // unbox
                BSTInternalNode node = (BSTInternalNode)currNode;

                // get left node
                this.getElements(node.Left, stack);

                // add current element to stack
                stack.Push(node);

                // get right nodes
                this.getElements(node.Right, stack);
            }
        }
        private int CountNodes(IBSTNode currNode, int count)
        {
            // check if current node is null
            if (currNode == null)
            {
                return(count);
            }

            // increment count
            count++;

            // check if current node is leaf
            if (currNode.isLeaf())
            {
                return(count);
            }

            // current node is internal, unbox it
            BSTInternalNode node = (BSTInternalNode)currNode;


            // go to left branch
            count = this.CountNodes(node.Left, count);
            // go to right branch
            count = this.CountNodes(node.Right, count);

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