/*		 
 * Copyright (C) 2002-2014 Sebastiano Vigna 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

package PACKAGE;

import it.unimi.dsi.fastutil.objects.AbstractObjectSortedSet;
import it.unimi.dsi.fastutil.objects.ObjectBidirectionalIterator;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import it.unimi.dsi.fastutil.objects.ObjectSortedSet;

import VALUE_PACKAGE.VALUE_COLLECTION;
import VALUE_PACKAGE.VALUE_ABSTRACT_COLLECTION;
import VALUE_PACKAGE.VALUE_ITERATOR;

import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedMap;
import java.util.NoSuchElementException;

#if #values(primitive)
import VALUE_PACKAGE.VALUE_LIST_ITERATOR;
#endif

/** A type-specific AVL tree map with a fast, small-footprint implementation.
 *
 * <P>The iterators provided by the views of this class are type-specific {@linkplain
 * it.unimi.dsi.fastutil.BidirectionalIterator bidirectional iterators}.
 * Moreover, the iterator returned by <code>iterator()</code> can be safely cast
 * to a type-specific {@linkplain java.util.ListIterator list iterator}.
 */

public class AVL_TREE_MAP KEY_VALUE_GENERIC extends ABSTRACT_SORTED_MAP  KEY_VALUE_GENERIC implements java.io.Serializable, Cloneable {

	/** A reference to the root entry. */
	protected transient Entry KEY_VALUE_GENERIC tree;

	/** Number of entries in this map. */
	protected int count;

	/** The first key in this map. */
	protected transient Entry KEY_VALUE_GENERIC firstEntry;

	/** The last key in this map. */
	protected transient Entry KEY_VALUE_GENERIC lastEntry;

	/** Cached set of entries. */
	protected transient volatile ObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC> entries;

	/** Cached set of keys. */
	protected transient volatile SORTED_SET KEY_GENERIC keys;

	/** Cached collection of values. */
	protected transient volatile VALUE_COLLECTION VALUE_GENERIC values;

	/** The value of this variable remembers, after a <code>put()</code> 
	 * or a <code>remove()</code>, whether the <em>domain</em> of the map
	 * has been modified. */
	protected transient boolean modified;

	/** This map's comparator, as provided in the constructor. */
	protected Comparator<? super KEY_GENERIC_CLASS> storedComparator;

	/** This map's actual comparator; it may differ from {@link #storedComparator} because it is
		always a type-specific comparator, so it could be derived from the former by wrapping. */
	protected transient KEY_COMPARATOR KEY_SUPER_GENERIC actualComparator;

	private static final long serialVersionUID = -7046029254386353129L;

	private static final boolean ASSERTS = ASSERTS_VALUE;

	{
		allocatePaths();
	}

	/** Creates a new empty tree map. 
	 */

	public AVL_TREE_MAP() {
		tree = null;
		count = 0;
	}

	/** Generates the comparator that will be actually used.
	 *
	 * <P>When a specific {@link Comparator} is specified and stored in {@link
	 * #storedComparator}, we must check whether it is type-specific.  If it is
	 * so, we can used directly, and we store it in {@link #actualComparator}. Otherwise,
	 * we generate on-the-fly an anonymous class that wraps the non-specific {@link Comparator}
	 * and makes it into a type-specific one.
	 */
	private void setActualComparator() {
#if #keyclass(Object)
		actualComparator = storedComparator;
#else
		/* If the provided comparator is already type-specific, we use it. Otherwise,
		   we use a wrapper anonymous class to fake that it is type-specific. */
		if ( storedComparator == null || storedComparator instanceof KEY_COMPARATOR ) actualComparator = (KEY_COMPARATOR)storedComparator;
		else actualComparator =	new KEY_COMPARATOR KEY_SUPER_GENERIC() {
				public int compare( KEY_GENERIC_TYPE k1, KEY_GENERIC_TYPE k2 ) {
					return storedComparator.compare( KEY2OBJ( k1 ), KEY2OBJ( k2 ) );
				}
				public int compare( KEY_GENERIC_CLASS ok1, KEY_GENERIC_CLASS ok2 ) {
					return storedComparator.compare( ok1, ok2 );
				}
			};
#endif
	}
	 

	/** Creates a new empty tree map with the given comparator.
	 *
	 * @param c a (possibly type-specific) comparator.
	 */

	public AVL_TREE_MAP( final Comparator<? super KEY_GENERIC_CLASS> c ) {
		this();
		storedComparator = c;
		setActualComparator();
	}


	/** Creates a new tree map copying a given map.
	 *
	 * @param m a {@link Map} to be copied into the new tree map. 
	 */
	 
	public AVL_TREE_MAP( final Map<? extends KEY_GENERIC_CLASS, ? extends VALUE_GENERIC_CLASS> m ) {
		this();
		putAll( m );
	}

	/** Creates a new tree map copying a given sorted map (and its {@link Comparator}).
	 *
	 * @param m a {@link SortedMap} to be copied into the new tree map. 
	 */
	 
	public AVL_TREE_MAP( final SortedMap<KEY_GENERIC_CLASS,VALUE_GENERIC_CLASS> m ) {
		this( m.comparator() );
		putAll( m );
	}

	/** Creates a new tree map copying a given map.
	 *
	 * @param m a type-specific map to be copied into the new tree map. 
	 */
	 
	public AVL_TREE_MAP( final MAP KEY_VALUE_EXTENDS_GENERIC m ) {
		this();
		putAll( m );
	}

	/** Creates a new tree map copying a given sorted map (and its {@link Comparator}).
	 *
	 * @param m a type-specific sorted map to be copied into the new tree map. 
	 */
	 
	public AVL_TREE_MAP( final SORTED_MAP KEY_VALUE_GENERIC m ) {
		this( m.comparator() );
		putAll( m );
	}

	/** Creates a new tree map using the elements of two parallel arrays and the given comparator.
	 *
	 * @param k the array of keys of the new tree map.
	 * @param v the array of corresponding values in the new tree map.
	 * @param c a (possibly type-specific) comparator.
	 * @throws IllegalArgumentException if <code>k</code> and <code>v</code> have different lengths.
	 */
	 
	public AVL_TREE_MAP( final KEY_GENERIC_TYPE[] k, final VALUE_GENERIC_TYPE v[], final Comparator<? super KEY_GENERIC_CLASS> c ) {
		this( c );
		if ( k.length != v.length ) throw new IllegalArgumentException( "The key array and the value array have different lengths (" + k.length + " and " + v.length + ")" );
		for( int i = 0; i < k.length; i++ ) this.put( k[ i ], v[ i ] );
	}

	/** Creates a new tree map using the elements of two parallel arrays.
	 *
	 * @param k the array of keys of the new tree map.
	 * @param v the array of corresponding values in the new tree map.
	 * @throws IllegalArgumentException if <code>k</code> and <code>v</code> have different lengths.
	 */
	 
	public AVL_TREE_MAP( final KEY_GENERIC_TYPE[] k, final VALUE_GENERIC_TYPE v[] ) {
		this( k, v, null );
	}

	/*
	 * The following methods implements some basic building blocks used by
	 * all accessors.  They are (and should be maintained) identical to those used in AVLTreeSet.drv.
	 *
	 * The put()/remove() code is derived from Ben Pfaff's GNU libavl
	 * (http://www.msu.edu/~pfaffben/avl/). If you want to understand what's
	 * going on, you should have a look at the literate code contained therein
	 * first.  
	 */


	/** Compares two keys in the right way. 
	 *
	 * <P>This method uses the {@link #actualComparator} if it is non-<code>null</code>.
	 * Otherwise, it resorts to primitive type comparisons or to {@link Comparable#compareTo(Object) compareTo()}.
	 *
	 * @param k1 the first key.
	 * @param k2 the second key.
	 * @return a number smaller than, equal to or greater than 0, as usual
	 * (i.e., when k1 &lt; k2, k1 = k2 or k1 &gt; k2, respectively).
	 */
	 
	SUPPRESS_WARNINGS_KEY_UNCHECKED
	final int compare( final KEY_GENERIC_TYPE k1, final KEY_GENERIC_TYPE k2 ) {
		return actualComparator == null ? KEY_CMP( k1, k2 ) : actualComparator.compare( k1, k2 );
	}



	/** Returns the entry corresponding to the given key, if it is in the tree; <code>null</code>, otherwise.
	 *
	 * @param k the key to search for.
	 * @return the corresponding entry, or <code>null</code> if no entry with the given key exists.
	 */

	final Entry KEY_VALUE_GENERIC findKey( final KEY_GENERIC_TYPE k ) {
		Entry KEY_VALUE_GENERIC e = tree;
		int cmp;
		 
		while ( e != null && ( cmp = compare( k, e.key ) ) != 0 ) e = cmp < 0 ? e.left() : e.right();

		return e;
	}

	/** Locates a key.
	 *
	 * @param k a key.
	 * @return the last entry on a search for the given key; this will be
	 * the given key, if it present; otherwise, it will be either the smallest greater key or the greatest smaller key.
	 */

	final Entry KEY_VALUE_GENERIC locateKey( final KEY_GENERIC_TYPE k ) {
		Entry KEY_VALUE_GENERIC e = tree, last = tree;
		int cmp = 0;
		  
		while ( e != null && ( cmp = compare( k, e.key ) ) != 0 ) {
			last = e;
			e = cmp < 0 ? e.left() : e.right();
		}
		  
		return cmp == 0 ? e : last;
	}

	/** This vector remembers the directions followed during 
	 * the current insertion. It suffices for about 2<sup>32</sup> entries. */
	private transient boolean dirPath[];

	private void allocatePaths() {
		dirPath = new boolean[ 48 ];
	}


	/* After execution of this method, modified is true iff a new entry has
	been inserted. */
	 
	public VALUE_GENERIC_TYPE put( final KEY_GENERIC_TYPE k, final VALUE_GENERIC_TYPE v ) {
		modified = false;

		if ( tree == null ) { // The case of the empty tree is treated separately.
			count++;
			tree = lastEntry = firstEntry = new Entry KEY_VALUE_GENERIC( k, v );
			modified = true;
		}
		else {
			Entry KEY_VALUE_GENERIC p = tree, q = null, y = tree, z = null, e = null, w = null;
			int cmp, i = 0;

			while( true ) {
				if ( ( cmp = compare( k, p.key ) ) == 0 ) {
					final VALUE_GENERIC_TYPE oldValue = p.value;
					p.value = v;
					return oldValue;
				}
					 
				if ( p.balance() != 0 ) {
					i = 0;
					z = q;
					y = p;
				}
					 
				if ( dirPath[ i++ ] = cmp > 0 ) {
					if ( p.succ() ) {
						count++;
						e = new Entry KEY_VALUE_GENERIC( k, v );
								
						modified = true; 
						if ( p.right == null ) lastEntry = e;
								
						e.left = p;
						e.right = p.right;
								
						p.right( e );
								
						break;
					}

					q = p;
					p = p.right;
				}
				else {
					if ( p.pred() ) {
						count++;
						e = new Entry KEY_VALUE_GENERIC( k, v );
								
						modified = true;
						if ( p.left == null ) firstEntry = e;
								
						e.right = p;
						e.left = p.left;
								
						p.left( e );

						break;
					}

					q = p;
					p = p.left;
				}
			}

			p = y;
			i = 0;

			while( p != e ) {
				if ( dirPath[ i ] ) p.incBalance();
				else p.decBalance();

				p = dirPath[ i++ ] ? p.right : p.left;
			}
				
			if ( y.balance() == -2 ) {
				Entry KEY_VALUE_GENERIC x = y.left;

				if ( x.balance() == -1 ) {
					w = x;
					if ( x.succ() ) {
						x.succ( false );
						y.pred( x );
					}
					else y.left = x.right;

					x.right = y;
					x.balance( 0 );
					y.balance( 0 );
				}
				else {
					if ( ASSERTS ) assert x.balance() == 1;

					w = x.right;
					x.right = w.left;
					w.left = x;
					y.left = w.right;
					w.right = y;
					if ( w.balance() == -1 ) {
						x.balance( 0 );
						y.balance( 1 );
					}
					else if ( w.balance() == 0 ) {
						x.balance( 0 );
						y.balance( 0 );
					}
					else {
						x.balance( -1 );
						y.balance( 0 );
					}
					w.balance( 0 );


					if ( w.pred() ) {
						x.succ( w );
						w.pred( false );
					}
					if ( w.succ() ) {
						y.pred( w );
						w.succ( false );
					}

				}
			}
			else if ( y.balance() == +2 ) {
				Entry KEY_VALUE_GENERIC x = y.right;

				if ( x.balance() == 1 ) {
					w = x;
					if ( x.pred() ) {
						x.pred( false );
						y.succ( x );
					}
					else y.right = x.left;

					x.left = y;
					x.balance( 0 );
					y.balance( 0 );
				}
				else {
					if ( ASSERTS ) assert x.balance() == -1;

					w = x.left;
					x.left = w.right;
					w.right = x;
					y.right = w.left;
					w.left = y;
					if ( w.balance() == 1 ) {
						x.balance( 0 );
						y.balance( -1 );
					}
					else if ( w.balance() == 0 ) {
						x.balance( 0 );
						y.balance( 0 );
					}
					else {
						x.balance( 1 );
						y.balance( 0 );
					}
					w.balance( 0 );


					if ( w.pred() ) {
						y.succ( w );
						w.pred( false );
					}
					if ( w.succ() ) {
						x.pred( w );
						w.succ( false );
					}

				}
			}
			else return defRetValue;

			if ( z == null ) tree = w;
			else {
				if ( z.left == y ) z.left = w;
				else z.right = w;
			}
		}

		if ( ASSERTS ) checkTree( tree );
		return defRetValue;
	}

	/** Finds the parent of an entry.
	 *
	 * @param e a node of the tree.
	 * @return the parent of the given node, or <code>null</code> for the root.
	 */

	private Entry KEY_VALUE_GENERIC parent( final Entry KEY_VALUE_GENERIC e ) {
		if ( e == tree ) return null;

		Entry KEY_VALUE_GENERIC x, y, p;
		x = y = e;

		while( true ) {
			if ( y.succ() ) {
				p = y.right;
				if ( p == null || p.left != e ) {
					while( ! x.pred() ) x = x.left;
					p = x.left;
				}
				return p;
			}
			else if ( x.pred() ) {
				p = x.left;
				if ( p == null || p.right != e ) {
					while( ! y.succ() ) y = y.right;
					p = y.right;
				}
				return p;
			}

			x = x.left;
			y = y.right;
		}
	}

	 
	/* After execution of this method, {@link #modified} is true iff an entry
	has been deleted. */

	SUPPRESS_WARNINGS_KEY_UNCHECKED
	public VALUE_GENERIC_TYPE REMOVE_VALUE( final KEY_TYPE k ) {
		modified = false;

		if ( tree == null ) return defRetValue;

		int cmp;
		Entry KEY_VALUE_GENERIC p = tree, q = null;
		boolean dir = false;
		final KEY_GENERIC_TYPE kk = KEY_GENERIC_CAST k;

		while( true ) {
			if ( ( cmp = compare( kk, p.key ) ) == 0 ) break;
			else if ( dir = cmp > 0 ) {
				q = p;
				if ( ( p = p.right() ) == null ) return defRetValue;
			}
			else {
				q = p;
				if ( ( p = p.left() ) == null ) return defRetValue;
			}
		}

		if ( p.left == null ) firstEntry = p.next();
		if ( p.right == null ) lastEntry = p.prev();

		if ( p.succ() ) {
			if ( p.pred() ) {
				if ( q != null ) {
					if ( dir ) q.succ( p.right );
					else q.pred( p.left );
				}
				else tree = dir ? p.right : p.left;
			}
			else {
				p.prev().right = p.right;

				if ( q != null ) {
					if ( dir ) q.right = p.left;
					else q.left = p.left;
				}
				else tree = p.left;
			}
		}
		else {
			Entry KEY_VALUE_GENERIC r = p.right;

			if ( r.pred() ) {
				r.left = p.left;
				r.pred( p.pred() );
				if ( ! r.pred() ) r.prev().right = r;
				if ( q != null ) {
					if ( dir ) q.right = r;
					else q.left = r;
				}
				else tree = r;

				r.balance( p.balance() );
				q = r;
				dir = true;

			}
			else {
				Entry KEY_VALUE_GENERIC s;

				while( true ) {
					s = r.left;
					if ( s.pred() ) break;
					r = s;
				}

				if ( s.succ() ) r.pred( s );
				else r.left = s.right;

				s.left = p.left;

				if ( ! p.pred() ) {
					p.prev().right = s;
					s.pred( false );
				}

				s.right = p.right;
				s.succ( false );
								
				if ( q != null ) {
					if ( dir ) q.right = s;
					else q.left = s;
				}
				else tree = s;

				s.balance( p.balance() );
				q = r;
				dir = false;
			}
		}

		Entry KEY_VALUE_GENERIC y;

		while( q != null ) {
			y = q;
			q = parent( y );

			if ( ! dir ) {
				dir = q != null && q.left != y;
				y.incBalance();

				if ( y.balance() == 1 ) break;
				else if ( y.balance() == 2 ) {

					Entry KEY_VALUE_GENERIC x = y.right;
					if ( ASSERTS ) assert x != null;
									 
					if ( x.balance() == -1 ) {
						Entry KEY_VALUE_GENERIC w;
								
						if ( ASSERTS ) assert x.balance() == -1;

						w = x.left;
						x.left = w.right;
						w.right = x;
						y.right = w.left;
						w.left = y;

						if ( w.balance() == 1 ) {
							x.balance( 0 );
							y.balance( -1 );
						}
						else if ( w.balance() == 0 ) {
							x.balance( 0 );
							y.balance( 0 );
						}
						else {
							if ( ASSERTS ) assert w.balance() == -1;

							x.balance( 1 );
							y.balance( 0 );
						}

						w.balance( 0 );

						if ( w.pred() ) {
							y.succ( w );
							w.pred( false );
						}
						if ( w.succ() ) {
							x.pred( w );
							w.succ( false );
						}

						if ( q != null ) {
							if ( dir ) q.right = w;
							else q.left = w;
						}
						else tree = w;
					}
					else {
						if ( q != null ) {
							if ( dir ) q.right = x;
							else q.left = x;
						}
						else tree = x;
										  
						if ( x.balance() == 0 ) {
							y.right = x.left;
							x.left = y;
							x.balance( -1 );
							y.balance( +1 );
							break;
						}
						if ( ASSERTS ) assert x.balance() == 1;

						if ( x.pred() ) {
							y.succ( true );
							x.pred( false );
						}
						else y.right = x.left;

						x.left = y;
						y.balance( 0 );
						x.balance( 0 );
					}
				}
			}
			else {
				dir = q != null && q.left != y;
				y.decBalance();

				if ( y.balance() == -1 ) break;
				else if ( y.balance() == -2 ) {

					Entry KEY_VALUE_GENERIC x = y.left;
					if ( ASSERTS ) assert x != null;
									 
					if ( x.balance() == 1 ) {
						Entry KEY_VALUE_GENERIC w;
								
						if ( ASSERTS ) assert x.balance() == 1;

						w = x.right;
						x.right = w.left;
						w.left = x;
						y.left = w.right;
						w.right = y;

						if ( w.balance() == -1 ) {
							x.balance( 0 );
							y.balance( 1 );
						}
						else if ( w.balance() == 0 ) {
							x.balance( 0 );
							y.balance( 0 );
						}
						else {
							if ( ASSERTS ) assert w.balance() == 1;

							x.balance( -1 );
							y.balance( 0 );
						}

						w.balance( 0 );

						if ( w.pred() ) {
							x.succ( w );
							w.pred( false );
						}
						if ( w.succ() ) {
							y.pred( w );
							w.succ( false );
						}

						if ( q != null ) {
							if ( dir ) q.right = w;
							else q.left = w;
						}
						else tree = w;
					}
					else {
						if ( q != null ) {
							if ( dir ) q.right = x;
							else q.left = x;
						}
						else tree = x;
										  
						if ( x.balance() == 0 ) {
							y.left = x.right;
							x.right = y;
							x.balance( +1 );
							y.balance( -1 );
							break;
						}
						if ( ASSERTS ) assert x.balance() == -1;

						if ( x.succ() ) {
							y.pred( true );
							x.succ( false );
						}
						else y.left = x.right;

						x.right = y;
						y.balance( 0 );
						x.balance( 0 );
					}
				}  
			}
		}

		modified = true;
		count--;
		if ( ASSERTS ) checkTree( tree );
		return p.value;
	}
	 

#if ! #keyclass(Object) || #values(primitive)
	public VALUE_GENERIC_CLASS put( final KEY_GENERIC_CLASS ok, final VALUE_GENERIC_CLASS ov ) {
		final VALUE_GENERIC_TYPE oldValue = put( KEY_CLASS2TYPE(ok), VALUE_CLASS2TYPE(ov) );
		return modified ? OBJECT_DEFAULT_RETURN_VALUE : VALUE2OBJ( oldValue );
	}
#endif



#if ! #keyclass(Object) || #values(primitive)
	public VALUE_GENERIC_CLASS remove( final Object ok ) {
		final VALUE_GENERIC_TYPE oldValue = REMOVE_VALUE( KEY_OBJ2TYPE( ok ) );
		return modified ? VALUE2OBJ( oldValue ) : OBJECT_DEFAULT_RETURN_VALUE;
	}
#endif


	public boolean containsValue( final VALUE_TYPE v ) {
		final ValueIterator i = new ValueIterator();
		VALUE_GENERIC_TYPE ev;
		  
		int j = count;
		while( j-- != 0 ) {
			ev = i.NEXT_VALUE();
			if ( VALUE_EQUALS( ev, v ) ) return true;
		}
		  
		return false;
	}

	public void clear() {
		count = 0;
		tree = null;
		entries = null;
		values = null;
		keys = null;
		firstEntry = lastEntry = null;
	}

	 
	/** This class represent an entry in a tree map.
	 *
	 * <P>We use the only "metadata", i.e., {@link Entry#info}, to store
	 * information about balance, predecessor status and successor status.
	 *
	 * <P>Note that since the class is recursive, it can be
	 * considered equivalently a tree.
	 */

	private static final class Entry KEY_VALUE_GENERIC implements Cloneable, MAP.Entry KEY_VALUE_GENERIC {
		/** If the bit in this mask is true, {@link #right} points to a successor. */
		private final static int SUCC_MASK = 1 << 31;
		/** If the bit in this mask is true, {@link #left} points to a predecessor. */
		private final static int PRED_MASK = 1 << 30;
		/** The bits in this mask hold the node balance info. You can get it just by casting to byte. */
		private final static int BALANCE_MASK = 0xFF;
		/** The key of this entry. */
		KEY_GENERIC_TYPE key;
		/** The value of this entry. */
		VALUE_GENERIC_TYPE value;
		/** The pointers to the left and right subtrees. */
		Entry KEY_VALUE_GENERIC left, right;
		/** This integers holds different information in different bits (see {@link #SUCC_MASK}, {@link #PRED_MASK} and {@link #BALANCE_MASK}). */
		int info;

		Entry() {}

		/** Creates a new entry with the given key and value.
		 *
		 * @param k a key.
		 * @param v a value.
		 */
		Entry( final KEY_GENERIC_TYPE k, final VALUE_GENERIC_TYPE v ) {
			this.key = k;
			this.value = v;
			info = SUCC_MASK | PRED_MASK;
		}
		  
		/** Returns the left subtree. 
		 *
		 * @return the left subtree (<code>null</code> if the left
		 * subtree is empty).
		 */
		Entry KEY_VALUE_GENERIC left() {
			return ( info & PRED_MASK ) != 0 ? null : left;
		}
		  
		/** Returns the right subtree. 
		 *
		 * @return the right subtree (<code>null</code> if the right
		 * subtree is empty).
		 */
		Entry KEY_VALUE_GENERIC right() {
			return ( info & SUCC_MASK ) != 0 ? null : right;
		}
		  
		/** Checks whether the left pointer is really a predecessor.
		 * @return true if the left pointer is a predecessor.
		 */
		boolean pred() {
			return ( info & PRED_MASK ) != 0;
		}
		  
		/** Checks whether the right pointer is really a successor.
		 * @return true if the right pointer is a successor.
		 */
		boolean succ() {
			return ( info & SUCC_MASK ) != 0;
		}
		  
		/** Sets whether the left pointer is really a predecessor.
		 * @param pred if true then the left pointer will be considered a predecessor.
		 */
		void pred( final boolean pred ) {
			if ( pred ) info |= PRED_MASK;
			else info &= ~PRED_MASK;
		}
		  
		/** Sets whether the right pointer is really a successor.
		 * @param succ if true then the right pointer will be considered a successor.
		 */
		void succ( final boolean succ ) {
			if ( succ ) info |= SUCC_MASK;
			else info &= ~SUCC_MASK;
		}
		  
		/** Sets the left pointer to a predecessor.
		 * @param pred the predecessr.
		 */
		void pred( final Entry KEY_VALUE_GENERIC pred ) {
			info |= PRED_MASK;
			left = pred;
		}
		  
		/** Sets the right pointer to a successor.
		 * @param succ the successor.
		 */
		void succ( final Entry KEY_VALUE_GENERIC succ ) {
			info |= SUCC_MASK;
			right = succ;
		}
		  
		/** Sets the left pointer to the given subtree.
		 * @param left the new left subtree.
		 */
		void left( final Entry KEY_VALUE_GENERIC left ) {
			info &= ~PRED_MASK;
			this.left = left;
		}
		  
		/** Sets the right pointer to the given subtree.
		 * @param right the new right subtree.
		 */
		void right( final Entry KEY_VALUE_GENERIC right ) {
			info &= ~SUCC_MASK;
			this.right = right;
		}
		  
		/** Returns the current level of the node.
		 * @return the current level of this node.
		 */
		int balance() {
			return (byte)info;
		}

		/** Sets the level of this node.
		 * @param level the new level of this node.
		 */
		void balance( int level ) {
			info &= ~BALANCE_MASK;
			info |= ( level & BALANCE_MASK );
		}

		/** Increments the level of this node. */
		void incBalance() {
			info = info & ~BALANCE_MASK | ( (byte)info + 1 ) & 0xFF;
		}
		  
		/** Decrements the level of this node. */
		protected	void decBalance() {
			info = info & ~BALANCE_MASK | ( (byte)info - 1 ) & 0xFF;
		}

		/** Computes the next entry in the set order.
		 *
		 * @return the next entry (<code>null</code>) if this is the last entry).
		 */

		Entry KEY_VALUE_GENERIC next() {
			Entry KEY_VALUE_GENERIC next = this.right;
			if ( ( info & SUCC_MASK ) == 0 ) while ( ( next.info & PRED_MASK ) == 0 ) next = next.left;
			return next;
		}

		/** Computes the previous entry in the set order.
		 *
		 * @return the previous entry (<code>null</code>) if this is the first entry).
		 */

		Entry KEY_VALUE_GENERIC prev() {
			Entry KEY_VALUE_GENERIC prev = this.left;
			if ( ( info & PRED_MASK ) == 0 ) while ( ( prev.info & SUCC_MASK ) == 0 ) prev = prev.right;
			return prev;
		}

		public KEY_GENERIC_CLASS getKey() {
			return KEY2OBJ(key);
		}
		  
#if ! #keyclass(Object)
		public KEY_GENERIC_TYPE ENTRY_GET_KEY() {
			return key;
		}
#endif
		  
		public VALUE_GENERIC_CLASS getValue() {
			return VALUE2OBJ(value);
		}
		  
#if #values(primitive)
		public VALUE_TYPE ENTRY_GET_VALUE() {
			return value;
		}
#endif
		  
		public VALUE_GENERIC_TYPE setValue(final VALUE_GENERIC_TYPE value) {
			final VALUE_GENERIC_TYPE oldValue = this.value;
			this.value = value;
			return oldValue;
		}
		  
#if #values(primitive)
		  
		public VALUE_GENERIC_CLASS setValue(final VALUE_GENERIC_CLASS value) {
			return VALUE2OBJ(setValue(VALUE_CLASS2TYPE(value)));
		}
		  
#endif

		SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED
		public Entry KEY_VALUE_GENERIC clone() {
			Entry KEY_VALUE_GENERIC c;
			try {
				c = (Entry KEY_VALUE_GENERIC)super.clone();
			}
			catch(CloneNotSupportedException cantHappen) {
				throw new InternalError();
			}

			c.key = key;
			c.value = value;
			c.info = info;

			return c;
		}

		@SuppressWarnings("unchecked")
		public boolean equals( final Object o ) {
			if (!(o instanceof Map.Entry)) return false;
			Map.Entry<KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS> e = (Map.Entry<KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS>)o;
				
			return KEY_EQUALS( key, KEY_CLASS2TYPE( e.getKey() ) ) && VALUE_EQUALS( value, VALUE_CLASS2TYPE( e.getValue() ) );
		}
		  
		public int hashCode() {
			return KEY2JAVAHASH_NOT_NULL(key) ^ VALUE2JAVAHASH(value);
		}
		  
		  
		public String toString() {
			return key + "=>" + value;
		}
		  

		/*
		public void prettyPrint() {
			prettyPrint(0);
		}

		public void prettyPrint(int level) {
			if ( pred() ) {
				for (int i = 0; i < level; i++)
					System.err.print("  ");
				System.err.println("pred: " + left );
			}
			else if (left != null)
				left.prettyPrint(level +1 );
			for (int i = 0; i < level; i++)
				System.err.print("  ");
			System.err.println(key + "=" + value + " (" + balance() + ")");
			if ( succ() ) {
				for (int i = 0; i < level; i++)
					System.err.print("  ");
				System.err.println("succ: " + right );
			}
			else if (right != null)
				right.prettyPrint(level + 1);
		}
		*/
	}
	 
	/*
	public void prettyPrint() {
		System.err.println("size: " + count);
		if (tree != null) tree.prettyPrint();
	}
	*/

	SUPPRESS_WARNINGS_KEY_UNCHECKED
	public boolean containsKey( final KEY_TYPE k ) {
		return findKey( KEY_GENERIC_CAST k ) != null;
	}
	 
	public int size() {
		return count;
	}
	 
	public boolean isEmpty() {
		return count == 0;
	}
	 
	 
	SUPPRESS_WARNINGS_KEY_UNCHECKED
	public VALUE_GENERIC_TYPE GET_VALUE( final KEY_TYPE k ) {
		final Entry KEY_VALUE_GENERIC e = findKey( KEY_GENERIC_CAST k );
		return e == null ? defRetValue : e.value;
	}

#if #keyclass(Object) && #values(primitive)

	SUPPRESS_WARNINGS_KEY_UNCHECKED
	public VALUE_GENERIC_CLASS get( final Object ok ) {
		final Entry KEY_VALUE_GENERIC e = findKey( KEY_GENERIC_CAST ok );
		return e == null ? OBJECT_DEFAULT_RETURN_VALUE : e.getValue();
	}

#endif

	public KEY_GENERIC_TYPE FIRST_KEY() {
		if ( tree == null ) throw new NoSuchElementException();
		return firstEntry.key;
	}

	public KEY_GENERIC_TYPE LAST_KEY() {
		if ( tree == null ) throw new NoSuchElementException();
		return lastEntry.key;
	}


	/** An abstract iterator on the whole range.
	 *
	 * <P>This class can iterate in both directions on a threaded tree.
	 */

	private class TreeIterator {
		/** The entry that will be returned by the next call to {@link java.util.ListIterator#previous()} (or <code>null</code> if no previous entry exists). */
		Entry KEY_VALUE_GENERIC prev;
		/** The entry that will be returned by the next call to {@link java.util.ListIterator#next()} (or <code>null</code> if no next entry exists). */
		Entry KEY_VALUE_GENERIC next;
		/** The last entry that was returned (or <code>null</code> if we did not iterate or used {@link #remove()}). */
		Entry KEY_VALUE_GENERIC curr;
		/** The current index (in the sense of a {@link java.util.ListIterator}). Note that this value is not meaningful when this {@link TreeIterator} has been created using the nonempty constructor.*/
		int index = 0;
		  
		TreeIterator() {
			next = firstEntry;
		}

		TreeIterator( final KEY_GENERIC_TYPE k ) {
			if ( ( next = locateKey( k ) ) != null ) {
				if ( compare( next.key, k ) <= 0 ) {
					prev = next;
					next = next.next();
				}
				else prev = next.prev();
			}
		}

		public boolean hasNext() { return next != null; }
		public boolean hasPrevious() { return prev != null; }

		void updateNext() {
			next = next.next();
		}

		Entry KEY_VALUE_GENERIC nextEntry() {
			if ( ! hasNext() ) throw new NoSuchElementException();
			curr = prev = next;
			index++;
			updateNext();
			return curr;
		}

		void updatePrevious() {
			prev = prev.prev();
		}

		Entry KEY_VALUE_GENERIC previousEntry() {
			if ( ! hasPrevious() ) throw new NoSuchElementException();
			curr = next = prev;
			index--;
			updatePrevious();
			return curr;
		}

		public int nextIndex() {
			return index;
		}

		public int previousIndex() {
			return index - 1;
		}

		public void remove() {
			if ( curr == null ) throw new IllegalStateException();
			/* If the last operation was a next(), we are removing an entry that preceeds
			   the current index, and thus we must decrement it. */
			if ( curr == prev ) index--;
			next = prev = curr;
			updatePrevious();
			updateNext();
			AVL_TREE_MAP.this.REMOVE_VALUE( curr.key );
			curr = null;
		}

		public int skip( final int n ) { 
			int i = n;
			while( i-- != 0 && hasNext() ) nextEntry(); 
			return n - i - 1;
		}

		public int back( final int n ) { 
			int i = n;
			while( i-- != 0 && hasPrevious() ) previousEntry(); 
			return n - i - 1;
		}
	}


	/** An iterator on the whole range.
	 *
	 * <P>This class can iterate in both directions on a threaded tree.
	 */

	private class EntryIterator extends TreeIterator implements ObjectListIterator<MAP.Entry KEY_VALUE_GENERIC> {
		EntryIterator() {}

		EntryIterator( final KEY_GENERIC_TYPE k ) {
			super( k );
		}

		public MAP.Entry KEY_VALUE_GENERIC next() { return nextEntry(); }
		public MAP.Entry KEY_VALUE_GENERIC previous() { return previousEntry(); }

		public void set( MAP.Entry KEY_VALUE_GENERIC ok ) { throw new UnsupportedOperationException(); }
		public void add( MAP.Entry KEY_VALUE_GENERIC ok ) { throw new UnsupportedOperationException(); }
	}


	public ObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC> ENTRYSET() {
		if ( entries == null ) entries = new AbstractObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC>() {
				final Comparator<? super MAP.Entry KEY_VALUE_GENERIC> comparator = new Comparator<MAP.Entry KEY_VALUE_GENERIC> () {
					public int compare( final MAP.Entry KEY_VALUE_GENERIC x, final MAP.Entry KEY_VALUE_GENERIC y ) {
						return AVL_TREE_MAP.this.actualComparator.compare( x.ENTRY_GET_KEY(), y.ENTRY_GET_KEY() );
					}
				};

				public Comparator<? super MAP.Entry KEY_VALUE_GENERIC> comparator() {
					return comparator;
				}

				public ObjectBidirectionalIterator<MAP.Entry KEY_VALUE_GENERIC> iterator() {
					return new EntryIterator();
				}

				public ObjectBidirectionalIterator<MAP.Entry KEY_VALUE_GENERIC> iterator( final MAP.Entry KEY_VALUE_GENERIC from ) {
					return new EntryIterator( from.ENTRY_GET_KEY() );
				}

				@SuppressWarnings("unchecked")
				public boolean contains( final Object o ) {
					if (!(o instanceof Map.Entry)) return false;
					final Map.Entry <KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS> e = (Map.Entry <KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS>)o;
					final Entry KEY_VALUE_GENERIC f = findKey( KEY_CLASS2TYPE( e.getKey() ) );
					return e.equals( f );
				}					 

				@SuppressWarnings("unchecked")
				public boolean remove( final Object o ) {
					if (!(o instanceof Map.Entry)) return false;
					final Map.Entry <KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS> e = (Map.Entry <KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS>)o;
					final Entry KEY_VALUE_GENERIC f = findKey( KEY_CLASS2TYPE( e.getKey() ) );
					if ( f != null ) AVL_TREE_MAP.this.REMOVE_VALUE( f.key );
					return f != null;
				}

				public int size() { return count; }
				public void clear() { AVL_TREE_MAP.this.clear(); }
					 
				public MAP.Entry KEY_VALUE_GENERIC first() { return firstEntry; }
				public MAP.Entry KEY_VALUE_GENERIC last() { return lastEntry; }
				public ObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC> subSet( MAP.Entry KEY_VALUE_GENERIC from, MAP.Entry KEY_VALUE_GENERIC to  ) { return subMap( from.ENTRY_GET_KEY(), to.ENTRY_GET_KEY() ).ENTRYSET(); }
				public ObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC> headSet( MAP.Entry KEY_VALUE_GENERIC to  ) { return headMap( to.ENTRY_GET_KEY() ).ENTRYSET(); }
				public ObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC> tailSet( MAP.Entry KEY_VALUE_GENERIC from  ) { return tailMap( from.ENTRY_GET_KEY() ).ENTRYSET(); }
			};

		return entries;
	}

	/** An iterator on the whole range of keys.
	 *
	 * <P>This class can iterate in both directions on the keys of a threaded tree. We 
	 * simply override the {@link java.util.ListIterator#next()}/{@link java.util.ListIterator#previous()} methods (and possibly
	 * their type-specific counterparts) so that they return keys instead of entries.
	 */
	private final class KeyIterator extends TreeIterator implements KEY_LIST_ITERATOR KEY_GENERIC {
		public KeyIterator() {}
		public KeyIterator( final KEY_GENERIC_TYPE k ) { super( k ); }
		public KEY_GENERIC_TYPE NEXT_KEY() { return nextEntry().key; }
		public KEY_GENERIC_TYPE PREV_KEY() { return previousEntry().key; }

		public void set( KEY_GENERIC_TYPE k ) { throw new UnsupportedOperationException(); }
		public void add( KEY_GENERIC_TYPE k ) { throw new UnsupportedOperationException(); }

#if !#keyclass(Object)
		public KEY_GENERIC_CLASS next() { return KEY2OBJ( nextEntry().key ); }
		public KEY_GENERIC_CLASS previous() { return KEY2OBJ( previousEntry().key ); }
		public void set( KEY_CLASS ok ) { throw new UnsupportedOperationException(); }
		public void add( KEY_CLASS ok ) { throw new UnsupportedOperationException(); }
#endif						
	};

	/** A keyset implementation using a more direct implementation for iterators. */
	private class KeySet extends ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC.KeySet {
		public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new KeyIterator(); }
		public KEY_BIDI_ITERATOR KEY_GENERIC iterator( final KEY_GENERIC_TYPE from ) { return new KeyIterator( from ); }
	}

	/** Returns a type-specific sorted set view of the keys contained in this map.
	 *
	 * <P>In addition to the semantics of {@link java.util.Map#keySet()}, you can
	 * safely cast the set returned by this call to a type-specific sorted
	 * set interface.
	 *
	 * @return a type-specific sorted set view of the keys contained in this map.
	 */
	public SORTED_SET KEY_GENERIC keySet() {
		if ( keys == null ) keys = new KeySet();
		return keys;
	}

	/** An iterator on the whole range of values.
	 *
	 * <P>This class can iterate in both directions on the values of a threaded tree. We 
	 * simply override the {@link java.util.ListIterator#next()}/{@link java.util.ListIterator#previous()} methods (and possibly
	 * their type-specific counterparts) so that they return values instead of entries.
	 */
	private final class ValueIterator extends TreeIterator implements VALUE_LIST_ITERATOR VALUE_GENERIC {
		public VALUE_GENERIC_TYPE NEXT_VALUE() { return nextEntry().value; }
		public VALUE_GENERIC_TYPE PREV_VALUE() { return previousEntry().value; }
		public void set( VALUE_GENERIC_TYPE v ) { throw new UnsupportedOperationException(); }
		public void add( VALUE_GENERIC_TYPE v ) { throw new UnsupportedOperationException(); }

#if #values(primitive)
		public VALUE_GENERIC_CLASS next() { return VALUE2OBJ( nextEntry().value ); }
		public VALUE_GENERIC_CLASS previous() { return VALUE2OBJ( previousEntry().value ); }
		public void set( VALUE_CLASS ok ) { throw new UnsupportedOperationException(); }
		public void add( VALUE_CLASS ok ) { throw new UnsupportedOperationException(); }
#endif									
	};

	/** Returns a type-specific collection view of the values contained in this map.
	 *
	 * <P>In addition to the semantics of {@link java.util.Map#values()}, you can
	 * safely cast the collection returned by this call to a type-specific collection
	 * interface.
	 *
	 * @return a type-specific collection view of the values contained in this map.
	 */

	public VALUE_COLLECTION VALUE_GENERIC values() {
		if ( values == null ) values = new VALUE_ABSTRACT_COLLECTION VALUE_GENERIC() {
				public VALUE_ITERATOR VALUE_GENERIC iterator() {
					return new ValueIterator();
				}

				public boolean contains( final VALUE_TYPE k ) {
					return containsValue( k );
				} 

				public int size() {
					return count;
				}
					 
				public void clear() {
					AVL_TREE_MAP.this.clear();
				}
					 
			};

		return values;
	}

	public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() {
		return actualComparator;
	}

	public SORTED_MAP KEY_VALUE_GENERIC headMap( KEY_GENERIC_TYPE to ) {
		return new Submap( KEY_NULL, true, to, false );
	}

	public SORTED_MAP KEY_VALUE_GENERIC tailMap( KEY_GENERIC_TYPE from ) {
		return new Submap( from, false, KEY_NULL, true );
	}

	public SORTED_MAP KEY_VALUE_GENERIC subMap( KEY_GENERIC_TYPE from,  KEY_GENERIC_TYPE to ) {
		return new Submap( from, false, to, false );
	}

	/** A submap with given range.
	 *
	 * <P>This class represents a submap. One has to specify the left/right
	 * limits (which can be set to -&infin; or &infin;). Since the submap is a
	 * view on the map, at a given moment it could happen that the limits of
	 * the range are not any longer in the main map. Thus, things such as
	 * {@link java.util.SortedMap#firstKey()} or {@link java.util.Collection#size()} must be always computed
	 * on-the-fly.  
	 */
	private final class Submap extends ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC implements java.io.Serializable {
    	private static final long serialVersionUID = -7046029254386353129L;

		/** The start of the submap range, unless {@link #bottom} is true. */
		KEY_GENERIC_TYPE from;
		/** The end of the submap range, unless {@link #top} is true. */
		KEY_GENERIC_TYPE to;
		/** If true, the submap range starts from -&infin;. */
		boolean bottom;
		/** If true, the submap range goes to &infin;. */
		boolean top;
		/** Cached set of entries. */
		@SuppressWarnings("hiding")
		protected transient volatile ObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC> entries;
		/** Cached set of keys. */
		@SuppressWarnings("hiding")
 		protected transient volatile SORTED_SET KEY_GENERIC keys;
		/** Cached collection of values. */
		@SuppressWarnings("hiding")
		protected transient volatile VALUE_COLLECTION VALUE_GENERIC values;

		/** Creates a new submap with given key range.
		 *
		 * @param from the start of the submap range.
		 * @param bottom if true, the first parameter is ignored and the range starts from -&infin;.
		 * @param to the end of the submap range.
		 * @param top if true, the third parameter is ignored and the range goes to &infin;.
		 */
		public Submap( final KEY_GENERIC_TYPE from, final boolean bottom, final KEY_GENERIC_TYPE to, final boolean top ) {
			if ( ! bottom && ! top && AVL_TREE_MAP.this.compare( from, to ) > 0 ) throw new IllegalArgumentException( "Start key (" + from  + ") is larger than end key (" + to + ")" );

			this.from = from;
			this.bottom = bottom;
			this.to = to;
			this.top = top;
			this.defRetValue = AVL_TREE_MAP.this.defRetValue;
		}

		public void clear() {
			final SubmapIterator i = new SubmapIterator();
			while( i.hasNext() ) {
				i.nextEntry();
				i.remove();
			}
		}

		/** Checks whether a key is in the submap range.
		 * @param k a key.
		 * @return true if is the key is in the submap range.
		 */
		final boolean in( final KEY_GENERIC_TYPE k ) {
			return ( bottom || AVL_TREE_MAP.this.compare( k, from ) >= 0 ) &&
				( top || AVL_TREE_MAP.this.compare( k, to ) < 0 );
		}

		public ObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC> ENTRYSET() {
			if ( entries == null ) entries = new AbstractObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC>() {
					public ObjectBidirectionalIterator<MAP.Entry KEY_VALUE_GENERIC> iterator() {
						return new SubmapEntryIterator();
					}
						  
					public ObjectBidirectionalIterator<MAP.Entry KEY_VALUE_GENERIC> iterator( final MAP.Entry KEY_VALUE_GENERIC from ) {
						return new SubmapEntryIterator( from.ENTRY_GET_KEY() );
					}

					public Comparator<? super MAP.Entry KEY_VALUE_GENERIC> comparator() { return AVL_TREE_MAP.this.entrySet().comparator(); }

					@SuppressWarnings("unchecked")
					public boolean contains( final Object o ) {
						if (!(o instanceof Map.Entry)) return false;
						final Map.Entry <KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS> e = (Map.Entry <KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS>)o;
						final AVL_TREE_MAP.Entry KEY_VALUE_GENERIC f = findKey( KEY_CLASS2TYPE( e.getKey() ) );
						return f != null && in( f.key ) && e.equals( f );
					}					 

					@SuppressWarnings("unchecked")
					public boolean remove( final Object o ) {
						if (!(o instanceof Map.Entry)) return false;
						final Map.Entry <KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS> e = (Map.Entry <KEY_GENERIC_CLASS, VALUE_GENERIC_CLASS>)o;
						final AVL_TREE_MAP.Entry KEY_VALUE_GENERIC f = findKey( KEY_CLASS2TYPE( e.getKey() ) );
						if ( f != null && in( f.key ) ) Submap.this.REMOVE_VALUE( f.key );
						return f != null;
					}

					public int size() {
						int c = 0;
						for( Iterator<?> i = iterator(); i.hasNext(); i.next() ) c++;
						return c;
					}
					 

					public boolean isEmpty() {
						return ! new SubmapIterator().hasNext();
					}

					public void clear() {
						Submap.this.clear();
					}
					 
					public MAP.Entry KEY_VALUE_GENERIC first() { return firstEntry(); }
					public MAP.Entry KEY_VALUE_GENERIC last() { return lastEntry(); }
					public ObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC> subSet( MAP.Entry KEY_VALUE_GENERIC from, MAP.Entry KEY_VALUE_GENERIC to  ) { return subMap( from.ENTRY_GET_KEY(), to.ENTRY_GET_KEY() ).ENTRYSET(); }
					public ObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC> headSet( MAP.Entry KEY_VALUE_GENERIC to  ) { return headMap( to.ENTRY_GET_KEY() ).ENTRYSET(); }
					public ObjectSortedSet<MAP.Entry KEY_VALUE_GENERIC> tailSet( MAP.Entry KEY_VALUE_GENERIC from  ) { return tailMap( from.ENTRY_GET_KEY() ).ENTRYSET(); }
				};

			return entries;
		}

		private class KeySet extends ABSTRACT_SORTED_MAP KEY_VALUE_GENERIC.KeySet {
			public KEY_BIDI_ITERATOR KEY_GENERIC iterator() { return new SubmapKeyIterator();	}
			public KEY_BIDI_ITERATOR KEY_GENERIC iterator( final KEY_GENERIC_TYPE from ) { return new SubmapKeyIterator( from ); }
		}

		public SORTED_SET KEY_GENERIC keySet() {
			if ( keys == null ) keys = new KeySet();
			return keys;
		}
		  
		public VALUE_COLLECTION VALUE_GENERIC values() {
			if ( values == null ) values = new VALUE_ABSTRACT_COLLECTION VALUE_GENERIC() {
					public VALUE_ITERATOR VALUE_GENERIC iterator() {
						return new SubmapValueIterator();
					}

					public boolean contains( final VALUE_TYPE k ) {
						return containsValue( k );
					} 

					public int size() {
						return Submap.this.size();
					}
						  
					public void clear() {
						Submap.this.clear();
					}
						  
				};
				
			return values;
		}
		  
		SUPPRESS_WARNINGS_KEY_UNCHECKED
		public boolean containsKey( final KEY_TYPE k ) {
			return in( KEY_GENERIC_CAST k ) && AVL_TREE_MAP.this.containsKey( k );
		}

		public boolean containsValue( final VALUE_TYPE v ) {
			final SubmapIterator i = new SubmapIterator();
			VALUE_TYPE ev;
				
			while( i.hasNext() ) {
				ev = i.nextEntry().value;
				if ( VALUE_EQUALS( ev, v ) ) return true;
			}
				
			return false;
		}
		  

		SUPPRESS_WARNINGS_KEY_UNCHECKED
		public VALUE_GENERIC_TYPE GET_VALUE(final KEY_TYPE k) {
			final AVL_TREE_MAP.Entry KEY_VALUE_GENERIC e;
			final KEY_GENERIC_TYPE kk = KEY_GENERIC_CAST k;
			return in( kk ) && ( e = findKey( kk ) ) != null ? e.value : this.defRetValue;
		}
		  
		  
#if #keyclass(Object) && #values(primitive)

		SUPPRESS_WARNINGS_KEY_UNCHECKED
		public VALUE_GENERIC_CLASS get( final Object ok ) {
			final AVL_TREE_MAP.Entry KEY_VALUE_GENERIC e;
			final KEY_GENERIC_TYPE kk = KEY_GENERIC_CAST KEY_OBJ2TYPE( ok );
			return in( kk ) && ( e = findKey( kk ) ) != null ? e.getValue() : OBJECT_DEFAULT_RETURN_VALUE;
		}
#endif
		  
		public VALUE_GENERIC_TYPE put(final KEY_GENERIC_TYPE k, final VALUE_GENERIC_TYPE v) {
			modified = false;
			if ( ! in( k ) ) throw new IllegalArgumentException( "Key (" + k + ") out of range [" + ( bottom ? "-" : String.valueOf( from ) ) + ", " + ( top ? "-" : String.valueOf( to ) ) + ")" ); 
			final VALUE_GENERIC_TYPE oldValue = AVL_TREE_MAP.this.put( k, v );
			return modified ? this.defRetValue : oldValue;
		}

		  
#if ! #keyclass(Object) || #values(primitive)
		public VALUE_GENERIC_CLASS put( final KEY_GENERIC_CLASS ok, final VALUE_GENERIC_CLASS ov ) {
			final VALUE_GENERIC_TYPE oldValue = put( KEY_CLASS2TYPE(ok), VALUE_CLASS2TYPE(ov) );
			return modified ? OBJECT_DEFAULT_RETURN_VALUE : VALUE2OBJ( oldValue );
		}
#endif

		SUPPRESS_WARNINGS_KEY_UNCHECKED
		public VALUE_GENERIC_TYPE REMOVE_VALUE( final KEY_TYPE k ) {
			modified = false;
			if ( ! in( KEY_GENERIC_CAST k ) ) return this.defRetValue;
			final VALUE_GENERIC_TYPE oldValue = AVL_TREE_MAP.this.REMOVE_VALUE( k );
			return modified ? oldValue : this.defRetValue;
		}

#if ! #keyclass(Object) || #values(primitive)
		public VALUE_GENERIC_CLASS remove( final Object ok ) {
			final VALUE_GENERIC_TYPE oldValue = REMOVE_VALUE( KEY_OBJ2TYPE( ok ) );
			return modified ? VALUE2OBJ( oldValue ) : OBJECT_DEFAULT_RETURN_VALUE;
		}
#endif

		public int size() {
			final SubmapIterator i = new SubmapIterator();
			int n = 0;
				
			while( i.hasNext() ) {
				n++;
				i.nextEntry();
			}
				
			return n;
		}


		public boolean isEmpty() {
			return ! new SubmapIterator().hasNext();
		}
		  
		public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() {
			return actualComparator;
		}
		  
		public SORTED_MAP KEY_VALUE_GENERIC headMap( final KEY_GENERIC_TYPE to ) {
			if ( top ) return new Submap( from, bottom, to, false );
			return compare( to, this.to ) < 0 ? new Submap( from, bottom, to, false ) : this;
		}
		  
		public SORTED_MAP KEY_VALUE_GENERIC tailMap( final KEY_GENERIC_TYPE from ) {
			if ( bottom ) return new Submap( from, false, to, top );
			return compare( from, this.from ) > 0 ? new Submap( from, false, to, top ) : this;
		}
		  
		public SORTED_MAP KEY_VALUE_GENERIC subMap( KEY_GENERIC_TYPE from,  KEY_GENERIC_TYPE to ) {
			if ( top && bottom ) return new Submap( from, false, to, false );
			if ( ! top ) to = compare( to, this.to ) < 0 ? to : this.to;
			if ( ! bottom ) from = compare( from, this.from ) > 0 ? from : this.from;
	 		if ( ! top && ! bottom && from == this.from && to == this.to ) return this;
			return new Submap( from, false, to, false );
		}

		/** Locates the first entry.
		 *
		 * @return the first entry of this submap, or <code>null</code> if the submap is empty.
		 */
		public AVL_TREE_MAP.Entry KEY_VALUE_GENERIC firstEntry() {
			if ( tree == null ) return null;
			// If this submap goes to -infinity, we return the main map first entry; otherwise, we locate the start of the map.
			AVL_TREE_MAP.Entry KEY_VALUE_GENERIC e;
			if ( bottom ) e = firstEntry;
			else {
				e = locateKey( from );
				// If we find either the start or something greater we're OK.
				if ( compare( e.key, from ) < 0 ) e = e.next();
			}
			// Finally, if this subset doesn't go to infinity, we check that the resulting key isn't greater than the end.
			if ( e == null || ! top && compare( e.key, to ) >= 0 ) return null;
			return e;
		}
	 
		/** Locates the last entry.
		 *
		 * @return the last entry of this submap, or <code>null</code> if the submap is empty.
		 */
		public AVL_TREE_MAP.Entry KEY_VALUE_GENERIC lastEntry() {
			if ( tree == null ) return null;
			// If this submap goes to infinity, we return the main map last entry; otherwise, we locate the end of the map.
			AVL_TREE_MAP.Entry KEY_VALUE_GENERIC e;
			if ( top ) e = lastEntry;
			else {
				e = locateKey( to );
				// If we find something smaller than the end we're OK.
				if ( compare( e.key, to ) >= 0 ) e = e.prev();
			}
			// Finally, if this subset doesn't go to -infinity, we check that the resulting key isn't smaller than the start.
			if ( e == null || ! bottom && compare( e.key, from ) < 0 ) return null;
			return e;
		}

		public KEY_GENERIC_TYPE FIRST_KEY() {
			AVL_TREE_MAP.Entry KEY_VALUE_GENERIC e = firstEntry();
			if ( e == null ) throw new NoSuchElementException();
			return e.key;
		}
		public KEY_GENERIC_TYPE LAST_KEY() {
			AVL_TREE_MAP.Entry KEY_VALUE_GENERIC e = lastEntry();
			if ( e == null ) throw new NoSuchElementException();
			return e.key;
		}
	 
#if !#keyclass(Object)
		public KEY_GENERIC_CLASS firstKey() {
			AVL_TREE_MAP.Entry KEY_VALUE_GENERIC e = firstEntry();
			if ( e == null ) throw new NoSuchElementException();
			return e.getKey();
		}
	 
		public KEY_GENERIC_CLASS lastKey() {
			AVL_TREE_MAP.Entry KEY_VALUE_GENERIC e = lastEntry();
			if ( e == null ) throw new NoSuchElementException();
			return e.getKey();
		}
#endif

		/** An iterator for subranges.
		 * 
		 * <P>This class inherits from {@link TreeIterator}, but overrides the methods that
		 * update the pointer after a {@link java.util.ListIterator#next()} or {@link java.util.ListIterator#previous()}. If we would
		 * move out of the range of the submap we just overwrite the next or previous
		 * entry with <code>null</code>.
		 */
		private class SubmapIterator extends TreeIterator {
			SubmapIterator() {
				next = firstEntry();
			}

			SubmapIterator( final KEY_GENERIC_TYPE k ) {
				this();
					 
				if ( next != null ) {
					if ( ! bottom && compare( k, next.key ) < 0 ) prev = null;
					else if ( ! top && compare( k, ( prev = lastEntry() ).key ) >= 0 ) next = null;
					else {
						next = locateKey( k );
								
						if ( compare( next.key, k ) <= 0 ) {
							prev = next;
							next = next.next();
						}
						else prev = next.prev();
					}
				}			
			}

			void updatePrevious() {
				prev = prev.prev();
				if ( ! bottom && prev != null && AVL_TREE_MAP.this.compare( prev.key, from ) < 0 ) prev = null;
			}
				
			void updateNext() {
				next = next.next();
				if ( ! top && next != null && AVL_TREE_MAP.this.compare( next.key, to ) >= 0 ) next = null;
			}
		}

		private class SubmapEntryIterator extends SubmapIterator implements ObjectListIterator<MAP.Entry KEY_VALUE_GENERIC>  {
			SubmapEntryIterator() {}

			SubmapEntryIterator( final KEY_GENERIC_TYPE k ) {
				super( k );
			}

			public MAP.Entry KEY_VALUE_GENERIC next() { return nextEntry(); }
			public MAP.Entry KEY_VALUE_GENERIC previous() { return previousEntry(); }

			public void set( MAP.Entry KEY_VALUE_GENERIC ok ) { throw new UnsupportedOperationException(); }
			public void add( MAP.Entry KEY_VALUE_GENERIC ok ) { throw new UnsupportedOperationException(); }
		}


		/** An iterator on a subrange of keys.
		 *
		 * <P>This class can iterate in both directions on a subrange of the
		 * keys of a threaded tree. We simply override the {@link
		 * java.util.ListIterator#next()}/{@link java.util.ListIterator#previous()} methods (and possibly their
		 * type-specific counterparts) so that they return keys instead of
		 * entries.
		 */
		private final class SubmapKeyIterator extends SubmapIterator implements KEY_LIST_ITERATOR KEY_GENERIC {
			public SubmapKeyIterator() { super(); }
			public SubmapKeyIterator( KEY_GENERIC_TYPE from ) { super( from ); }
			public KEY_GENERIC_TYPE NEXT_KEY() { return nextEntry().key; }
			public KEY_GENERIC_TYPE PREV_KEY() { return previousEntry().key; }
			public void set( KEY_GENERIC_TYPE k ) { throw new UnsupportedOperationException(); }
			public void add( KEY_GENERIC_TYPE k ) { throw new UnsupportedOperationException(); }
#if !#keyclass(Object)
			public KEY_GENERIC_CLASS next() { return KEY2OBJ( nextEntry().key ); }
			public KEY_GENERIC_CLASS previous() { return KEY2OBJ( previousEntry().key ); }
			public void set( KEY_CLASS ok ) { throw new UnsupportedOperationException(); }
			public void add( KEY_CLASS ok ) { throw new UnsupportedOperationException(); }
#endif									
		};
		  
		/** An iterator on a subrange of values.
		 *
		 * <P>This class can iterate in both directions on the values of a
		 * subrange of the keys of a threaded tree. We simply override the
		 * {@link java.util.ListIterator#next()}/{@link java.util.ListIterator#previous()} methods (and possibly their
		 * type-specific counterparts) so that they return values instead of
		 * entries.  
		 */
		private final class SubmapValueIterator extends SubmapIterator implements VALUE_LIST_ITERATOR VALUE_GENERIC {
			public VALUE_GENERIC_TYPE NEXT_VALUE() { return nextEntry().value; }
			public VALUE_GENERIC_TYPE PREV_VALUE() { return previousEntry().value; }
			public void set( VALUE_GENERIC_TYPE v ) { throw new UnsupportedOperationException(); }
			public void add( VALUE_GENERIC_TYPE v ) { throw new UnsupportedOperationException(); }

#if #values(primitive)
			public VALUE_GENERIC_CLASS next() { return VALUE2OBJ( nextEntry().value ); }
			public VALUE_GENERIC_CLASS previous() { return VALUE2OBJ( previousEntry().value ); }
			public void set( VALUE_CLASS ok ) { throw new UnsupportedOperationException(); }
			public void add( VALUE_CLASS ok ) { throw new UnsupportedOperationException(); }
#endif			
		};


	}
	 

	/** Returns a deep copy of this tree map.
	 *
	 * <P>This method performs a deep copy of this tree map; the data stored in the
	 * set, however, is not cloned. Note that this makes a difference only for object keys.
	 *
	 * @return a deep copy of this tree map.
	 */

	SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED
	public AVL_TREE_MAP KEY_VALUE_GENERIC clone() {
		AVL_TREE_MAP KEY_VALUE_GENERIC c;
		try {
			c = (AVL_TREE_MAP KEY_VALUE_GENERIC)super.clone();
		}
		catch(CloneNotSupportedException cantHappen) {
			throw new InternalError();
		}
		
		c.keys = null;
		c.values = null;
		c.entries = null;
		c.allocatePaths();

		if ( count != 0 ) {
			// Also this apparently unfathomable code is derived from GNU libavl.
			Entry KEY_VALUE_GENERIC e, p, q, rp = new Entry KEY_VALUE_GENERIC(), rq = new Entry KEY_VALUE_GENERIC();

			p = rp;
			rp.left( tree );

			q = rq;
			rq.pred( null );

			while( true ) {
				if ( ! p.pred() ) {
					e = p.left.clone();
					e.pred( q.left );
					e.succ( q );
					q.left( e );

					p = p.left;
					q = q.left;
				}
				else {
					while( p.succ() ) {
						p = p.right;

						if ( p == null ) {
							q.right = null;
							c.tree = rq.left;

							c.firstEntry = c.tree;
							while( c.firstEntry.left != null ) c.firstEntry = c.firstEntry.left;
							c.lastEntry = c.tree;
							while( c.lastEntry.right != null ) c.lastEntry = c.lastEntry.right;

							return c;
						}
						q = q.right;
					}
								
					p = p.right;
					q = q.right;
				}

				if ( ! p.succ() ) {
					e = p.right.clone();
					e.succ( q.right );
					e.pred( q );
					q.right( e );
				}
			}
		}

		return c;
	}
	 

	private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
		int n = count;
		EntryIterator i = new EntryIterator();
		Entry KEY_VALUE_GENERIC e;

		s.defaultWriteObject();

		while(n-- != 0) {
			e = i.nextEntry();
			s.WRITE_KEY( e.key );
			s.WRITE_VALUE( e.value );
		}
	}


	/** Reads the given number of entries from the input stream, returning the corresponding tree. 
	 *
	 * @param s the input stream.
	 * @param n the (positive) number of entries to read.
	 * @param pred the entry containing the key that preceeds the first key in the tree.
	 * @param succ the entry containing the key that follows the last key in the tree.
	 */
	SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED
	private Entry KEY_VALUE_GENERIC readTree( final java.io.ObjectInputStream s, final int n, final Entry KEY_VALUE_GENERIC pred, final Entry KEY_VALUE_GENERIC succ ) throws java.io.IOException, ClassNotFoundException {
		if ( n == 1 ) {
			final Entry KEY_VALUE_GENERIC top = new Entry KEY_VALUE_GENERIC( KEY_GENERIC_CAST s.READ_KEY(), VALUE_GENERIC_CAST s.READ_VALUE() );
			top.pred( pred );
			top.succ( succ );

			return top;
		}

		if ( n == 2 ) {
			/* We handle separately this case so that recursion will
			 *always* be on nonempty subtrees. */
			final Entry KEY_VALUE_GENERIC top = new Entry KEY_VALUE_GENERIC( KEY_GENERIC_CAST s.READ_KEY(), VALUE_GENERIC_CAST s.READ_VALUE() );
			top.right( new Entry KEY_VALUE_GENERIC( KEY_GENERIC_CAST s.READ_KEY(), VALUE_GENERIC_CAST s.READ_VALUE() ) );
			top.right.pred( top );
			top.balance( 1 );
			top.pred( pred );
			top.right.succ( succ );
			
			return top;
		}

		// The right subtree is the largest one.
		final int rightN = n / 2, leftN = n - rightN - 1;

		final Entry KEY_VALUE_GENERIC top = new Entry KEY_VALUE_GENERIC();

		top.left( readTree( s, leftN, pred, top ) );
		
		top.key = KEY_GENERIC_CAST s.READ_KEY();
		top.value = VALUE_GENERIC_CAST s.READ_VALUE();

		top.right( readTree( s, rightN, top, succ ) );

		if ( n == ( n & -n ) ) top.balance( 1 ); // Quick test for determining whether n is a power of 2.

		return top;
	}



	private void readObject( java.io.ObjectInputStream s ) throws java.io.IOException, ClassNotFoundException {
		s.defaultReadObject();
		/* The storedComparator is now correctly set, but we must restore
		   on-the-fly the actualComparator. */
		setActualComparator();
		allocatePaths();

		if ( count != 0 ) {
			tree = readTree( s, count, null, null );
			Entry KEY_VALUE_GENERIC e;

			e = tree;
			while( e.left() != null ) e = e.left();
			firstEntry = e;

			e = tree;
			while( e.right() != null ) e = e.right();
			lastEntry = e;
		}

		if ( ASSERTS ) checkTree( tree );
	}


#ifdef ASSERTS_CODE
	private static KEY_VALUE_GENERIC int checkTree( Entry KEY_VALUE_GENERIC e ) {
		if ( e == null ) return 0;

		final int leftN = checkTree( e.left() ), rightN = checkTree( e.right() );
		if ( leftN + e.balance() != rightN )
			throw new AssertionError( "Mismatch between left tree size (" + leftN + "), right tree size (" + rightN + ") and balance (" + e.balance() + ")" );

		return Math.max( leftN , rightN ) + 1;
	}
#else
	private static KEY_VALUE_GENERIC int checkTree( @SuppressWarnings("unused") Entry KEY_VALUE_GENERIC e ) { return 0; }
#endif

#ifdef TEST

	private static long seed = System.currentTimeMillis(); 
	private static java.util.Random r = new java.util.Random( seed );

	private static KEY_TYPE genKey() {
#if #keyclass(Byte) || #keyclass(Short) || #keyclass(Character)
		return (KEY_TYPE)(r.nextInt());
#elif #keys(primitive)
		return r.NEXT_KEY(); 
#else
		return Integer.toBinaryString( r.nextInt() );
#endif
	}

	private static VALUE_TYPE genValue() {
#if #valueclass(Byte) || #valueclass(Short) || #valueclass(Character)
		return (VALUE_TYPE)(r.nextInt());
#elif #values(primitive)
		return r.NEXT_VALUE();
#elif !#valueclass(Reference) || #keyclass(Reference)
		return Integer.toBinaryString( r.nextInt() );
#else
		return new java.io.Serializable() {};
#endif
	}


	private static java.text.NumberFormat format = new java.text.DecimalFormat( "#,###.00" );
	private static java.text.FieldPosition p = new java.text.FieldPosition( 0 );

	private static String format( double d ) {
		StringBuffer s = new StringBuffer();
		return format.format( d, s, p ).toString();
	}

	private static void speedTest( int n, boolean comp ) {
		int i, j;
		AVL_TREE_MAP m;
		java.util.TreeMap t;
		KEY_TYPE k[] = new KEY_TYPE[n];
		KEY_TYPE nk[] = new KEY_TYPE[n];
		VALUE_TYPE v[] = new VALUE_TYPE[n];
		long ms;

		for( i = 0; i < n; i++ ) {
			k[i] = genKey();
			nk[i] = genKey();
			v[i] = genValue();
		}

		double totPut = 0, totYes = 0, totNo = 0, totIterFor = 0, totIterBack = 0, totRemYes = 0, d, dd;

		if ( comp ) { for( j = 0; j < 20; j++ ) {

			t = new java.util.TreeMap();

			/* We first add all pairs to t. */
			for( i = 0; i < n;  i++ ) t.put( KEY2OBJ( k[i] ), VALUE2OBJ( v[i] ) );

			/* Then we remove the first half and put it back. */
			for( i = 0; i < n/2;  i++ ) t.remove( KEY2OBJ( k[i] ) );

			ms = System.currentTimeMillis();
			for( i = 0; i < n/2;  i++ ) t.put( KEY2OBJ( k[i] ), VALUE2OBJ( v[i] ) );
			d = System.currentTimeMillis() - ms;

			/* Then we remove the other half and put it back again. */
			ms = System.currentTimeMillis();
			for( i = n/2; i < n;  i++ ) t.remove( KEY2OBJ( k[i] ) );
			dd = System.currentTimeMillis() - ms ;

			ms = System.currentTimeMillis();
			for( i = n/2; i < n;  i++ ) t.put( KEY2OBJ( k[i] ), VALUE2OBJ( v[i] )  );
			d += System.currentTimeMillis() - ms;
			if ( j > 2 ) totPut += n/d; 				
			System.out.print("Add: " + format( n/d ) +" K/s " );

			/* Then we remove again the first half. */
			ms = System.currentTimeMillis();
			for( i = 0; i < n/2;  i++ ) t.remove( KEY2OBJ( k[i] ) );
			dd += System.currentTimeMillis() - ms ;
			if ( j > 2 ) totRemYes += n/dd; 				
			System.out.print("RemYes: " + format( n/dd ) +" K/s " );

			/* And then we put it back. */
			for( i = 0; i < n/2;  i++ ) t.put( KEY2OBJ( k[i] ), VALUE2OBJ( v[i] )  );

			/* We check for pairs in t. */
			ms = System.currentTimeMillis();
			for( i = 0; i < n;  i++ ) t.containsKey( KEY2OBJ( k[i] ) );
			d = 1.0 * n / (System.currentTimeMillis() - ms );
			if ( j > 2 ) totYes += d; 				
			System.out.print("Yes: " + format( d ) +" K/s " );

			/* We check for pairs not in t. */
			ms = System.currentTimeMillis();
			for( i = 0; i < n;  i++ ) t.containsKey( KEY2OBJ( nk[i] ) );
			d = 1.0 * n / (System.currentTimeMillis() - ms );
			if ( j > 2 ) totNo += d; 				
			System.out.print("No: " + format( d ) +" K/s " );

			/* We iterate on t. */
			ms = System.currentTimeMillis();
			for( Iterator it = t.entrySet().iterator(); it.hasNext(); it.next() );
			d = 1.0 * n / (System.currentTimeMillis() - ms );
			if ( j > 2 ) totIterFor += d; 				
			System.out.print("IterFor: " + format( d ) +" K/s " );
				
			System.out.println();
		}

		System.out.println();
		System.out.println( "java.util Put: " + format( totPut/(j-3) ) + " K/s RemYes: " + format( totRemYes/(j-3) ) + " K/s Yes: " + format( totYes/(j-3) ) + " K/s No: " + format( totNo/(j-3) )+ " K/s IterFor: " + format( totIterFor/(j-3) )  + " K/s"  );

		System.out.println();

		t = null;
		totPut = totYes = totNo = totIterFor = totIterBack = totRemYes = 0;

		}

		for( j = 0; j < 20; j++ ) {

			m = new AVL_TREE_MAP();

			/* We first add all pairs to m. */
			for( i = 0; i < n;  i++ ) m.put( k[i], v[i] );

			/* Then we remove the first half and put it back. */
			for( i = 0; i < n/2;  i++ ) m.remove( k[i] );

			ms = System.currentTimeMillis();
			for( i = 0; i < n/2;  i++ ) m.put( k[i], v[i] );
			d = System.currentTimeMillis() - ms;

			/* Then we remove the other half and put it back again. */
			ms = System.currentTimeMillis();
			for( i = n/2; i < n;  i++ ) m.remove( k[i] );
			dd = System.currentTimeMillis() - ms ;

			ms = System.currentTimeMillis();
			for( i = n/2; i < n;  i++ ) m.put( k[i], v[i]  );
			d += System.currentTimeMillis() - ms;
			if ( j > 2 ) totPut += n/d; 				
			System.out.print("Add: " + format( n/d ) +" K/s " );

			/* Then we remove again the first half. */
			ms = System.currentTimeMillis();
			for( i = 0; i < n/2;  i++ ) m.remove( k[i] );
			dd += System.currentTimeMillis() - ms ;
			if ( j > 2 ) totRemYes += n/dd; 				
			System.out.print("RemYes: " + format( n/dd ) +" K/s " );

			/* And then we put it back. */
			for( i = 0; i < n/2;  i++ ) m.put( k[i], v[i]  );

			/* We check for pairs in m. */
			ms = System.currentTimeMillis();
			for( i = 0; i < n;  i++ ) m.containsKey( k[i] );
			d = 1.0 * n / (System.currentTimeMillis() - ms );
			if ( j > 2 ) totYes += d; 				
			System.out.print("Yes: " + format( d ) +" K/s " );

			/* We check for pairs not in m. */
			ms = System.currentTimeMillis();
			for( i = 0; i < n;  i++ ) m.containsKey( nk[i] );
			d = 1.0 * n / (System.currentTimeMillis() - ms );
			if ( j > 2 ) totNo += d; 				
			System.out.print("No: " + format( d ) +" K/s " );


			/* We iterate on m. */
			java.util.ListIterator it = (java.util.ListIterator)m.entrySet().iterator();
			ms = System.currentTimeMillis();
			for( it = (java.util.ListIterator)m.entrySet().iterator(); it.hasNext(); it.next() );
			d = 1.0 * n / (System.currentTimeMillis() - ms );
			if ( j > 2 ) totIterFor += d; 				
			System.out.print("IterFor: " + format( d ) +" K/s " );
				
			/* We iterate back on m. */
			ms = System.currentTimeMillis();
			for( ; it.hasPrevious(); it.previous() );
			d = 1.0 * n / (System.currentTimeMillis() - ms );
			if ( j > 2 ) totIterBack += d; 				
			System.out.print("IterBack: " + format( d ) +" K/s " );
				
			System.out.println();
		}


		System.out.println();
		System.out.println( "fastutil  Put: " + format( totPut/(j-3) ) + " K/s RemYes: " + format( totRemYes/(j-3) ) + " K/s Yes: " + format( totYes/(j-3) ) + " K/s No: " + format( totNo/(j-3) ) + " K/s IterFor: " + format( totIterFor/(j-3) ) + " K/s IterBack: " + format( totIterBack/(j-3) ) + "K/s"  );

		System.out.println();

	}


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

	private static void fatal( String msg ) {
		System.out.println( msg );
		System.exit( 1 );
	}

	private static void ensure( boolean cond, String msg ) {
		if ( cond ) return;
		fatal( msg );
	}

	private static Object[] k, v, nk;
	private static KEY_TYPE kt[];
	private static KEY_TYPE nkt[];
	private static VALUE_TYPE vt[];
	private static AVL_TREE_MAP topMap;

	protected static void testMaps( SORTED_MAP m, SortedMap t, int n, int level ) {
		long ms;
		boolean mThrowsIllegal, tThrowsIllegal, mThrowsNoElement, tThrowsNoElement;
		Object rt = null, rm = null;

		if ( level > 4 ) return;
				

		/* Now we check that both maps agree on first/last keys. */

		mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;
		  
		try {
			m.firstKey();
		}
		catch ( NoSuchElementException e ) { mThrowsNoElement = true; }
		try {
			t.firstKey();
		}
		catch ( NoSuchElementException e ) { tThrowsNoElement = true; }
		  
		ensure( mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): firstKey() divergence at start in NoSuchElementException  (" + mThrowsNoElement + ", " + tThrowsNoElement + ")" );
		if ( ! mThrowsNoElement ) ensure( t.firstKey().equals( m.firstKey() ), "Error (" + level + ", " + seed + "): m and t differ at start on their first key (" + m.firstKey() + ", " + t.firstKey() +")" );

		mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;
		  
		try {
			m.lastKey();
		}
		catch ( NoSuchElementException e ) { mThrowsNoElement = true; }
		try {
			t.lastKey();
		}
		catch ( NoSuchElementException e ) { tThrowsNoElement = true; }
		  
		ensure( mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): lastKey() divergence at start in NoSuchElementException  (" + mThrowsNoElement + ", " + tThrowsNoElement + ")" );


		if ( ! mThrowsNoElement ) ensure( t.lastKey().equals( m.lastKey() ), "Error (" + level + ", " + seed + "): m and t differ at start on their last key (" + m.lastKey() + ", " + t.lastKey() +")");


		/* Now we check that m and t are equal. */
		if ( !m.equals( t ) || ! t.equals( m ) ) System.err.println("m: " + m + " t: " + t);

		ensure( m.equals( t ), "Error (" + level + ", " + seed + "): ! m.equals( t ) at start" );
		ensure( t.equals( m ), "Error (" + level + ", " + seed + "): ! t.equals( m ) at start" );



		/* Now we check that m actually holds that data. */
		for(Iterator i=t.entrySet().iterator(); i.hasNext();  ) {
			java.util.Map.Entry e = (java.util.Map.Entry)i.next();
			ensure( valEquals(e.getValue(), m.get(e.getKey())), "Error (" + level + ", " + seed + "): m and t differ on an entry ("+e+") after insertion (iterating on t)" );
		}

		/* Now we check that m actually holds that data, but iterating on m. */
		for(Iterator i=m.entrySet().iterator(); i.hasNext();  ) {
			Entry e = (Entry)i.next();
			ensure( valEquals(e.getValue(), t.get(e.getKey())), "Error (" + level + ", " + seed + "): m and t differ on an entry ("+e+") after insertion (iterating on m)" );
		}

		/* Now we check that m actually holds the same keys. */
		for(Iterator i=t.keySet().iterator(); i.hasNext();  ) {
			Object o = i.next();
			ensure( m.containsKey(o), "Error (" + level + ", " + seed + "): m and t differ on a key ("+o+") after insertion (iterating on t)" );
			ensure( m.keySet().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a key ("+o+", in keySet()) after insertion (iterating on t)" );
		}

		/* Now we check that m actually holds the same keys, but iterating on m. */
		for(Iterator i=m.keySet().iterator(); i.hasNext();  ) {
			Object o = i.next();
			ensure( t.containsKey(o), "Error (" + level + ", " + seed + "): m and t differ on a key after insertion (iterating on m)" );
			ensure( t.keySet().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a key (in keySet()) after insertion (iterating on m)" );
		}


		/* Now we check that m actually hold the same values. */
		for(Iterator i=t.values().iterator(); i.hasNext();  ) {
			Object o = i.next();
			ensure( m.containsValue(o), "Error (" + level + ", " + seed + "): m and t differ on a value after insertion (iterating on t)" );
			ensure( m.values().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a value (in values()) after insertion (iterating on t)" );
		}

		/* Now we check that m actually hold the same values, but iterating on m. */
		for(Iterator i=m.values().iterator(); i.hasNext();  ) {
			Object o = i.next();
			ensure( t.containsValue(o), "Error (" + level + ", " + seed + "): m and t differ on a value after insertion (iterating on m)");
			ensure( t.values().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a value (in values()) after insertion (iterating on m)");
		}

		/* Now we check that inquiries about random data give the same answer in m and t. For
		   m we use the polymorphic method. */

		for(int i=0; i<n;  i++ ) {
			KEY_TYPE T = genKey();
				
			mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;

			try {
				m.containsKey(KEY2OBJ(T));
			}
			catch ( NoSuchElementException e ) { mThrowsNoElement = true; }
			catch ( IllegalArgumentException e ) { mThrowsIllegal = true; }

			try {
				t.containsKey(KEY2OBJ(T));
			}
			catch ( NoSuchElementException e ) { tThrowsNoElement = true; }
			catch ( IllegalArgumentException e ) { tThrowsIllegal = true; }

			ensure( mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): containsKey() divergence in NoSuchElementException (" + mThrowsNoElement + ", " + tThrowsNoElement + ")" );
			ensure( mThrowsIllegal == tThrowsIllegal, "Error (" + level + ", " + seed + "): containsKey() divergence in IllegalArgumentException (" + mThrowsIllegal + ", " + tThrowsIllegal + ")" );
			if ( !mThrowsNoElement && !mThrowsIllegal ) {
				ensure( m.containsKey(KEY2OBJ(T)) == t.containsKey(KEY2OBJ(T)), "Error (" + level + ", " + seed + "): divergence in keys between t and m (polymorphic method)" );
					 
#if #keyclass(Object) && ! ( #values(reference) )
				if ((m.GET_VALUE(T) != VALUE_NULL) != ((t.get(KEY2OBJ(T)) == null ? VALUE_NULL : VALUE_OBJ2TYPE(t.get(KEY2OBJ(T)))) != VALUE_NULL) || 
					t.get(KEY2OBJ(T)) != null && 
					! VALUE2OBJ(m.GET_VALUE(T)).equals(t.get(KEY2OBJ(T)))) 
#else
					if ((m.get(T) != VALUE_NULL) != ((t.get(KEY2OBJ(T)) == null ? VALUE_NULL : VALUE_OBJ2TYPE(t.get(KEY2OBJ(T)))) != VALUE_NULL) || 
						t.get(KEY2OBJ(T)) != null && 
						! m.get(KEY2OBJ(T)).equals(t.get(KEY2OBJ(T)))) 
#endif
						{
							System.out.println("Error (" + level + ", " + seed + "): divergence between t and m (polymorphic method)");
							System.exit( 1 );
						}
			}
		}

		/* Again, we check that inquiries about random data give the same answer in m and t, but
		   for m we use the standard method. */

		for(int i=0; i<n;  i++ ) {
			KEY_TYPE T = genKey();
			mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;

			try {
				m.get(KEY2OBJ(T));
			}
			catch ( NoSuchElementException e ) { mThrowsNoElement = true; }
			catch ( IllegalArgumentException e ) { mThrowsIllegal = true; }

			try {
				t.get(KEY2OBJ(T));
			}
			catch ( NoSuchElementException e ) { tThrowsNoElement = true; }
			catch ( IllegalArgumentException e ) { tThrowsIllegal = true; }

			ensure( mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): get() divergence in NoSuchElementException for " + T + " (" + mThrowsNoElement + ", " + tThrowsNoElement + ")" );
			ensure( mThrowsIllegal == tThrowsIllegal, "Error (" + level + ", " + seed + "): get() divergence in IllegalArgumentException for " + T + " (" + mThrowsIllegal + ", " + tThrowsIllegal + ")" );
			if ( !mThrowsNoElement && !mThrowsIllegal ) ensure( valEquals(m.get(KEY2OBJ(T)), t.get(KEY2OBJ(T))), "Error (" + level + ", " + seed + "): divergence between t and m (standard method)" );
		}

		/* Now we put and remove random data in m and t, checking that the result is the same. */

		for(int i=0; i<20*n;  i++ ) {
			KEY_TYPE T = genKey();
			VALUE_TYPE U = genValue();

			mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;

			try {
				rm = m.put(KEY2OBJ(T), VALUE2OBJ(U));
			}
			catch ( NoSuchElementException e ) { mThrowsNoElement = true; }
			catch ( IllegalArgumentException e ) { mThrowsIllegal = true; }

			try {
				rt = t.put(KEY2OBJ(T), VALUE2OBJ(U));
			}
			catch ( NoSuchElementException e ) { tThrowsNoElement = true; }
			catch ( IllegalArgumentException e ) { tThrowsIllegal = true; }


			ensure( mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): put() divergence in NoSuchElementException for " + T + " (" + mThrowsNoElement + ", " + tThrowsNoElement + ")" );
			ensure( mThrowsIllegal == tThrowsIllegal, "Error (" + level + ", " + seed + "): put() divergence in IllegalArgumentException for " + T + " (" + mThrowsIllegal + ", " + tThrowsIllegal + ")" );
			if ( !mThrowsNoElement && !mThrowsIllegal ) ensure( valEquals( rm, rt ), "Error (" + level + ", " + seed + "): divergence in put() between t and m (" + rt + ", " + rm + ")" );

			T = genKey();
				

			mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;

			try {
				rm = m.remove(KEY2OBJ(T));
			}
			catch ( NoSuchElementException e ) { mThrowsNoElement = true; }
			catch ( IllegalArgumentException e ) { mThrowsIllegal = true; }

			try {
				rt = t.remove(KEY2OBJ(T));
			}
			catch ( NoSuchElementException e ) { tThrowsNoElement = true; }
			catch ( IllegalArgumentException e ) { tThrowsIllegal = true; }


			ensure( mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): remove() divergence in NoSuchElementException for " + T + " (" + mThrowsNoElement + ", " + tThrowsNoElement + ")" );
			ensure( mThrowsIllegal == tThrowsIllegal, "Error (" + level + ", " + seed + "): remove() divergence in IllegalArgumentException for " + T + " (" + mThrowsIllegal + ", " + tThrowsIllegal + ")" );
			if ( !mThrowsNoElement && !mThrowsIllegal ) ensure( valEquals( rm, rt ), "Error (" + level + ", " + seed + "): divergence in remove() between t and m (" + rt + ", " + rm + ")" );
		}

		ensure( m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals( t ) after removal" );
		ensure( t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals( m ) after removal" );

		/* Now we check that m actually holds the same data. */
		  
		for(Iterator i=t.entrySet().iterator(); i.hasNext();  ) {
			java.util.Map.Entry e = (java.util.Map.Entry)i.next();
			ensure( valEquals(e.getValue(), m.get(e.getKey())), "Error (" + level + ", " + seed + "): m and t differ on an entry ("+e+") after removal (iterating on t)");
		}

		/* Now we check that m actually holds that data, but iterating on m. */
		  
		for(Iterator i=m.entrySet().iterator(); i.hasNext();  ) {
			Entry e = (Entry)i.next();
			ensure( valEquals(e.getValue(), t.get(e.getKey())), "Error (" + level + ", " + seed + "): m and t differ on an entry ("+e+") after removal (iterating on m)" );
		}

		/* Now we check that m actually holds the same keys. */
		  
		for(Iterator i=t.keySet().iterator(); i.hasNext();  ) {
			Object o = i.next();
			ensure( m.containsKey(o), "Error (" + level + ", " + seed + "): m and t differ on a key ("+o+") after removal (iterating on t)");
			ensure( m.keySet().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a key ("+o+", in keySet()) after removal (iterating on t)");
		}

		/* Now we check that m actually holds the same keys, but iterating on m. */
		  
		for(Iterator i=m.keySet().iterator(); i.hasNext();  ) {
			Object o = i.next();
			ensure( t.containsKey(o), "Error (" + level + ", " + seed + "): m and t differ on a key after removal (iterating on m)");
			ensure( t.keySet().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a key (in keySet()) after removal (iterating on m)");
		}


		/* Now we check that m actually hold the same values. */
		  
		for(Iterator i=t.values().iterator(); i.hasNext();  ) {
			Object o = i.next();
			ensure( m.containsValue(o), "Error (" + level + ", " + seed + "): m and t differ on a value after removal (iterating on t)" );
			ensure( m.values().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a value (in values()) after removal (iterating on t)");
		}

		/* Now we check that m actually hold the same values, but iterating on m. */
		  
		for(Iterator i=m.values().iterator(); i.hasNext();  ) {
			Object o = i.next();
			ensure( t.containsValue(o), "Error (" + level + ", " + seed + "): m and t differ on a value after removal (iterating on m)");
			ensure( t.values().contains(o), "Error (" + level + ", " + seed + "): m and t differ on a value (in values()) after removal (iterating on m)");
		}

		/* Now we check that both maps agree on first/last keys. */

		mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;
		  
		try {
			m.firstKey();
		}
		catch ( NoSuchElementException e ) { mThrowsNoElement = true; }
		try {
			t.firstKey();
		}
		catch ( NoSuchElementException e ) { tThrowsNoElement = true; }
		  
		ensure( mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): firstKey() divergence in NoSuchElementException  (" + mThrowsNoElement + ", " + tThrowsNoElement + ")" );
		if ( ! mThrowsNoElement ) ensure( t.firstKey().equals( m.firstKey() ), "Error (" + level + ", " + seed + "): m and t differ on their first key (" + m.firstKey() + ", " + t.firstKey() +")" );

		mThrowsNoElement = mThrowsIllegal = tThrowsNoElement = tThrowsIllegal = false;
		  
		try {
			m.lastKey();
		}
		catch ( NoSuchElementException e ) { mThrowsNoElement = true; }
		try {
			t.lastKey();
		}
		catch ( NoSuchElementException e ) { tThrowsNoElement = true; }
		  
		ensure( mThrowsNoElement == tThrowsNoElement, "Error (" + level + ", " + seed + "): lastKey() divergence in NoSuchElementException  (" + mThrowsNoElement + ", " + tThrowsNoElement + ")" );

		if ( ! mThrowsNoElement ) ensure( t.lastKey().equals( m.lastKey() ), "Error (" + level + ", " + seed + "): m and t differ on their last key (" + m.lastKey() + ", " + t.lastKey() +")");

		/* Now we check cloning. */

		if ( level == 0 ) {
			ensure( m.equals( ((AVL_TREE_MAP)m).clone() ), "Error (" + level + ", " + seed + "): m does not equal m.clone()" );
			ensure( ((AVL_TREE_MAP)m).clone().equals( m ), "Error (" + level + ", " + seed + "): m.clone() does not equal m" );
		}

		int h = m.hashCode();


		/* Now we save and read m. */

		SORTED_MAP m2 = null;
		  
		try {
			java.io.File ff = new java.io.File("it.unimi.dsi.fastutil.test");
			java.io.OutputStream os = new java.io.FileOutputStream(ff);
			java.io.ObjectOutputStream oos = new java.io.ObjectOutputStream(os);
				
			oos.writeObject(m);
			oos.close();
				
			java.io.InputStream is = new java.io.FileInputStream(ff);
			java.io.ObjectInputStream ois = new java.io.ObjectInputStream(is);
				
			m2 = (SORTED_MAP)ois.readObject();
			ois.close();
			ff.delete();
		}
		catch(Exception e) {
			e.printStackTrace();
			System.exit( 1 );
		}

#if !#valueclass(Reference)
		ensure( m2.hashCode() == h, "Error (" + level + ", " + seed + "): hashCode() changed after save/read" );
		  
		/* Now we check that m2 actually holds that data. */
		  
		ensure( m2.equals(t), "Error (" + level + ", " + seed + "): ! m2.equals( t ) after save/read" );
		ensure( t.equals(m2), "Error (" + level + ", " + seed + "): ! t.equals( m2 ) after save/read" );
#else
		m2.clear();
		m2.putAll( m );
#endif

		/* Now we take out of m everything, and check that it is empty. */
		  
		for(Iterator i=t.keySet().iterator(); i.hasNext(); ) m2.remove(i.next());
		  
		ensure( m2.isEmpty(), "Error (" + level + ", " + seed + "): m2 is not empty (as it should be)" );

		/* Now we play with iterators. */

		{
			java.util.ListIterator i, j;
			Map.Entry E, F;
			Object J;
			i = (java.util.ListIterator)m.entrySet().iterator(); 
			j = new java.util.LinkedList( t.entrySet() ).listIterator(); 

			for( int k = 0; k < 2*n; k++ ) {
				ensure( i.hasNext() == j.hasNext(), "Error (" + level + ", " + seed + "): divergence in hasNext()" );
				ensure( i.hasPrevious() == j.hasPrevious(), "Error (" + level + ", " + seed + "): divergence in hasPrevious()" );

				if ( r.nextFloat() < .8 && i.hasNext() ) {
					ensure( (E=(Entry)i.next()).getKey().equals( J = (F=(Map.Entry)j.next()).getKey() ), "Error (" + level + ", " + seed + "): divergence in next()" );

					if ( r.nextFloat() < 0.3 ) {
						i.remove();
						j.remove();
						t.remove( J );
					}
					else if ( r.nextFloat() < 0.3 ) {
						Object U = VALUE2OBJ(genValue());
						E.setValue( U );
                        t.put( F.getKey(), U );
					}
				}
				else if ( r.nextFloat() < .2 && i.hasPrevious() ) {
					ensure( (E=(Entry)i.previous()).getKey().equals( J = (F=(Map.Entry)j.previous()).getKey() ), "Error (" + level + ", " + seed + "): divergence in previous()" );

					if ( r.nextFloat() < 0.3 ) {
						i.remove();
						j.remove();
						t.remove( J );
					}
					else if ( r.nextFloat() < 0.3 ) {
						Object U = VALUE2OBJ(genValue());
						E.setValue( U );
                        t.put( F.getKey(), U );
					}
				}

				ensure( i.nextIndex() == j.nextIndex(), "Error (" + level + ", " + seed + "): divergence in nextIndex()" );
				ensure( i.previousIndex() == j.previousIndex(), "Error (" + level + ", " + seed + "): divergence in previousIndex()" );

			}

		}
		  

		{
			boolean badPrevious = false;
			Object previous = null;
			it.unimi.dsi.fastutil.BidirectionalIterator i;
			java.util.ListIterator j;
			Object I, J;
			KEY_TYPE from = genKey();
			j = new java.util.LinkedList( t.keySet() ).listIterator(); 
			while( j.hasNext() ) {
				Object k = j.next();
				if ( ((Comparable)k).compareTo( KEY2OBJ( from ) ) > 0 ) {
					badPrevious = true;
					j.previous();
					break;
				}
				previous = k;
			}

			i = (it.unimi.dsi.fastutil.BidirectionalIterator)((SORTED_SET)m.keySet()).iterator( from ); 

			for( int k = 0; k < 2*n; k++ ) {
				ensure( i.hasNext() == j.hasNext(), "Error (" + level + ", " + seed + "): divergence in hasNext() (iterator with starting point " + from + ")" );
				ensure( i.hasPrevious() == j.hasPrevious() || badPrevious && ( i.hasPrevious() == ( previous != null ) ), "Error (" + level + ", " + seed + "): divergence in hasPrevious() (iterator with starting point " + from + ")" + badPrevious );

				if ( r.nextFloat() < .8 && i.hasNext() ) {
					ensure( ( I = i.next() ).equals( J = j.next() ), "Error (" + level + ", " + seed + "): divergence in next() (" + I + ", " + J + ", iterator with starting point " + from + ")" );
					//System.err.println("Done next " + I + " " + J + "  " + badPrevious);

					badPrevious = false;

					if ( r.nextFloat() < 0.5 ) {
						//System.err.println("Removing in next");
						i.remove();
						j.remove();
						t.remove( J );
					}
				}
				else if ( !badPrevious && r.nextFloat() < .2 && i.hasPrevious() ) {
					ensure( ( I = i.previous() ).equals( J = j.previous() ), "Error (" + level + ", " + seed + "): divergence in previous() (" + I + ", " + J + ", iterator with starting point " + from + ")" );

					if ( r.nextFloat() < 0.5 ) {
						//System.err.println("Removing in prev");
						i.remove();
						j.remove();
						t.remove( J );
					}
				}
			}

		}

		/* Now we check that m actually holds that data. */
		  
		ensure( m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals( t ) after iteration" );
		ensure( t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals( m ) after iteration" );

		/* Now we select a pair of keys and create a submap. */

		if ( ! m.isEmpty() ) {
			java.util.ListIterator i;
			Object start = m.firstKey(), end = m.firstKey();
			for( i = (java.util.ListIterator)m.keySet().iterator(); i.hasNext() && r.nextFloat() < .3; start = end = i.next() );
			for( ; i.hasNext() && r.nextFloat() < .95; end = i.next() );
				
			//System.err.println("Checking subMap from " + start + " to " + end + " (level=" + (level+1) + ")..." );
			testMaps( (SORTED_MAP)m.subMap( (KEY_CLASS)start, (KEY_CLASS)end ), t.subMap( start, end ), n, level + 1 );

			ensure( m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals( t ) after subMap" );
			ensure( t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals( m ) after subMap" );

			//System.err.println("Checking headMap to " + end + " (level=" + (level+1) + ")..." );
			testMaps( (SORTED_MAP)m.headMap( (KEY_CLASS)end ), t.headMap( end ), n, level + 1 );

			ensure( m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals( t ) after headMap" );
			ensure( t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals( m ) after headMap" );

			//System.err.println("Checking tailMap from " + start + " (level=" + (level+1) + ")..." );
			testMaps( (SORTED_MAP)m.tailMap( (KEY_CLASS)start ), t.tailMap( start ), n, level + 1 );

			ensure( m.equals(t), "Error (" + level + ", " + seed + "): ! m.equals( t ) after tailMap" );
			ensure( t.equals(m), "Error (" + level + ", " + seed + "): ! t.equals( m ) after tailMap" );
		}
		  

	}


	private static void test( int n ) {
		AVL_TREE_MAP m = new AVL_TREE_MAP();
		SortedMap t = new java.util.TreeMap();
		topMap = m;
		k = new Object[n];
		v = new Object[n];
		nk = new Object[n];
		kt = new KEY_TYPE[n];
		nkt = new KEY_TYPE[n];
		vt = new VALUE_TYPE[n];

		for( int i = 0; i < n; i++ ) {
#if #keyclass(Object)
			k[i] = kt[i] = genKey();
			nk[i] = nkt[i] = genKey();
#else
			k[i] = new KEY_CLASS( kt[i] = genKey() );
			nk[i] = new KEY_CLASS( nkt[i] = genKey() );
#endif
#if #values(reference)
			v[i] = vt[i] = genValue();
#else
			v[i] = new VALUE_CLASS( vt[i] = genValue() );
#endif
		}
		  
		/* We add pairs to t. */
		for( int i = 0; i < n;  i++ ) t.put( k[i], v[i] );
		  
		/* We add to m the same data */
		m.putAll(t);

		testMaps( m, t, n, 0 );

		System.out.println("Test OK");
		return;
	}


	public static void main( String args[] ) {
		int n  = Integer.parseInt(args[1]);
		if ( args.length > 2 ) r = new java.util.Random( seed = Long.parseLong( args[ 2 ] ) );

		try {
			if ("speedTest".equals(args[0]) || "speedComp".equals(args[0])) speedTest( n, "speedComp".equals(args[0]) );
			else if ( "test".equals( args[0] ) ) test(n);
		} catch( Throwable e ) {
			e.printStackTrace( System.err );
			System.err.println( "seed: " + seed );
		}
	}

#endif


}
