@Override
  public void insert(int newValue) {

    // Empty tree
    if (this.parent == null && !hasValue) {
      this.value = newValue;
      this.hasValue = true;
      return;
    }

    if (this.value == newValue) return;

    int direction = newValue < value ? LEFT : RIGHT;
    BinarySearchTree child = children[direction];
    if (child == null) {
      children[direction] = new BinarySearchTree(this, newValue);
      if (children[direction ^ 1] != null) ++height;
    } else {
      child.insert(newValue);
    }
  }
  @Override
  public void delete(int delVal) {

    int numChildren = numChildren();

    // Root node
    if (this.parent == null && this.value == delVal && numChildren == 0) {
      this.hasValue = false;
      return;
    }

    if (value == delVal) {

      // System.out.println("Deleting " + value + " with numChildren = " + numChildren);
      if (numChildren == 2) {
        BinarySearchTree successor = children[RIGHT].findMin();
        this.value = successor.value;
        successor.delete(successor.value);
      } else if (numChildren == 1) {

        BinarySearchTree child = children[LEFT] != null ? children[LEFT] : children[RIGHT];
        // Check if root
        if (parent != null) {
          int directionFromParent = this.equals(parent.children[LEFT]) ? LEFT : RIGHT;
          parent.children[directionFromParent] = child;
        }

        child.parent = this.parent;
      } else {
        int directionFromParent = this.equals(parent.children[LEFT]) ? LEFT : RIGHT;
        parent.children[directionFromParent] = null;
        this.parent = null;
      }
    }

    int direction = delVal < value ? LEFT : RIGHT;
    BinarySearchTree child = children[direction];
    if (child != null) child.delete(delVal);
  }