public E max() {
   BinaryTreeNodeImpl<E> current = root;
   while (current.getRight() != null) {
     current = (BinaryTreeNodeImpl<E>) current.getRight();
   }
   return current.getData();
 }
 private BinaryTreeNodeImpl<E> predecessor(BinaryTreeNodeImpl<E> subroot)
     throws NoSuchElementException {
   BinaryTreeNodeImpl<E> current = (BinaryTreeNodeImpl<E>) subroot.getLeft();
   if (current == null) {
     throw new NoSuchElementException();
   }
   while (current.getRight() != null) {
     current = (BinaryTreeNodeImpl<E>) current.getRight();
   }
   return current;
 }
 private void add(BinaryTreeNodeImpl<E> node, BinaryTreeNodeImpl<E> subroot) {
   if (c.compare(node.getData(), subroot.getData()) == 0) {
     return; // element found; no duplicate added
   }
   if (c.compare(node.getData(), subroot.getData()) < 0) {
     if (subroot.getLeft() == null) {
       subroot.setLeft(node);
       node.setParent(subroot);
       return;
     } else {
       add(node, (BinaryTreeNodeImpl<E>) subroot.getLeft());
     }
   } else {
     if (subroot.getRight() == null) {
       subroot.setRight(node);
       node.setParent(subroot);
       return;
     } else {
       add(node, (BinaryTreeNodeImpl<E>) subroot.getRight());
     }
   }
 }
 private boolean contains(BinaryTreeNodeImpl<E> node, BinaryTreeNodeImpl<E> subroot) {
   count++;
   if (subroot == null) {
     return false;
   }
   if (c.compare(node.getData(), subroot.getData()) == 0) {
     return true;
   }
   if (c.compare(node.getData(), subroot.getData()) < 0) {
     return contains(node, (BinaryTreeNodeImpl<E>) subroot.getLeft());
   } else {
     return contains(node, (BinaryTreeNodeImpl<E>) subroot.getRight());
   }
 }
 private void remove(BinaryTreeNodeImpl<E> node, BinaryTreeNodeImpl<E> subroot) {
   if (subroot == null) { // element not present
     return;
   }
   if (c.compare(node.getData(), subroot.getData()) == 0) { // element found
     if (subroot.getLeft() == null && subroot.getRight() == null) { // no children
       if (subroot == root) {
         root = null;
       } else if (subroot.getParent().getLeft() == subroot) {
         subroot.getParent().setLeft(null);
       } else {
         subroot.getParent().setRight(null);
       }
     } else if (subroot.getLeft() == null) { // right child only
       BinaryTreeNodeImpl<E> successor = successor(subroot);
       if (successor.getParent() == subroot) {
         successor.getParent().setRight(null);
       } else {
         successor.getParent().setLeft(null);
       }
       if (subroot == root) {
         root = successor;
         successor.setParent(null);
       } else {
         successor.setParent(subroot.getParent());
         if (subroot.getParent().getLeft() == subroot) {
           successor.getParent().setLeft(successor);
         } else {
           successor.getParent().setRight(successor);
         }
       }
       successor.setRight(subroot.getRight());
       if (successor.getRight() != null) {
         successor.getRight().setParent(successor);
       }
     } else if (subroot.getRight() == null) { // left child only
       // get predecessor
       BinaryTreeNodeImpl<E> predecessor = predecessor(subroot);
       // remove predecessor's parent's link to it
       if (predecessor.getParent() == subroot) {
         predecessor.getParent().setLeft(null);
       } else {
         predecessor.getParent().setRight(null);
       }
       // set predecessor's link to its new parent, if applicable
       if (subroot == root) {
         root = predecessor;
         predecessor.setParent(null);
       } else {
         predecessor.setParent(subroot.getParent());
         // and set reciprocating link from parent to predecessor
         if (subroot.getParent().getLeft() == subroot) {
           predecessor.getParent().setLeft(predecessor);
         } else {
           predecessor.getParent().setRight(predecessor);
         }
       }
       // set predecessor's new left child (right remains null)
       predecessor.setLeft(subroot.getLeft());
       if (predecessor.getLeft() != null) {
         predecessor.getLeft().setParent(predecessor);
       }
     } else { // two children
       BinaryTreeNodeImpl<E> successor = successor(subroot);
       if (successor.getParent() == subroot) {
         successor.getParent().setRight(null);
       } else {
         successor.getParent().setLeft(null);
       }
       if (subroot == root) {
         root = successor;
         successor.setParent(null);
       } else {
         successor.setParent(subroot.getParent());
         if (subroot.getParent().getLeft() == subroot) {
           successor.getParent().setLeft(successor);
         } else {
           successor.getParent().setRight(successor);
         }
       }
       successor.setLeft(subroot.getLeft());
       successor.getLeft().setParent(successor);
       successor.setRight(subroot.getRight());
       if (successor.getRight() != null) {
         successor.getRight().setParent(successor);
       }
     }
     return;
   }
   if (c.compare(node.getData(), subroot.getData()) < 0) { // element not yet found, go left
     remove(node, (BinaryTreeNodeImpl<E>) subroot.getLeft());
   } else { // element not yet found, go right
     remove(node, (BinaryTreeNodeImpl<E>) subroot.getRight());
   }
 }