/*
 * Written by Dawid Kurzyniec, on the basis of public specifications and
 * public domain sources from JSR 166 and the Doug Lea's collections package,
 * and released to the public domain,
 * as explained at http://creativecommons.org/licenses/publicdomain.
 */

package edu.emory.mathcs.backport.java.util;

import java.util.Comparator;
import java.util.Map;
import java.util.AbstractSet;
import java.util.SortedSet;
import java.util.Set;
import java.util.Iterator;
import java.util.SortedMap;
import java.util.NoSuchElementException;
import java.util.ConcurrentModificationException;
import java.io.Serializable;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import edu.emory.mathcs.backport.java.util.TreeMap.AscendingSubMap;

/**
 * Sorted map implementation based on a red-black tree and implementing
 * all the methods from the NavigableMap interface.
 *
 * @author Dawid Kurzyniec
 */
public class TreeMap extends AbstractMap
                     implements NavigableMap, Serializable {

    private static final long serialVersionUID = 919286545866124006L;

    private final Comparator comparator;

    private transient Entry root;

    private transient int size = 0;
    private transient int modCount = 0;

    private transient EntrySet entrySet;
    private transient KeySet navigableKeySet;
    private transient NavigableMap descendingMap;
    private transient Comparator reverseComparator;

    public TreeMap() {
        this.comparator = null;
    }

    public TreeMap(Comparator comparator) {
        this.comparator = comparator;
    }

    public TreeMap(SortedMap map) {
        this.comparator = map.comparator();
        this.buildFromSorted(map.entrySet().iterator(), map.size());
    }

    public TreeMap(Map map) {
        this.comparator = null;
        putAll(map);
    }

    public int size() { return size; }

    public void clear() {
        root = null;
        size = 0;
        modCount++;
    }

    public Object clone() {
        TreeMap clone;
        try { clone = (TreeMap)super.clone(); }
        catch (CloneNotSupportedException e) { throw new InternalError(); }
        clone.root = null;
        clone.size = 0;
        clone.modCount = 0;
        if (!isEmpty()) {
            clone.buildFromSorted(this.entrySet().iterator(), this.size);
        }
        return clone;
    }

    public Object put(Object key, Object value) {
        if (root == null) {
            root = new Entry(key, value);
            size++;
            modCount++;
            return null;
        }
        else {
            Entry t = root;
            for (;;) {
                int diff = compare(key, t.getKey(), comparator);
                if (diff == 0) return t.setValue(value);
                else if (diff <= 0) {
                    if (t.left != null) t = t.left;
                    else {
                        size++;
                        modCount++;
                        Entry e = new Entry(key, value);
                        e.parent = t;
                        t.left = e;
                        fixAfterInsertion(e);
                        return null;
                    }
                }
                else {
                    if (t.right != null) t = t.right;
                    else {
                        size++;
                        modCount++;
                        Entry e = new Entry(key, value);
                        e.parent = t;
                        t.right = e;
                        fixAfterInsertion(e);
                        return null;
                    }
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public Object get(Object key) {
        Entry entry = getEntry(key);
        return (entry == null) ? null : entry.getValue();
    }

    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

    public Set entrySet() {
        if (entrySet == null) {
            entrySet = new EntrySet();
        }
        return entrySet;
    }

    public static class Entry
        implements Map.Entry, Cloneable, java.io.Serializable {

        private static final boolean RED = false;
        private static final boolean BLACK = true;

        private Object key;
        private Object element;

        /**
         * The node color (RED, BLACK)
         */
        private boolean color;

        /**
         * Pointer to left child
         */
        private Entry left;

        /**
         * Pointer to right child
         */
        private Entry right;

        /**
         * Pointer to parent (null if root)
         */
        private Entry parent;

        /**
         * Make a new node with given element, null links, and BLACK color.
         * Normally only called to establish a new root.
         */
        public Entry(Object key, Object element) {
            this.key = key;
            this.element = element;
            this.color = BLACK;
        }

        /**
         * Return a new Entry with same element and color as self,
         * but with null links. (Since it is never OK to have
         * multiple identical links in a RB tree.)
         */
        protected Object clone() throws CloneNotSupportedException {
            Entry t = new Entry(key, element);
            t.color = color;
            return t;
        }

        public final Object getKey() {
            return key;
        }

        /**
         * return the element value
         */
        public final Object getValue() {
            return element;
        }

        /**
         * set the element value
         */
        public final Object setValue(Object v) {
            Object old = element;
            element = v;
            return old;
        }

        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry)) return false;
            Map.Entry e = (Map.Entry)o;
            return eq(key, e.getKey()) && eq(element, e.getValue());
        }

        public int hashCode() {
            return (key == null ? 0 : key.hashCode()) ^
                   (element == null ? 0 : element.hashCode());
        }

        public String toString() {
            return key + "=" + element;
        }
    }

    /**
     * Return the inorder successor, or null if no such
     */
    private static Entry successor(Entry e) {
        if (e.right != null) {
            for (e = e.right; e.left != null; e = e.left) {}
            return e;
        } else {
            Entry p = e.parent;
            while (p != null && e == p.right) {
                e = p;
                p = p.parent;
            }
            return p;
        }
    }

    /**
     * Return the inorder predecessor, or null if no such
     */
    private static Entry predecessor(Entry e) {
        if (e.left != null) {
            for (e = e.left; e.right != null; e = e.right) {}
            return e;
        }
        else {
            Entry p = e.parent;
            while (p != null && e == p.left) {
                e = p;
                p = p.parent;
            }
            return p;
        }
    }

    private Entry getEntry(Object key) {
        Entry t = root;
        if (comparator != null) {
            for (;;) {
                if (t == null) return null;
                int diff = comparator.compare(key, t.key);
                if (diff == 0) return t;
                t = (diff < 0) ? t.left : t.right;
            }
        }
        else {
            Comparable c = (Comparable)key;
            for (;;) {
                if (t == null) return null;
                int diff = c.compareTo(t.key);
                if (diff == 0) return t;
                t = (diff < 0) ? t.left : t.right;
            }
        }
    }

    private Entry getHigherEntry(Object key) {
        Entry t = root;
        if (t == null) return null;
        for (;;) {
            int diff = compare(key, t.key, comparator);
            if (diff < 0) {
                if (t.left != null) t = t.left; else return t;
            }
            else {
                if (t.right != null) {
                    t = t.right;
                }
                else {
                    Entry parent = t.parent;
                    while (parent != null && t == parent.right) {
                        t = parent;
                        parent = parent.parent;
                    }
                    return parent;
                }
            }
        }
    }

    private Entry getFirstEntry() {
        Entry e = root;
        if (e == null) return null;
        while (e.left != null) e = e.left;
        return e;
    }

    private Entry getLastEntry() {
        Entry e = root;
        if (e == null) return null;
        while (e.right != null) e = e.right;
        return e;
    }

    private Entry getCeilingEntry(Object key) {
        Entry e = root;
        if (e == null) return null;
        for (;;) {
            int diff = compare(key, e.key, comparator);
            if (diff < 0) {
                if (e.left != null) e = e.left; else return e;
            }
            else if (diff > 0) {
                if (e.right != null) {
                    e = e.right;
                }
                else {
                    Entry p = e.parent;
                    while (p != null && e == p.right) {
                        e = p;
                        p = p.parent;
                    }
                    return p;
                }
            }
            else return e;
        }
    }

    private Entry getLowerEntry(Object key) {
        Entry e = root;
        if (e == null) return null;
        for (;;) {
            int diff = compare(key, e.key, comparator);
            if (diff > 0) {
                if (e.right != null) e = e.right; else return e;
            }
            else {
                if (e.left != null) {
                    e = e.left;
                }
                else {
                    Entry p = e.parent;
                    while (p != null && e == p.left) {
                        e = p;
                        p = p.parent;
                    }
                    return p;
                }
            }
        }
    }

    private Entry getFloorEntry(Object key) {
        Entry e = root;
        if (e == null) return null;
        for (;;) {
            int diff = compare(key, e.key, comparator);
            if (diff > 0) {
                if (e.right != null) e = e.right; else return e;
            }
            else if (diff < 0) {
                if (e.left != null) {
                    e = e.left;
                }
                else {
                    Entry p = e.parent;
                    while (p != null && e == p.left) {
                        e = p;
                        p = p.parent;
                    }
                    return p;
                }
            }
            else return e;
        }
    }

    void buildFromSorted(Iterator itr, int size) {
        modCount++;
        this.size = size;
        // nodes at the bottom (unbalanced) level must be red
        int bottom = 0;
        for (int ssize = 1; ssize-1 < size; ssize <<= 1) bottom++;
        this.root = createFromSorted(itr, size, 0, bottom);
    }

    private static Entry createFromSorted(Iterator itr, int size,
                                          int level, int bottom) {
        level++;
        if (size == 0) return null;
        int leftSize = (size-1) >> 1;
        int rightSize = size-1-leftSize;
        Entry left = createFromSorted(itr, leftSize, level, bottom);
        Map.Entry orig = (Map.Entry)itr.next();
        Entry right = createFromSorted(itr, rightSize, level, bottom);
        Entry e = new Entry(orig.getKey(), orig.getValue());
        if (left != null) {
            e.left = left;
            left.parent = e;
        }
        if (right != null) {
            e.right = right;
            right.parent = e;
        }
        if (level == bottom) e.color = Entry.RED;
        return e;
    }

    /**
     * Delete the current node, and then rebalance the tree it is in
     * @param root the root of the current tree
     * @return the new root of the current tree. (Rebalancing
     * can change the root!)
     */
    private void delete(Entry e) {

        // handle case where we are only node
        if (e.left == null && e.right == null && e.parent == null) {
            root = null;
            size = 0;
            modCount++;
            return;
        }
        // if strictly internal, swap places with a successor
        if (e.left != null && e.right != null) {
            Entry s = successor(e);
            e.key = s.key;
            e.element = s.element;
            e = s;
        }

        // Start fixup at replacement node (normally a child).
        // But if no children, fake it by using self

        if (e.left == null && e.right == null) {

            if (e.color == Entry.BLACK)
                fixAfterDeletion(e);

            // Unlink  (Couldn't before since fixAfterDeletion needs parent ptr)

            if (e.parent != null) {
                if (e == e.parent.left)
                    e.parent.left = null;
                else if (e == e.parent.right)
                    e.parent.right = null;
                e.parent = null;
            }

        }
        else {
            Entry replacement = e.left;
            if (replacement == null)
                replacement = e.right;

            // link replacement to parent
            replacement.parent = e.parent;

            if (e.parent == null)
                root = replacement;
            else if (e == e.parent.left)
                e.parent.left = replacement;
            else
                e.parent.right = replacement;

            e.left = null;
            e.right = null;
            e.parent = null;

            // fix replacement
            if (e.color == Entry.BLACK)
                fixAfterDeletion(replacement);

        }

        size--;
        modCount++;
    }

    /**
     * Return color of node p, or BLACK if p is null
     * (In the CLR version, they use
     * a special dummy `nil' node for such purposes, but that doesn't
     * work well here, since it could lead to creating one such special
     * node per real node.)
     *
     */
    static boolean colorOf(Entry p) {
        return (p == null) ? Entry.BLACK : p.color;
    }

    /**
     * return parent of node p, or null if p is null
     */
    static Entry parentOf(Entry p) {
        return (p == null) ? null : p.parent;
    }

    /**
     * Set the color of node p, or do nothing if p is null
     */
    private static void setColor(Entry p, boolean c) {
        if (p != null) p.color = c;
    }

    /**
     * return left child of node p, or null if p is null
     */
    private static Entry leftOf(Entry p) {
        return (p == null) ? null : p.left;
    }

    /**
     * return right child of node p, or null if p is null
     */
    private static Entry rightOf(Entry p) {
        return (p == null) ? null : p.right;
    }

    /** From CLR */
    private final void rotateLeft(Entry e) {
        Entry r = e.right;
        e.right = r.left;
        if (r.left != null)
            r.left.parent = e;
        r.parent = e.parent;
        if (e.parent == null) root = r;
        else if (e.parent.left == e)
            e.parent.left = r;
        else
            e.parent.right = r;
        r.left = e;
        e.parent = r;
    }

    /** From CLR */
    private final void rotateRight(Entry e) {
        Entry l = e.left;
        e.left = l.right;
        if (l.right != null)
            l.right.parent = e;
        l.parent = e.parent;
        if (e.parent == null) root = l;
        else if (e.parent.right == e)
            e.parent.right = l;
        else
            e.parent.left = l;
        l.right = e;
        e.parent = l;
    }

    /** From CLR */
    private final void fixAfterInsertion(Entry e) {
        e.color = Entry.RED;
        Entry x = e;

        while (x != null && x != root && x.parent.color == Entry.RED) {
            if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
                Entry y = rightOf(parentOf(parentOf(x)));
                if (colorOf(y) == Entry.RED) {
                    setColor(parentOf(x), Entry.BLACK);
                    setColor(y, Entry.BLACK);
                    setColor(parentOf(parentOf(x)), Entry.RED);
                    x = parentOf(parentOf(x));
                }
                else {
                    if (x == rightOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateLeft(x);
                    }
                    setColor(parentOf(x), Entry.BLACK);
                    setColor(parentOf(parentOf(x)), Entry.RED);
                    if (parentOf(parentOf(x)) != null)
                        rotateRight(parentOf(parentOf(x)));
                }
            }
            else {
                Entry y = leftOf(parentOf(parentOf(x)));
                if (colorOf(y) == Entry.RED) {
                    setColor(parentOf(x), Entry.BLACK);
                    setColor(y, Entry.BLACK);
                    setColor(parentOf(parentOf(x)), Entry.RED);
                    x = parentOf(parentOf(x));
                }
                else {
                    if (x == leftOf(parentOf(x))) {
                        x = parentOf(x);
                        rotateRight(x);
                    }
                    setColor(parentOf(x), Entry.BLACK);
                    setColor(parentOf(parentOf(x)), Entry.RED);
                    if (parentOf(parentOf(x)) != null)
                        rotateLeft(parentOf(parentOf(x)));
                }
            }
        }
        root.color = Entry.BLACK;
    }

    /** From CLR */
    private final Entry fixAfterDeletion(Entry e) {
        Entry x = e;
        while (x != root && colorOf(x) == Entry.BLACK) {
            if (x == leftOf(parentOf(x))) {
                Entry sib = rightOf(parentOf(x));
                if (colorOf(sib) == Entry.RED) {
                    setColor(sib, Entry.BLACK);
                    setColor(parentOf(x), Entry.RED);
                    rotateLeft(parentOf(x));
                    sib = rightOf(parentOf(x));
                }
                if (colorOf(leftOf(sib)) == Entry.BLACK &&
                    colorOf(rightOf(sib)) == Entry.BLACK) {
                    setColor(sib, Entry.RED);
                    x = parentOf(x);
                }
                else {
                    if (colorOf(rightOf(sib)) == Entry.BLACK) {
                        setColor(leftOf(sib), Entry.BLACK);
                        setColor(sib, Entry.RED);
                        rotateRight(sib);
                        sib = rightOf(parentOf(x));
                    }
                    setColor(sib, colorOf(parentOf(x)));
                    setColor(parentOf(x), Entry.BLACK);
                    setColor(rightOf(sib), Entry.BLACK);
                    rotateLeft(parentOf(x));
                    x = root;
                }
            }
            else {
                Entry sib = leftOf(parentOf(x));
                if (colorOf(sib) == Entry.RED) {
                    setColor(sib, Entry.BLACK);
                    setColor(parentOf(x), Entry.RED);
                    rotateRight(parentOf(x));
                    sib = leftOf(parentOf(x));
                }
                if (colorOf(rightOf(sib)) == Entry.BLACK &&
                    colorOf(leftOf(sib)) == Entry.BLACK) {
                    setColor(sib, Entry.RED);
                    x = parentOf(x);
                }
                else {
                    if (colorOf(leftOf(sib)) == Entry.BLACK) {
                        setColor(rightOf(sib), Entry.BLACK);
                        setColor(sib, Entry.RED);
                        rotateLeft(sib);
                        sib = leftOf(parentOf(x));
                    }
                    setColor(sib, colorOf(parentOf(x)));
                    setColor(parentOf(x), Entry.BLACK);
                    setColor(leftOf(sib), Entry.BLACK);
                    rotateRight(parentOf(x));
                    x = root;
                }
            }
        }
        setColor(x, Entry.BLACK);
        return root;
    }

    private class BaseEntryIterator {
        Entry cursor;
        Entry lastRet;
        int expectedModCount;
        BaseEntryIterator(Entry cursor) {
            this.cursor = cursor;
            this.expectedModCount = modCount;
        }
        public boolean hasNext() {
            return (cursor != null);
        }
        Entry nextEntry() {
            Entry curr = cursor;
            if (curr == null) throw new NoSuchElementException();
            if (expectedModCount != modCount)
                throw new ConcurrentModificationException();
            cursor = successor(curr);
            lastRet = curr;
            return curr;
        }
        Entry prevEntry() {
            Entry curr = cursor;
            if (curr == null) throw new NoSuchElementException();
            if (expectedModCount != modCount)
                throw new ConcurrentModificationException();
            cursor = predecessor(curr);
            lastRet = curr;
            return curr;
        }
        public void remove() {
            if (lastRet == null) throw new IllegalStateException();
            if (expectedModCount != modCount)
                throw new ConcurrentModificationException();
            // if removal strictly internal, it swaps places with a successor
            if (lastRet.left != null && lastRet.right != null && cursor != null) cursor = lastRet;
            delete(lastRet);
            lastRet = null;
            expectedModCount++;
        }
    }

    class EntryIterator extends BaseEntryIterator implements Iterator {
        EntryIterator(Entry cursor) { super(cursor); }
        public Object next() { return nextEntry(); }
    }

    class KeyIterator extends BaseEntryIterator implements Iterator {
        KeyIterator(Entry cursor) { super(cursor); }
        public Object next() { return nextEntry().key; }
    }

    class ValueIterator extends BaseEntryIterator implements Iterator {
        ValueIterator(Entry cursor) { super(cursor); }
        public Object next() { return nextEntry().element; }
    }

    class DescendingEntryIterator extends BaseEntryIterator implements Iterator {
        DescendingEntryIterator(Entry cursor) { super(cursor); }
        public Object next() { return prevEntry(); }
    }

    class DescendingKeyIterator extends BaseEntryIterator implements Iterator {
        DescendingKeyIterator(Entry cursor) { super(cursor); }
        public Object next() { return prevEntry().key; }
    }

    class DescendingValueIterator extends BaseEntryIterator implements Iterator {
        DescendingValueIterator(Entry cursor) { super(cursor); }
        public Object next() { return prevEntry().element; }
    }

    private Entry getMatchingEntry(Object o) {
        if (!(o instanceof Map.Entry)) return null;
        Map.Entry e = (Map.Entry)o;
        Entry found = TreeMap.this.getEntry(e.getKey());
        return (found != null && eq(found.getValue(), e.getValue())) ? found : null;
    }

    class EntrySet extends AbstractSet {
        public int size() { return TreeMap.this.size(); }
        public boolean isEmpty() { return TreeMap.this.isEmpty(); }
        public void clear() { TreeMap.this.clear(); }

        public Iterator iterator() {
            return new EntryIterator(getFirstEntry());
        }

        public boolean contains(Object o) {
            return getMatchingEntry(o) != null;
        }

        public boolean remove(Object o) {
            Entry e = getMatchingEntry(o);
            if (e == null) return false;
            delete(e);
            return true;
        }
    }

    class DescendingEntrySet extends EntrySet {
        public Iterator iterator() {
            return new DescendingEntryIterator(getLastEntry());
        }
    }

    class ValueSet extends AbstractSet {
        public int size() { return TreeMap.this.size(); }
        public boolean isEmpty() { return TreeMap.this.isEmpty(); }
        public void clear() { TreeMap.this.clear(); }

        public boolean contains(Object o) {
            for (Entry e = getFirstEntry(); e != null; e = successor(e)) {
                if (eq(o, e.element)) return true;
            }
            return false;
        }

        public Iterator iterator() {
            return new ValueIterator(getFirstEntry());
        }

        public boolean remove(Object o) {
            for (Entry e = getFirstEntry(); e != null; e = successor(e)) {
                if (eq(o, e.element)) {
                    delete(e);
                    return true;
                }
            }
            return false;
        }
    }

    abstract class KeySet extends AbstractSet implements NavigableSet {
        public int size() { return TreeMap.this.size(); }
        public boolean isEmpty() { return TreeMap.this.isEmpty(); }
        public void clear() { TreeMap.this.clear(); }

        public boolean contains(Object o) {
            return getEntry(o) != null;
        }

        public boolean remove(Object o) {
            Entry found = getEntry(o);
            if (found == null) return false;
            delete(found);
            return true;
        }
        public SortedSet subSet(Object fromElement, Object toElement) {
            return subSet(fromElement, true, toElement, false);
        }
        public SortedSet headSet(Object toElement) {
            return headSet(toElement, false);
        }
        public SortedSet tailSet(Object fromElement) {
            return tailSet(fromElement, true);
        }
    }

    class AscendingKeySet extends KeySet {

        public Iterator iterator() {
            return new KeyIterator(getFirstEntry());
        }

        public Iterator descendingIterator() {
            return new DescendingKeyIterator(getFirstEntry());
        }

        public Object lower(Object e)   { return lowerKey(e); }
        public Object floor(Object e)   { return floorKey(e); }
        public Object ceiling(Object e) { return ceilingKey(e); }
        public Object higher(Object e)  { return higherKey(e); }
        public Object first()           { return firstKey(); }
        public Object last()            { return lastKey(); }
        public Comparator comparator()  { return TreeMap.this.comparator(); }

        public Object pollFirst() {
            Map.Entry e = pollFirstEntry();
            return e == null? null : e.getKey();
        }
        public Object pollLast() {
            Map.Entry e = pollLastEntry();
            return e == null? null : e.getKey();
        }

        public NavigableSet subSet(Object fromElement, boolean fromInclusive,
                                   Object toElement,   boolean toInclusive) {
            return (NavigableSet)(subMap(fromElement, fromInclusive,
                                         toElement,   toInclusive)).keySet();
        }
        public NavigableSet headSet(Object toElement, boolean inclusive) {
            return (NavigableSet)(headMap(toElement, inclusive)).keySet();
        }
        public NavigableSet tailSet(Object fromElement, boolean inclusive) {
            return (NavigableSet)(tailMap(fromElement, inclusive)).keySet();
        }
        public NavigableSet descendingSet() {
            return (NavigableSet)descendingMap().keySet();
        }
    }

    class DescendingKeySet extends KeySet {

        public Iterator iterator() {
            return new DescendingKeyIterator(getLastEntry());
        }

        public Iterator descendingIterator() {
            return new KeyIterator(getFirstEntry());
        }

        public Object lower(Object e)   { return higherKey(e); }
        public Object floor(Object e)   { return ceilingKey(e); }
        public Object ceiling(Object e) { return floorKey(e); }
        public Object higher(Object e)  { return lowerKey(e); }
        public Object first()           { return lastKey(); }
        public Object last()            { return firstKey(); }
        public Comparator comparator()  { return descendingMap().comparator(); }

        public Object pollFirst() {
            Map.Entry e = pollLastEntry();
            return e == null? null : e.getKey();
        }
        public Object pollLast() {
            Map.Entry e = pollFirstEntry();
            return e == null? null : e.getKey();
        }

        public NavigableSet subSet(Object fromElement, boolean fromInclusive,
                                   Object toElement,   boolean toInclusive) {
            return (NavigableSet)(descendingMap().subMap(fromElement, fromInclusive,
                                          toElement,   toInclusive)).keySet();
        }
        public NavigableSet headSet(Object toElement, boolean inclusive) {
            return (NavigableSet)(descendingMap().headMap(toElement, inclusive)).keySet();
        }
        public NavigableSet tailSet(Object fromElement, boolean inclusive) {
            return (NavigableSet)(descendingMap().tailMap(fromElement, inclusive)).keySet();
        }
        public NavigableSet descendingSet() {
            return (NavigableSet)keySet();
        }
    }

    private static boolean eq(Object o1, Object o2) {
        return o1 == null ? o2 == null : o1.equals(o2);
    }

    private static int compare(Object o1, Object o2, Comparator cmp) {
        return (cmp == null)
            ? ((Comparable)o1).compareTo(o2)
            : cmp.compare(o1, o2);
    }

    /**
     * @since 1.6
     */
    public Map.Entry lowerEntry(Object key) {
        Map.Entry e = getLowerEntry(key);
        return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
    }

    /**
     * @since 1.6
     */
    public Object lowerKey(Object key) {
        Map.Entry e = getLowerEntry(key);
        return (e == null) ? null : e.getKey();
    }

    /**
     * @since 1.6
     */
    public Map.Entry floorEntry(Object key) {
        Entry e = getFloorEntry(key);
        return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
    }

    /**
     * @since 1.6
     */
    public Object floorKey(Object key) {
        Entry e = getFloorEntry(key);
        return (e == null) ? null : e.key;
    }

    /**
     * @since 1.6
     */
    public Map.Entry ceilingEntry(Object key) {
        Entry e = getCeilingEntry(key);
        return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
    }

    /**
     * @since 1.6
     */
    public Object ceilingKey(Object key) {
        Entry e = getCeilingEntry(key);
        return (e == null) ? null : e.key;
    }

    /**
     * @since 1.6
     */
    public Map.Entry higherEntry(Object key) {
        Entry e = getHigherEntry(key);
        return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
    }

    /**
     * @since 1.6
     */
    public Object higherKey(Object key) {
        Entry e = getHigherEntry(key);
        return (e == null) ? null : e.key;
    }

    /**
     * @since 1.6
     */
    public Map.Entry firstEntry() {
        Entry e = getFirstEntry();
        return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
    }

    /**
     * @since 1.6
     */
    public Map.Entry lastEntry() {
        Entry e = getLastEntry();
        return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
    }

    /**
     * @since 1.6
     */
    public Map.Entry pollFirstEntry() {
        Entry e = getFirstEntry();
        if (e == null) return null;
        Map.Entry res = new AbstractMap.SimpleImmutableEntry(e);
        delete(e);
        return res;
    }

    /**
     * @since 1.6
     */
    public Map.Entry pollLastEntry() {
        Entry e = getLastEntry();
        if (e == null) return null;
        Map.Entry res = new AbstractMap.SimpleImmutableEntry(e);
        delete(e);
        return res;
    }

    /**
     * @since 1.6
     */
    public NavigableMap descendingMap() {
        NavigableMap map = descendingMap;
        if (map == null) {
            descendingMap = map = new DescendingSubMap(true, null, true,
                                                       true, null, true);
        }
        return map;
    }

    public NavigableSet descendingKeySet() {
        return descendingMap().navigableKeySet();
    }

    public SortedMap subMap(Object fromKey, Object toKey) {
        return subMap(fromKey, true, toKey, false);
    }

    public SortedMap headMap(Object toKey) {
        return headMap(toKey, false);
    }

    public SortedMap tailMap(Object fromKey) {
        return tailMap(fromKey, true);
    }

    public NavigableMap subMap(Object fromKey, boolean fromInclusive,
                               Object toKey,   boolean toInclusive) {
        return new AscendingSubMap(false, fromKey, fromInclusive,
                                   false, toKey, toInclusive);
    }

    public NavigableMap headMap(Object toKey, boolean toInclusive) {
        return new AscendingSubMap(true,  null,  true,
                                   false, toKey, toInclusive);
    }

    public NavigableMap tailMap(Object fromKey, boolean fromInclusive) {
        return new AscendingSubMap(false, fromKey, fromInclusive,
                                   true,  null,    true);
    }

    public Comparator comparator() {
        return comparator;
    }

    final Comparator reverseComparator() {
        if (reverseComparator == null) {
            reverseComparator = Collections.reverseOrder(comparator);
        }
        return reverseComparator;
    }

    public Object firstKey() {
        Entry e = getFirstEntry();
        if (e == null) throw new NoSuchElementException();
        return e.key;
    }

    public Object lastKey() {
        Entry e = getLastEntry();
        if (e == null) throw new NoSuchElementException();
        return e.key;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public boolean containsValue(Object value) {
        if (root == null) return false;
        return (value == null) ? containsNull(root) : containsValue(root, value);
    }

    private static boolean containsNull(Entry e) {
        if (e.element == null) return true;
        if (e.left != null && containsNull(e.left)) return true;
        if (e.right != null && containsNull(e.right)) return true;
        return false;
    }

    private static boolean containsValue(Entry e, Object val) {
        if (val.equals(e.element)) return true;
        if (e.left != null && containsValue(e.left, val)) return true;
        if (e.right != null && containsValue(e.right, val)) return true;
        return false;
    }

    public Object remove(Object key) {
        Entry e = getEntry(key);
        if (e == null) return null;
        Object old = e.getValue();
        delete(e);
        return old;
    }

    public void putAll(Map map) {
        if (map instanceof SortedMap) {
            SortedMap smap = (SortedMap)map;
            if (eq(this.comparator, smap.comparator())) {
                this.buildFromSorted(smap.entrySet().iterator(), map.size());
                return;
            }
        }
        // not a sorted map, or comparator mismatch
        super.putAll(map);
    }

    public Set keySet() {
        return navigableKeySet();
    }

    public NavigableSet navigableKeySet() {
        if (navigableKeySet == null) {
            navigableKeySet = new AscendingKeySet();
        }
        return navigableKeySet;
    }

//    public Collection values() {
//        if (valueSet == null) {
//            valueSet = new ValueSet();
//        }
//        return valueSet;
//    }
//
    private abstract class NavigableSubMap extends AbstractMap
                                           implements NavigableMap, Serializable {

        private static final long serialVersionUID = -6520786458950516097L;

        final Object fromKey, toKey;
        final boolean fromStart, toEnd;
        final boolean fromInclusive, toInclusive;
        transient int cachedSize = -1, cacheVersion;
        transient SubEntrySet entrySet;
        transient NavigableMap descendingMap;
        transient NavigableSet navigableKeySet;

        NavigableSubMap(boolean fromStart, Object fromKey, boolean fromInclusive,
                        boolean toEnd,     Object toKey,   boolean toInclusive) {
            if (!fromStart && !toEnd) {
                if (compare(fromKey, toKey, comparator) > 0) {
                    throw new IllegalArgumentException("fromKey > toKey");
                }
            }
            else {
                if (!fromStart) compare(fromKey, fromKey, comparator);
                if (!toEnd) compare(toKey, toKey, comparator);
            }
            this.fromStart = fromStart;
            this.toEnd = toEnd;
            this.fromKey = fromKey;
            this.toKey = toKey;
            this.fromInclusive = fromInclusive;
            this.toInclusive = toInclusive;
        }

        final TreeMap.Entry checkLoRange(TreeMap.Entry e) {
            return (e == null || absTooLow(e.key)) ? null : e;
        }

        final TreeMap.Entry checkHiRange(TreeMap.Entry e) {
            return (e == null || absTooHigh(e.key)) ? null : e;
        }

        final boolean inRange(Object key) {
            return !absTooLow(key) && !absTooHigh(key);
        }

        final boolean inRangeExclusive(Object key) {
            return (fromStart || compare(key, fromKey, comparator) >= 0)
                && (toEnd     || compare(toKey, key, comparator) >= 0);
        }

        final boolean inRange(Object key, boolean inclusive) {
            return inclusive ? inRange(key) : inRangeExclusive(key);
        }


        private boolean absTooHigh(Object key) {
            if (toEnd) return false;
            int c = compare(key, toKey, comparator);
            return (c > 0 || (c == 0 && !toInclusive));
        }

        private boolean absTooLow(Object key) {
            if (fromStart) return false;
            int c = compare(key, fromKey, comparator);
            return (c < 0 || (c == 0 && !fromInclusive));
        }

        protected abstract TreeMap.Entry first();
        protected abstract TreeMap.Entry last();
        protected abstract TreeMap.Entry lower(Object key);
        protected abstract TreeMap.Entry floor(Object key);
        protected abstract TreeMap.Entry ceiling(Object key);
        protected abstract TreeMap.Entry higher(Object key);
        protected abstract TreeMap.Entry uncheckedHigher(TreeMap.Entry e);

        // absolute comparisons, for use by subclasses

        final TreeMap.Entry absLowest() {
            return checkHiRange((fromStart) ? getFirstEntry() :
                fromInclusive ? getCeilingEntry(fromKey) : getHigherEntry(fromKey));
        }

        final TreeMap.Entry absHighest() {
            return checkLoRange((toEnd) ? getLastEntry() :
                toInclusive ? getFloorEntry(toKey) : getLowerEntry(toKey));
        }

        final TreeMap.Entry absLower(Object key) {
            return absTooHigh(key) ? absHighest() : checkLoRange(getLowerEntry(key));
        }

        final TreeMap.Entry absFloor(Object key) {
            return absTooHigh(key) ? absHighest() : checkLoRange(getFloorEntry(key));
        }

        final TreeMap.Entry absCeiling(Object key) {
            return absTooLow(key) ? absLowest() : checkHiRange(getCeilingEntry(key));
        }

        final TreeMap.Entry absHigher(Object key) {
            return absTooLow(key) ? absLowest() : checkHiRange(getHigherEntry(key));
        }

        // navigable implementations, using subclass-defined comparisons

        public Map.Entry firstEntry() {
            TreeMap.Entry e = first();
            return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
        }

        public Object firstKey() {
            TreeMap.Entry e = first();
            if (e == null) throw new NoSuchElementException();
            return e.key;
        }

        public Map.Entry lastEntry() {
            TreeMap.Entry e = last();
            return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
        }

        public Object lastKey() {
            TreeMap.Entry e = last();
            if (e == null) throw new NoSuchElementException();
            return e.key;
        }

        public Map.Entry pollFirstEntry() {
            TreeMap.Entry e = first();
            if (e == null) return null;
            Map.Entry result = new SimpleImmutableEntry(e);
            delete(e);
            return result;
        }

        public java.util.Map.Entry pollLastEntry() {
            TreeMap.Entry e = last();
            if (e == null) return null;
            Map.Entry result = new SimpleImmutableEntry(e);
            delete(e);
            return result;
        }

        public Map.Entry lowerEntry(Object key) {
            TreeMap.Entry e = lower(key);
            return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
        }

        public Object lowerKey(Object key) {
            TreeMap.Entry e = lower(key);
            return (e == null) ? null : e.key;
        }

        public Map.Entry floorEntry(Object key) {
            TreeMap.Entry e = floor(key);
            return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
        }

        public Object floorKey(Object key) {
            TreeMap.Entry e = floor(key);
            return (e == null) ? null : e.key;
        }

        public Map.Entry ceilingEntry(Object key) {
            TreeMap.Entry e = ceiling(key);
            return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
        }

        public Object ceilingKey(Object key) {
            TreeMap.Entry e = ceiling(key);
            return (e == null) ? null : e.key;
        }

        public Map.Entry higherEntry(Object key) {
            TreeMap.Entry e = higher(key);
            return (e == null) ? null : new AbstractMap.SimpleImmutableEntry(e);
        }

        public Object higherKey(Object key) {
            TreeMap.Entry e = higher(key);
            return (e == null) ? null : e.key;
        }

        public NavigableSet descendingKeySet() {
            return descendingMap().navigableKeySet();
        }

        public SortedMap subMap(Object fromKey, Object toKey) {
            return subMap(fromKey, true, toKey, false);
        }

        public SortedMap headMap(Object toKey) {
            return headMap(toKey, false);
        }

        public SortedMap tailMap(Object fromKey) {
            return tailMap(fromKey, true);
        }

        public int size() {
            if (cachedSize < 0 || cacheVersion != modCount) {
                cachedSize = recalculateSize();
                cacheVersion = modCount;
            }
            return cachedSize;
        }

        private int recalculateSize() {
            TreeMap.Entry terminator = absHighest();
            Object terminalKey = terminator != null ? terminator.key : null;

            int size = 0;
            for (TreeMap.Entry e = absLowest(); e != null;
                 e = (e.key == terminalKey) ? null : successor(e)) {
                size++;
            }
            return size;
        }

        public boolean isEmpty() {
            return absLowest() == null;
        }

        public boolean containsKey(Object key) {
            return (inRange(key) && TreeMap.this.containsKey(key));
        }

        public Object get(Object key) {
            if (!inRange(key)) return null;
            else return TreeMap.this.get(key);
        }

        public Object put(Object key, Object value) {
            if (!inRange(key))
                throw new IllegalArgumentException("Key out of range");
            return TreeMap.this.put(key, value);
        }

        public Object remove(Object key) {
            if (!inRange(key)) return null;
            return TreeMap.this.remove(key);
        }

        public Set entrySet() {
            if (entrySet == null) {
                entrySet = new SubEntrySet();
            }
            return entrySet;
        }

        public Set keySet() {
            return navigableKeySet();
        }

        public NavigableSet navigableKeySet() {
            if (navigableKeySet == null) {
                navigableKeySet = new SubKeySet();
            }
            return navigableKeySet;
        }

        private TreeMap.Entry getMatchingSubEntry(Object o) {
            if (!(o instanceof Map.Entry)) return null;
            Map.Entry e = (Map.Entry)o;
            Object key = e.getKey();
            if (!inRange(key)) return null;
            TreeMap.Entry found = getEntry(key);
            return (found != null && eq(found.getValue(), e.getValue())) ? found : null;
        }

        class SubEntrySet extends AbstractSet {
            public int size() { return NavigableSubMap.this.size(); }
            public boolean isEmpty() { return NavigableSubMap.this.isEmpty(); }

            public boolean contains(Object o) {
                return getMatchingSubEntry(o) != null;
            }

            public boolean remove(Object o) {
                TreeMap.Entry e = getMatchingSubEntry(o);
                if (e == null) return false;
                delete(e);
                return true;
            }

            public Iterator iterator() {
                return new SubEntryIterator();
            }
        }

        class SubKeySet extends AbstractSet implements NavigableSet {
            public int size() { return NavigableSubMap.this.size(); }
            public boolean isEmpty() { return NavigableSubMap.this.isEmpty(); }
            public void clear() { NavigableSubMap.this.clear(); }

            public boolean contains(Object o) {
                return getEntry(o) != null;
            }

            public boolean remove(Object o) {
                if (!inRange(o)) return false;
                TreeMap.Entry found = getEntry(o);
                if (found == null) return false;
                delete(found);
                return true;
            }
            public SortedSet subSet(Object fromElement, Object toElement) {
                return subSet(fromElement, true, toElement, false);
            }
            public SortedSet headSet(Object toElement) {
                return headSet(toElement, false);
            }
            public SortedSet tailSet(Object fromElement) {
                return tailSet(fromElement, true);
            }

            public Iterator iterator() {
                return new SubKeyIterator(NavigableSubMap.this.entrySet().iterator());
            }

            public Iterator descendingIterator() {
                return new SubKeyIterator(NavigableSubMap.this.descendingMap().entrySet().iterator());
            }

            public Object lower(Object e)   { return NavigableSubMap.this.lowerKey(e); }
            public Object floor(Object e)   { return NavigableSubMap.this.floorKey(e); }
            public Object ceiling(Object e) { return NavigableSubMap.this.ceilingKey(e); }
            public Object higher(Object e)  { return NavigableSubMap.this.higherKey(e); }
            public Object first()           { return NavigableSubMap.this.firstKey(); }
            public Object last()            { return NavigableSubMap.this.lastKey(); }
            public Comparator comparator()  { return NavigableSubMap.this.comparator(); }

            public Object pollFirst() {
                Map.Entry e = NavigableSubMap.this.pollFirstEntry();
                return e == null? null : e.getKey();
            }
            public Object pollLast() {
                Map.Entry e = NavigableSubMap.this.pollLastEntry();
                return e == null? null : e.getKey();
            }

            public NavigableSet subSet(Object fromElement, boolean fromInclusive,
                                       Object toElement,   boolean toInclusive) {
                return (NavigableSet)(NavigableSubMap.this.subMap(fromElement, fromInclusive,
                                             toElement,   toInclusive)).keySet();
            }
            public NavigableSet headSet(Object toElement, boolean inclusive) {
                return (NavigableSet)(NavigableSubMap.this.headMap(toElement, inclusive)).keySet();
            }
            public NavigableSet tailSet(Object fromElement, boolean inclusive) {
                return (NavigableSet)(NavigableSubMap.this.tailMap(fromElement, inclusive)).keySet();
            }
            public NavigableSet descendingSet() {
                return (NavigableSet)NavigableSubMap.this.descendingMap().keySet();
            }
        }

        class SubEntryIterator extends BaseEntryIterator implements Iterator {
            final Object terminalKey;
            SubEntryIterator() {
                super(first());
                TreeMap.Entry terminator = last();
                this.terminalKey = terminator == null ? null : terminator.key;
            }
            public boolean hasNext() {
                return cursor != null;
            }
            public Object next() {
                TreeMap.Entry curr = cursor;
                if (curr == null) throw new NoSuchElementException();
                if (expectedModCount != modCount)
                    throw new ConcurrentModificationException();
                cursor = (curr.key == terminalKey) ? null : uncheckedHigher(curr);
                lastRet = curr;
                return curr;
            }
        }

        class SubKeyIterator implements Iterator {
            final Iterator itr;
            SubKeyIterator(Iterator itr) { this.itr = itr; }
            public boolean hasNext()     { return itr.hasNext(); }
            public Object next()         { return ((Map.Entry)itr.next()).getKey(); }
            public void remove()         { itr.remove(); }
        }
    }

    class AscendingSubMap extends NavigableSubMap {
        AscendingSubMap(boolean fromStart, Object fromKey, boolean fromInclusive,
                        boolean toEnd,     Object toKey,   boolean toInclusive) {
            super(fromStart, fromKey, fromInclusive, toEnd, toKey, toInclusive);
        }

        public Comparator comparator() {
            return comparator;
        }

        protected TreeMap.Entry first()             { return absLowest(); }
        protected TreeMap.Entry last()              { return absHighest(); }
        protected TreeMap.Entry lower(Object key)   { return absLower(key); }
        protected TreeMap.Entry floor(Object key)   { return absFloor(key); }
        protected TreeMap.Entry ceiling(Object key) { return absCeiling(key); }
        protected TreeMap.Entry higher(Object key)  { return absHigher(key); }

        protected TreeMap.Entry uncheckedHigher(TreeMap.Entry e) {
            return successor(e);
        }

        public NavigableMap subMap(Object fromKey, boolean fromInclusive,
                                   Object toKey, boolean toInclusive) {
            if (!inRange(fromKey, fromInclusive)) {
                throw new IllegalArgumentException("fromKey out of range");
            }
            if (!inRange(toKey, toInclusive)) {
                throw new IllegalArgumentException("toKey out of range");
            }
            return new AscendingSubMap(false, fromKey, fromInclusive,
                                       false, toKey, toInclusive);
        }

        public NavigableMap headMap(Object toKey, boolean toInclusive) {
            if (!inRange(toKey, toInclusive)) {
                throw new IllegalArgumentException("toKey out of range");
            }
            return new AscendingSubMap(fromStart, fromKey, fromInclusive,
                                       false, toKey, toInclusive);
        }

        public NavigableMap tailMap(Object fromKey, boolean fromInclusive) {
            if (!inRange(fromKey, fromInclusive)) {
                throw new IllegalArgumentException("fromKey out of range");
            }
            return new AscendingSubMap(false, fromKey, fromInclusive,
                                       toEnd, toKey, toInclusive);
        }

        public NavigableMap descendingMap() {
            if (descendingMap == null) {
                descendingMap =
                    new DescendingSubMap(fromStart, fromKey, fromInclusive,
                                         toEnd,     toKey,   toInclusive);
            }
            return descendingMap;
        }
    }

    class DescendingSubMap extends NavigableSubMap {
        DescendingSubMap(boolean fromStart, Object fromKey, boolean fromInclusive,
                         boolean toEnd,     Object toKey,   boolean toInclusive) {
            super(fromStart, fromKey, fromInclusive, toEnd, toKey, toInclusive);
        }

        public Comparator comparator() { return TreeMap.this.reverseComparator(); }

        protected TreeMap.Entry first()             { return absHighest(); }
        protected TreeMap.Entry last()              { return absLowest(); }
        protected TreeMap.Entry lower(Object key)   { return absHigher(key); }
        protected TreeMap.Entry floor(Object key)   { return absCeiling(key); }
        protected TreeMap.Entry ceiling(Object key) { return absFloor(key); }
        protected TreeMap.Entry higher(Object key)  { return absLower(key); }

        protected TreeMap.Entry uncheckedHigher(TreeMap.Entry e) {
            return predecessor(e);
        }

        public NavigableMap subMap(Object fromKey, boolean fromInclusive,
                                   Object toKey,   boolean toInclusive) {
            if (!inRange(fromKey, fromInclusive)) {
                throw new IllegalArgumentException("fromKey out of range");
            }
            if (!inRange(toKey, toInclusive)) {
                throw new IllegalArgumentException("toKey out of range");
            }
            return new DescendingSubMap(false, toKey, toInclusive,
                                        false, fromKey, fromInclusive);
        }

        public NavigableMap headMap(Object toKey, boolean toInclusive) {
            if (!inRange(toKey, toInclusive)) {
                throw new IllegalArgumentException("toKey out of range");
            }
            return new DescendingSubMap(false, toKey, toInclusive,
                                        this.toEnd, this.toKey, this.toInclusive);
        }

        public NavigableMap tailMap(Object fromKey, boolean fromInclusive) {
            if (!inRange(fromKey, fromInclusive)) {
                throw new IllegalArgumentException("fromKey out of range");
            }
            return new DescendingSubMap(this.fromStart, this.fromKey, this.fromInclusive,
                                        false, fromKey, fromInclusive);
        }

        public NavigableMap descendingMap() {
            if (descendingMap == null) {
                descendingMap =
                    new AscendingSubMap(fromStart, fromKey, fromInclusive,
                                        toEnd,     toKey,   toInclusive);

            }
            return descendingMap;
        }
    }

    // serialization

    static class IteratorIOException extends RuntimeException {
        IteratorIOException(java.io.IOException e) {
            super(e);
        }
        java.io.IOException getException() {
            return (java.io.IOException)getCause();
        }
    }

    static class IteratorNoClassException extends RuntimeException {
        IteratorNoClassException(ClassNotFoundException e) {
            super(e);
        }
        ClassNotFoundException getException() {
            return (ClassNotFoundException)getCause();
        }
    }

    static class IOIterator implements Iterator {
        final java.io.ObjectInputStream ois;
        int remaining;
        IOIterator(java.io.ObjectInputStream ois, int remaining) {
            this.ois = ois;
            this.remaining = remaining;
        }
        public boolean hasNext() {
            return remaining > 0;
        }
        public Object next() {
            if (remaining <= 0) throw new NoSuchElementException();
            remaining--;
            try {
                return new AbstractMap.SimpleImmutableEntry(ois.readObject(),
                                                            ois.readObject());
            }
            catch (java.io.IOException e) { throw new IteratorIOException(e); }
            catch (ClassNotFoundException e) { throw new IteratorNoClassException(e); }
        }
        public void remove() { throw new UnsupportedOperationException(); }
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();
        out.writeInt(size);
        for (Entry e = getFirstEntry(); e != null; e = successor(e)) {
            out.writeObject(e.key);
            out.writeObject(e.element);
        }
    }

    private void readObject(ObjectInputStream in)
        throws java.io.IOException, ClassNotFoundException
    {
        in.defaultReadObject();
        int size = in.readInt();
        try {
            buildFromSorted(new IOIterator(in, size), size);
        }
        catch (IteratorIOException e) {
            throw e.getException();
        }
        catch (IteratorNoClassException e) {
            throw e.getException();
        }
    }

    private class SubMap extends AbstractMap implements Serializable, NavigableMap {

        private static final long serialVersionUID = -6520786458950516097L;

        final Object fromKey, toKey;

        SubMap() { fromKey = toKey = null; }

        private Object readResolve() {
            return new AscendingSubMap(fromKey == null, fromKey, true,
                                       toKey == null, toKey, false);
        }

        public Map.Entry lowerEntry(Object key)   { throw new Error(); }
        public Object lowerKey(Object key)        { throw new Error(); }
        public Map.Entry floorEntry(Object key)   { throw new Error(); }
        public Object floorKey(Object key)        { throw new Error(); }
        public Map.Entry ceilingEntry(Object key) { throw new Error(); }
        public Object ceilingKey(Object key)      { throw new Error(); }
        public Map.Entry higherEntry(Object key)  { throw new Error(); }
        public Object higherKey(Object key)       { throw new Error(); }
        public Map.Entry firstEntry()             { throw new Error(); }
        public Map.Entry lastEntry()              { throw new Error(); }
        public Map.Entry pollFirstEntry()         { throw new Error(); }
        public Map.Entry pollLastEntry()          { throw new Error(); }
        public NavigableMap descendingMap()       { throw new Error(); }
        public NavigableSet navigableKeySet()     { throw new Error(); }
        public NavigableSet descendingKeySet()    { throw new Error(); }
        public Set entrySet()                     { throw new Error(); }

        public NavigableMap subMap(Object fromKey, boolean fromInclusive,
                                   Object toKey, boolean toInclusive) {
            throw new Error();
        }

        public NavigableMap headMap(Object toKey, boolean inclusive) {
            throw new Error();
        }

        public NavigableMap tailMap(Object fromKey, boolean inclusive) {
            throw new Error();
        }

        public SortedMap subMap(Object fromKey, Object toKey) {
            throw new Error();
        }

        public SortedMap headMap(Object toKey)     { throw new Error(); }
        public SortedMap tailMap(Object fromKey)   { throw new Error(); }
        public Comparator comparator()             { throw new Error(); }
        public Object firstKey()                   { throw new Error(); }
        public Object lastKey()                    { throw new Error(); }
    }
}
