IntTree<V> minus(final long key) {
        if (size == 0)
            return this;
        if (key < this.key)
            return rebalanced(left.minus(key - this.key), right);
        if (key > this.key)
            return rebalanced(left, right.minus(key - this.key));

        // otherwise key==this.key, so we are killing this node:

        if (left.size == 0) // we can just become right node
            // make key 'absolute':
            return right.withKey(right.key + this.key);
        if (right.size == 0) // we can just become left node
            return left.withKey(left.key + this.key);

        // otherwise replace this with the next key (i.e. the smallest key to the right):

        // TODO have minNode() instead of minKey to avoid having to call get()
        // TODO get node from larger subtree, i.e. if left.size>right.size use left.maxNode()
        // TODO have faster minusMin() instead of just using minus()

        long newKey = right.minKey() + this.key;
        //(right.minKey() is relative to this; adding this.key makes it 'absolute'
        //	where 'absolute' really means relative to the parent of this)

        V newValue = right.get(newKey - this.key);
        // now that we've got the new stuff, take it out of the right subtree:
        IntTree<V> newRight = right.minus(newKey - this.key);

        // lastly, make the subtree keys relative to newKey (currently they are relative to this.key):
        newRight = newRight.withKey((newRight.key + this.key) - newKey);
        // left is definitely not empty:
        IntTree<V> newLeft = left.withKey((left.key + this.key) - newKey);

        return rebalanced(newKey, newValue, newLeft, newRight);
    }