import java.util.*;

public class BinarySearchTree implements Iterable<String> {
    private BinarySearchTree left, right, parent;
    private String value;
    
    // Pre: None
    // Post: isEmpty()
    public BinarySearchTree() {
        parent = null;
        selfDestruct();
    }
    
    // Pre: None
    // Post: isEmpty()
    private void selfDestruct() {
        left = right = null;
        value = null;
    }
    
    // Pre: !isEmpty()
    // Post: Returns value of this
    public String get() {return value;}
    
    // Pre: None
    // Post: Returns false if tree has any elements
    public boolean isEmpty() {return value == null;}
    
    // Pre: None
    // Post: Returns true if target is contained in this tree
    public boolean contains(String target) {
	/* Implement this method */
	return false;
    }
 
    // Pre: None
    // Post: Returns the smallest value from this that is larger than target
    //       If no such value exists, returns null
    public String successor(String target) {
	/* Implement this method */
	return null;
    }

    // Pre: None
    // Post: Returns the largest value from this that is smaller than target
    //       If no such value exists, returns null
    public String predecessor(String target) {
	/* Implement this method */
	return null;
    }

    public String min() {return leftMost().get();}
    
    public String max() {return rightMost().get();}

    private BinarySearchTree leftMost() {
        return left.isEmpty() ? this : left.leftMost();
    }
    
    private BinarySearchTree rightMost() {
        return right.isEmpty() ? this : right.rightMost();
    }
    
    // Pre: None
    // Post: contains(target)
    public void add(String target) {
        if (this.isEmpty()) {
            value = target;
            setLeft(new BinarySearchTree());
            setRight(new BinarySearchTree());
        } else {
            int compare = target.compareTo(this.get());
            if (compare == 0) {
                value = target;
            } else {
                if (compare > 0) {
                    this.right.add(target);
                } else {
                    this.left.add(target);
                }
            }
        }
    }

    public boolean isRoot() {
        return parent == null;
    }
    
    public boolean isLeaf() {
        return left.isEmpty() && right.isEmpty();
    }
    
    public boolean isLeftChild() {
        return !isRoot() && parent.left == this;
    }
    
    public boolean isRightChild() {
        return !isRoot() && parent.right == this;
    }
    
    private void setLeft(BinarySearchTree newLeft) {
        if (newLeft == null) {throw new IllegalArgumentException();}
        left = newLeft;
        left.parent = this;
    }
    
    private void setRight(BinarySearchTree newRight) {
        if (newRight == null) {throw new IllegalArgumentException();}
        right = newRight;
        right.parent = this;
    }
    
    // Pre: None
    // Post: !contains(target)
    public void remove(String target) {
        /* Implement solution here */
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        appendSelf(sb, 0);
        return sb.toString();
    }
    
    private void appendSelf(StringBuilder sb, int numTabs) {
        appendTabs(numTabs, sb);
        
        if (isEmpty()) {
            sb.append("[sentinel]\n");
        } else {
            sb.append("[\"" + get() + "\"]\n");
            left.appendSelf(sb, numTabs + 1);
            right.appendSelf(sb, numTabs + 1);
        }
    }
    
    private void appendTabs(int numTabs, StringBuilder sb) {
        for (int i = 0; i < numTabs; ++i) {sb.append('\t');}
    }
    
    public Iterator<String> iterator() {
        return new BSTIterator(min());
    }
    
    private class BSTIterator implements Iterator<String> {
        private String val;
        
        public BSTIterator(String start) {
            val = start;
        }
        
        public boolean hasNext() {
            return val != null;
        }

        public String next() {
            String result = val;
            val = successor(val);
            return result;
        }

        public void remove() {
            throw new UnsupportedOperationException();
        }
    }
}
