/*		 
 * Copyright (C) 2002-2013 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.Hash;
import it.unimi.dsi.fastutil.HashCommon;
import it.unimi.dsi.fastutil.bytes.ByteArrays;

import java.util.Arrays;
import java.util.Collection;
#if #keys(primitive)
import java.util.Iterator;
#endif
import java.util.NoSuchElementException;

#ifdef Linked
#if #keys(reference)
import java.util.Comparator;
#endif

/**  A type-specific linked hash set with with a fast, small-footprint implementation.
 *
 * <P>Instances of this class use a hash table to represent a set. The table is
 * enlarged as needed when new entries are created, but it is <em>never</em> made
 * smaller (even on a {@link #clear()}). A family of {@linkplain #trim() trimming
 * methods} lets you control the size of the table; this is particularly useful
 * if you reuse instances of this class.
 *
 * <P>The enlargement speed is controlled by the <em>growth factor</em>, a
 * positive number. If the growth factor is <var>p</var>, then the table is
 * enlarged each time roughly by a factor 2<sup>p/16</sup>. By default, <var>p</var> is
 * {@link Hash#DEFAULT_GROWTH_FACTOR}, which means that the table is doubled at
 * each enlargement, but one can easily set more or less aggressive policies by
 * calling {@link #growthFactor(int)} (note that the growth factor is <em>not</em> serialized:
 * deserialized tables gets the {@linkplain Hash#DEFAULT_GROWTH_FACTOR default growth factor}).
 *
 * <P>Iterators created by this map will enumerate elements in the same order in which they
 * have been added to the set (note that addition of elements already present 
 * in the set does not change the iteration order). Note that this order has nothing in common with the natural
 * order of the keys.
 *
 * <P>This class implements the interface of a sorted set, so to allow easy
 * access of the iteration order: for instance, you can get the first element
 * in iteration order with {@link #first()} without having to create an
 * iterator; however, this class partially violates the {@link java.util.SortedSet}
 * contract because all subset methods throw an exception and {@link
 * #comparator()} returns always <code>null</code>.
 *
 * <P>The iterators provided by this class are type-specific {@linkplain
 * java.util.ListIterator list iterators}.  However, creation of an iterator
 * using a starting point is going to be very expensive, as the chosen starting
 * point must be linearly searched for, unless it is {@link #last()}, in which
 * case the iterator is created in constant time.
 *
 * <P>Note that deletions in a linked table require scanning the list until the
 * element to be removed is found. The only exceptions are the first element, the last element,
 * and deletions performed using an iterator.
 *
 * @see Hash
 * @see HashCommon
 */

public class OPEN_DOUBLE_HASH_SET KEY_GENERIC extends ABSTRACT_SORTED_SET KEY_GENERIC implements java.io.Serializable, Cloneable, Hash {

#else

#ifdef Custom

/**  A hash set with with a fast, small-footprint implementation whose {@linkplain it.unimi.dsi.fastutil.Hash.Strategy hashing strategy}
 * is specified at creation time.
 *
 * <P>Instances of this class use a hash table to represent a set. The table is
 * enlarged as needed when new entries are created, but it is <em>never</em> made
 * smaller (even on a {@link #clear()}). A family of {@linkplain #trim() trimming
 * methods} lets you control the size of the table; this is particularly useful
 * if you reuse instances of this class.
 *
 * <P>The enlargement speed is controlled by the <em>growth factor</em>, a
 * positive number. If the growth factor is <var>p</var>, then the table is
 * enlarged each time roughly by a factor 2<sup>p/16</sup>. By default, <var>p</var> is
 * {@link Hash#DEFAULT_GROWTH_FACTOR}, which means that the table is doubled at
 * each enlargement, but one can easily set more or less aggressive policies by
 * calling {@link #growthFactor(int)} (note that the growth factor is <em>not</em> serialized:
 * deserialized tables gets the {@linkplain Hash#DEFAULT_GROWTH_FACTOR default growth factor}).
 *
 *
 * @see Hash
 * @see HashCommon
 */

public class OPEN_DOUBLE_HASH_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implements java.io.Serializable, Cloneable, Hash {

#else

/**  A type-specific hash set with with a fast, small-footprint implementation.
 *
 * <P>Instances of this class use a hash table to represent a set. The table is
 * enlarged as needed when new entries are created, but it is <em>never</em> made
 * smaller (even on a {@link #clear()}). A family of {@linkplain #trim() trimming
 * methods} lets you control the size of the table; this is particularly useful
 * if you reuse instances of this class.
 *
 * <P>The enlargement speed is controlled by the <em>growth factor</em>, a
 * positive number. If the growth factor is <var>p</var>, then the table is
 * enlarged each time roughly by a factor 2<sup>p/16</sup>. By default, <var>p</var> is
 * {@link Hash#DEFAULT_GROWTH_FACTOR}, which means that the table is doubled at
 * each enlargement, but one can easily set more or less aggressive policies by
 * calling {@link #growthFactor(int)} (note that the growth factor is <em>not</em> serialized:
 * deserialized tables gets the {@linkplain Hash#DEFAULT_GROWTH_FACTOR default growth factor}).
 *
 *
 * @see Hash
 * @see HashCommon
 */

public class OPEN_DOUBLE_HASH_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implements java.io.Serializable, Cloneable, Hash {

#endif

#endif

	/** The array of keys. */
	protected transient KEY_GENERIC_TYPE key[];
	 
	/** The array of occupancy states. */
	protected transient byte state[];

	/** The acceptable load factor. */
	protected final float f;
	 
	/** Index into the prime list, giving the current table size. */
	protected transient int p;

	/** Threshold after which we rehash. It must be the table size times {@link #f}. */
	protected transient int maxFill;

	/** Number of free entries in the table (may be less than the table size - {@link #count} because of deleted entries). */
	protected transient int free;

	/** Number of entries in the set. */
	protected int count;

	/** The growth factor of the table. The next table size will be <code>{@link Hash#PRIMES}[{@link #p}+growthFactor</code>. */
	protected transient int growthFactor = Hash.DEFAULT_GROWTH_FACTOR;

#ifdef Linked
	/** The index of the first entry in iteration order. It is valid iff {@link #count} is nonzero; otherwise, it contains -1. */
	protected transient int first = -1;
	/** The index of the last entry in iteration order. It is valid iff {@link #count} is nonzero; otherwise, it contains -1. */
	protected transient int last = -1;
	/** For each entry, the next and the previous entry in iteration order
	exclusive-or'd together. It is valid only on {@link Hash#OCCUPIED}
	entries. The first and the last entry contain the actual successor and
	predecessor, respectively, exclusived-or'd with -1. */
	protected transient int link[];
#endif

#ifdef Custom
	/** The hash strategy of this custom set. */
	protected Strategy<K> strategy;
#endif

    private static final long serialVersionUID = -7046029254386353129L;

	private static final boolean ASSERTS = ASSERTS_VALUE;

#ifdef Custom
	/** Creates a new hash set.
	 *
	 * The actual table size is the least available prime greater than <code>n</code>/<code>f</code>.
	 *
	 * @param n the expected number of elements in the hash set. 
	 * @param f the load factor.
	 * @param strategy the strategy.
	 * @see Hash#PRIMES
	 */
	@SuppressWarnings("unchecked")
	public OPEN_DOUBLE_HASH_SET( final int n, final float f, final Strategy<K> strategy ) {
		this.strategy = strategy;
#else
	/** Creates a new hash set.
	 *
	 * The actual table size is the least available prime greater than <code>n</code>/<code>f</code>.
	 *
	 * @param n the expected number of elements in the hash set. 
	 * @param f the load factor.
	 * @see Hash#PRIMES
	 */
	@SuppressWarnings("unchecked")
	public OPEN_DOUBLE_HASH_SET( final int n, final float f ) {
#endif
		if ( f <= 0 || f > 1 ) throw new IllegalArgumentException( "Load factor must be greater than 0 and smaller than or equal to 1" );
		if ( n < 0 ) throw new IllegalArgumentException( "Hash table size must be nonnegative" );

		int l = Arrays.binarySearch( PRIMES, (int)( n / f ) + 1 );
		if ( l < 0 ) l = -l - 1;

		free = PRIMES[ p = l ];
		this.f = f;
		this.maxFill = (int)( free * f );
		key = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[ free ];
		state = new byte[ free ];
#ifdef Linked
		link = new int[ free ];
#endif
	}
	 
	 
#ifdef Custom
	/** Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor.
	 *
	 * @param n the expected number of elements in the hash set. 
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final int n, final Strategy<K> strategy ) {
		this( n, DEFAULT_LOAD_FACTOR, strategy );
	}

#else
	/** Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor.
	 *
	 * @param n the expected number of elements in the hash set. 
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final int n ) {
		this( n, DEFAULT_LOAD_FACTOR );
	}

#endif


#ifdef Custom
	/** Creates a new hash set with {@link Hash#DEFAULT_INITIAL_SIZE} elements
	 * and {@link Hash#DEFAULT_LOAD_FACTOR} as load factor.
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final Strategy<K> strategy ) {
	this( DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR, strategy );
	}
#else
	/** Creates a new hash set with {@link Hash#DEFAULT_INITIAL_SIZE} elements
	 * and {@link Hash#DEFAULT_LOAD_FACTOR} as load factor.
	 */
	 
	public OPEN_DOUBLE_HASH_SET() {
		this( DEFAULT_INITIAL_SIZE, DEFAULT_LOAD_FACTOR );
	} 
#endif


#ifdef Custom
	/** Creates a new hash set copying a given collection.
	 *
	 * @param c a {@link Collection} to be copied into the new hash set. 
	 * @param f the load factor.
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final Collection<? extends KEY_GENERIC_CLASS> c, final float f, final Strategy<K> strategy ) {
	this( c.size(), f, strategy );
		addAll( c );
	}
#else
	/** Creates a new hash set copying a given collection.
	 *
	 * @param c a {@link Collection} to be copied into the new hash set. 
	 * @param f the load factor.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final Collection<? extends KEY_GENERIC_CLASS> c, final float f ) {
		this( c.size(), f );
		addAll( c );
	}
#endif



#ifdef Custom
	/** Creates a new hash set  with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor 
	 * copying a given collection.
	 *
	 * @param c a {@link Collection} to be copied into the new hash set. 
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final Collection<? extends KEY_GENERIC_CLASS> c, final Strategy<K> strategy ) {
		this( c, DEFAULT_LOAD_FACTOR, strategy );
	}

#else
	/** Creates a new hash set  with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor 
	 * copying a given collection.
	 *
	 * @param c a {@link Collection} to be copied into the new hash set. 
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final Collection<? extends KEY_GENERIC_CLASS> c ) {
		this( c, DEFAULT_LOAD_FACTOR );
	}
#endif


#ifdef Custom
	/** Creates a new hash set copying a given type-specific collection.
	 *
	 * @param c a type-specific collection to be copied into the new hash set. 
	 * @param f the load factor.
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final COLLECTION KEY_EXTENDS_GENERIC c, final float f, Strategy<K> strategy ) {
	this( c.size(), f, strategy );
		addAll( c );
	}
#else
	/** Creates a new hash set copying a given type-specific collection.
	 *
	 * @param c a type-specific collection to be copied into the new hash set. 
	 * @param f the load factor.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final COLLECTION KEY_EXTENDS_GENERIC c, final float f ) {
		this( c.size(), f );
		addAll( c );
	}
#endif


#ifdef Custom
	/** Creates a new hash set  with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor 
	 * copying a given type-specific collection.
	 *
	 * @param c a type-specific collection to be copied into the new hash set. 
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final COLLECTION KEY_EXTENDS_GENERIC c, final Strategy<K> strategy ) {
		this( c, DEFAULT_LOAD_FACTOR, strategy );
	}
#else
	/** Creates a new hash set  with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor 
	 * copying a given type-specific collection.
	 *
	 * @param c a type-specific collection to be copied into the new hash set. 
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final COLLECTION KEY_EXTENDS_GENERIC c ) {
		this( c, DEFAULT_LOAD_FACTOR );
	}
#endif


#ifdef Custom
	/** Creates a new hash set using elements provided by a type-specific iterator.
	 *
	 * @param i a type-specific iterator whose elements will fill the set.
	 * @param f the load factor.
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_ITERATOR KEY_GENERIC i, final float f, final Strategy<K> strategy ) {
		this( DEFAULT_INITIAL_SIZE, f, strategy );
		while( i.hasNext() ) add( i.NEXT_KEY() );
	}
#else
	/** Creates a new hash set using elements provided by a type-specific iterator.
	 *
	 * @param i a type-specific iterator whose elements will fill the set.
	 * @param f the load factor.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_ITERATOR KEY_GENERIC i, final float f ) {
		this( DEFAULT_INITIAL_SIZE, f );
		while( i.hasNext() ) add( i.NEXT_KEY() );
	}
#endif

#ifdef Custom
	/** Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor using elements provided by a type-specific iterator.
	 *
	 * @param i a type-specific iterator whose elements will fill the set.
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_ITERATOR KEY_GENERIC i, final Strategy<K> strategy ) {
		this( i, DEFAULT_LOAD_FACTOR, strategy );
	}
#else
	/** Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor using elements provided by a type-specific iterator.
	 *
	 * @param i a type-specific iterator whose elements will fill the set.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_ITERATOR KEY_GENERIC i ) {
		this( i, DEFAULT_LOAD_FACTOR );
	}
#endif



#if #keys(primitive)

#ifdef Custom
	/** Creates a new hash set using elements provided by an iterator.
	 *
	 * @param i an iterator whose elements will fill the set.
	 * @param f the load factor.
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final Iterator<?> i, final float f, final Strategy<K> strategy ) {
		this( ITERATORS.AS_KEY_ITERATOR( i ), f, strategy );
	}
#else
	/** Creates a new hash set using elements provided by an iterator.
	 *
	 * @param i an iterator whose elements will fill the set.
	 * @param f the load factor.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final Iterator<?> i, final float f ) {
		this( ITERATORS.AS_KEY_ITERATOR( i ), f );
	}
#endif

#ifdef Custom
	/** Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor using elements provided by an iterator.
	 *
	 * @param i an iterator whose elements will fill the set.
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final Iterator i, final Strategy<K> strategy ) {
		this( ITERATORS.AS_KEY_ITERATOR( i, strategy ) );
	}
#else
	/** Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor using elements provided by an iterator.
	 *
	 * @param i an iterator whose elements will fill the set.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final Iterator<?> i ) {
		this( ITERATORS.AS_KEY_ITERATOR( i ) );
	}
#endif

#endif



#ifdef Custom
	/** Creates a new hash set and fills it with the elements of a given array.
	 *
	 * @param a an array whose elements will be used to fill the set.
	 * @param offset the first element to use.
	 * @param length the number of elements to use.
	 * @param f the load factor.
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_GENERIC_TYPE[] a, final int offset, final int length, final float f, final Strategy<K> strategy ) {
	this( length < 0 ? 0 : length, f, strategy );
		ARRAYS.ensureOffsetLength( a, offset, length );
		for( int i = 0; i < length; i++ ) add( a[ offset + i ] );
	}
#else
	/** Creates a new hash set and fills it with the elements of a given array.
	 *
	 * @param a an array whose elements will be used to fill the set.
	 * @param offset the first element to use.
	 * @param length the number of elements to use.
	 * @param f the load factor.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_GENERIC_TYPE[] a, final int offset, final int length, final float f ) {
		this( length < 0 ? 0 : length, f );
		ARRAYS.ensureOffsetLength( a, offset, length );
		for( int i = 0; i < length; i++ ) add( a[ offset + i ] );
	}
#endif

#ifdef Custom
	/** Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor and fills it with the elements of a given array.
	 *
	 * @param a an array whose elements will be used to fill the set.
	 * @param offset the first element to use.
	 * @param length the number of elements to use.
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_GENERIC_TYPE[] a, final int offset, final int length, final Strategy<K> strategy ) {
		this( a, offset, length, DEFAULT_LOAD_FACTOR, strategy );
	}
#else
	/** Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor and fills it with the elements of a given array.
	 *
	 * @param a an array whose elements will be used to fill the set.
	 * @param offset the first element to use.
	 * @param length the number of elements to use.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_GENERIC_TYPE[] a, final int offset, final int length ) {
		this( a, offset, length, DEFAULT_LOAD_FACTOR );
	}
#endif

#ifdef Custom
	/** Creates a new hash set copying the elements of an array.
	 *
	 * @param a an array to be copied into the new hash set. 
	 * @param f the load factor.
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_GENERIC_TYPE[] a, final float f, final Strategy<K> strategy ) {
		this( a, 0, a.length, f, strategy );
	}
#else
	/** Creates a new hash set copying the elements of an array.
	 *
	 * @param a an array to be copied into the new hash set. 
	 * @param f the load factor.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_GENERIC_TYPE[] a, final float f ) {
		this( a, 0, a.length, f );
	}
#endif

#ifdef Custom
	/** Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor 
	 * copying the elements of an array.
	 *
	 * @param a an array to be copied into the new hash set. 
	 * @param strategy the strategy.
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_GENERIC_TYPE[] a, final Strategy<K> strategy ) {
		this( a, DEFAULT_LOAD_FACTOR, strategy );
	}
#else
	/** Creates a new hash set with {@link Hash#DEFAULT_LOAD_FACTOR} as load factor 
	 * copying the elements of an array.
	 *
	 * @param a an array to be copied into the new hash set. 
	 */
	 
	public OPEN_DOUBLE_HASH_SET( final KEY_GENERIC_TYPE[] a ) {
		this( a, DEFAULT_LOAD_FACTOR );
	}
#endif


#ifdef Custom
	/** Returns the hashing strategy.
	 *
	 * @return the hashing strategy of this custom hash set.
	 */

	public Strategy<K> strategy() {
		return strategy;
	}
#endif

	/** Sets the growth factor. Subsequent enlargements will increase the table
	 * size roughly by a multiplicative factor of 2<sup>p/16</sup>.
	 * 
	 * @param growthFactor the new growth factor; it must be positive.
	 */

	public void growthFactor( int growthFactor ) {
		if ( growthFactor <= 0 ) throw new IllegalArgumentException( "Illegal growth factor " + growthFactor );
		this.growthFactor = growthFactor;
	}


	/** Gets the growth factor.
	 *
	 * @return the growth factor of this set.
	 * @see #growthFactor(int)
	 */

	public int growthFactor() {
		return growthFactor;
	}


	/*
	 * The following methods implements some basic building blocks used by
	 * all accessors. They are (and should be maintained) identical to those used in HashMap.drv.
	 */

	/** Searches for a key, keeping track of a possible insertion point.
	 *
	 * @param k the key.
	 * @return the index of the correct insertion point, if the key is not found; otherwise,
	 * <var>-i</var>-1, where <var>i</var> is the index of the entry containing the key.
	 */

	protected final int findInsertionPoint( final KEY_GENERIC_TYPE k ) {
		final KEY_GENERIC_TYPE key[] = this.key;
		final byte state[] = this.state;
		final int n = key.length;

		// First of all, we make the key into a positive integer.
#if #keyclass(Object)
		final int h, k2i = ( h = KEY2INTHASH( k ) ) & 0x7FFFFFFF; 
#else
		final int k2i = KEY2INTHASH(k) & 0x7FFFFFFF; 
#endif
		// The primary hash, a.k.a. starting point.
		int h1 = k2i % n;

		if ( state[ h1 ] == OCCUPIED && ! KEY_EQUALS( key[ h1 ], k ) ) {
			// The secondary hash.
			final int h2 = ( k2i % ( n - 2 ) ) + 1;
			do {
				h1 += h2;
				if ( h1 >= n || h1 < 0 ) h1 -= n;
			} while( state[ h1 ] == OCCUPIED && ! KEY_EQUALS( key[ h1 ], k ) ); // There's always a FREE entry.
		}

		if (state[ h1 ] == FREE) return h1;
		if (state[ h1 ] == OCCUPIED) return -h1-1; // Necessarily, KEY_EQUALS( key[ h1 ], k ).

		/* Tables without deletions will never use code beyond this point. */

		final int i = h1; // Remember first available bucket for later.
		  
		/** See the comments in the documentation of the interface Hash. */
		if ( ASSERTS ) assert state[ h1 ] == REMOVED;
		if ( ! KEY_EQUALS( key[ h1 ], k ) ) {
			// The secondary hash.
			final int h2 = ( k2i % ( n - 2 ) ) + 1;
			do {
				h1 += h2;
				if ( h1 >= n || h1 < 0 ) h1 -= n;
			}  while( state[ h1 ] != FREE && ! KEY_EQUALS( key[ h1 ], k ) );
		}
		  
		return state[ h1 ] == OCCUPIED ? -h1-1 : i; // In the first case, necessarily, KEY_EQUALS( key[ h1 ], k ).
	}


	/** Searches for a key.
	 *
	 * @param k the key.
	 * @return the index of the entry containing the key, or -1 if the key wasn't found.
	 */

	protected final int findKey( final KEY_GENERIC_TYPE k ) {
		final KEY_GENERIC_TYPE key[] = this.key;
		final byte state[] = this.state;
		final int n = key.length;

		// First of all, we make the key into a positive integer.
#if #keyclass(Object)
		final int h, k2i = ( h = KEY2INTHASH( k ) ) & 0x7FFFFFFF; 
#else
		final int k2i = KEY2INTHASH(k) & 0x7FFFFFFF; 
#endif
		// The primary hash, a.k.a. starting point.
		int h1 = k2i % n;
		  
		/** See the comments in the documentation of the interface Hash. */
		if ( state[ h1 ] != FREE && ! KEY_EQUALS( key[ h1 ], k ) ) {
			// The secondary hash.
			final int h2 = ( k2i % ( n - 2 ) ) + 1;
			do {
				h1 += h2;
				if ( h1 >= n || h1 < 0 ) h1 -= n;
			} while( state[ h1 ] != FREE && ! KEY_EQUALS( key[ h1 ], k ) ); // There's always a FREE entry.
		}

		return state[ h1 ] == OCCUPIED ? h1 : -1;  // In the first case, necessarily, KEY_EQUALS( key[ h1 ], k ).
	}

	public boolean add( final KEY_GENERIC_TYPE k ) {
		final int i = findInsertionPoint( k );
		if ( i < 0 ) return false;

		if ( state[ i ] == FREE ) free--;
		state[ i ] = OCCUPIED;
		key[ i ] = k;

#ifdef Linked
		if ( count == 0 ) {
			first = last = i;
			link[ i ] = 0;
		}
		else {
			link[ last ] ^= i ^ -1;
			link[ i ] = last ^ -1;
			last = i;
		}
#endif

		if ( ++count >= maxFill ) {
			int newP = Math.min( p + growthFactor, PRIMES.length - 1 );
			// Just to be sure that size changes when p is very small.
			while( PRIMES[ newP ] == PRIMES[ p ] ) newP++;
			rehash( newP ); // Table too filled, let's rehash
		}
		if ( free == 0 ) rehash( p );
		if ( ASSERTS ) checkTable();
		return true;
	}

	@SuppressWarnings("unchecked")
	public boolean remove( final KEY_TYPE k ) {
		final int i = findKey( KEY_GENERIC_CAST k );
		if ( i < 0 ) return false;
		state[ i ] = REMOVED;
		count--;

#if #keys(reference)
		key[ i ] = KEY_GENERIC_CAST HashCommon.REMOVED;
#endif

#ifdef Linked
		fixPointers( i );
#endif

		if ( ASSERTS ) checkTable();
		return true;
	}
	 
	@SuppressWarnings("unchecked")
	public boolean contains( final KEY_TYPE k ) {
		return findKey( KEY_GENERIC_CAST k ) >= 0;
	}


	/* Removes all elements from this set.
	 *
	 * <P>To increase object reuse, this method does not change the table size.
	 * If you want to reduce the table size, you must use {@link #trim()}.
	 *
	 */

	public void clear() {
		if ( free == state.length ) return;

		free = state.length;
		count = 0;

		ByteArrays.fill( state, FREE );

#if #keys(reference)
		ObjectArrays.fill( key, null );
#endif

#ifdef Linked
		first = last = -1;
#endif
	}



#ifdef Linked

	/** Modifies the {@link #link} vector so that the given entry is removed.
	 *
	 * <P>If the given entry is the first or the last one, this method will complete
	 * in constant time; otherwise, it will have to search for the given entry.
	 *
	 * @param i the index of an entry. 
	 */
	private void fixPointers( int i ) {
		if ( count == 0 ) {
			first = last = -1;
			return;
		}

		if ( first == i ) {
			first = link[ i ] ^ -1;
			link[ first ] ^= i ^ -1;
			return;
		}

		if ( last == i ) {
			last = link[ i ] ^ -1;
			link[ last ] ^= i ^ -1;
			return;
		}

		int j = first, prev = -1, next;
		while( ( next = link[ j ] ^ prev ) != i ) {
			prev = j;
			j = next;
		}
		link[ j ] ^= link[ i ] ^ i ^ j;
		link[ link[ i ] ^ j ] ^= i ^ j;
	}


	/** Returns the first element of this set in iteration order.
	 *
	 * @return the first element in iteration order.
	 */
	public KEY_GENERIC_TYPE FIRST() {
		if ( count == 0 ) throw new NoSuchElementException();
		return key[ first ];
	}


	/** Returns the last element of this set in iteration order.
	 *
	 * @return the last element in iteration order.
	 */
	public KEY_GENERIC_TYPE LAST() {
		if ( count == 0 ) throw new NoSuchElementException();
		return key[ last ];
	}


	public SORTED_SET KEY_GENERIC tailSet( KEY_GENERIC_TYPE from ) { throw new UnsupportedOperationException(); }
	public SORTED_SET KEY_GENERIC headSet( KEY_GENERIC_TYPE to ) { throw new UnsupportedOperationException(); }
	public SORTED_SET KEY_GENERIC subSet( KEY_GENERIC_TYPE from, KEY_GENERIC_TYPE to ) { throw new UnsupportedOperationException(); }
	
	public KEY_COMPARATOR KEY_SUPER_GENERIC comparator() { return null; }



	/** A list iterator over a linked set.
	 *
	 * <P>This class provides a list iterator over a linked hash set. The empty constructor runs in 
	 * constant time. The one-argoument constructor needs to search for the given element, but it is 
	 * optimized for the case of {@link java.util.SortedSet#last()}, in which case runs in constant time, too.
	 */
	private class SetIterator extends KEY_ABSTRACT_LIST_ITERATOR KEY_GENERIC {
		/** 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). */
		int prev = -1;
		/** 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). */
		int next = -1;
		/** The last entry that was returned (or -1 if we did not iterate or used {@link #remove()}). */
		int curr = -1;
		/** The current index (in the sense of a {@link java.util.ListIterator}). Note that this value is not meaningful when this {@link SetIterator} has been created using the nonempty constructor.*/
		int index = 0;

		SetIterator() {
			next = first;
		}

		SetIterator( KEY_GENERIC_TYPE from ) {
			if ( KEY_EQUALS( key[ last ], from ) ) {
				prev = last;
				index = count;
			}
			else {
				if ( ! contains( from ) ) throw new IllegalArgumentException( "The key " + from + " does not belong to this set." );
				next = first;
				KEY_GENERIC_TYPE k;
				do k = NEXT_KEY(); while( ! KEY_EQUALS( k, from ) );
				curr = -1;
			}
		}
					 
		public boolean hasNext() { return next != -1; }
		public boolean hasPrevious() { return prev != -1; }
					 
		public KEY_GENERIC_TYPE NEXT_KEY() {
			if ( ! hasNext() ) throw new NoSuchElementException();

			curr = next;
			next = link[ curr ] ^ prev;
			prev = curr;

			index++;

			return key[ curr ];
		}

		public KEY_GENERIC_TYPE PREV_KEY() {
			if ( ! hasPrevious() ) throw new NoSuchElementException();

			curr = prev;
			prev = link[ curr ] ^ next;
			next = curr;

			index--;

			return key[ curr ];
		}

		public int nextIndex() {
			return index;
		}

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

		
		@SuppressWarnings("unchecked")
		public void remove() {
			if ( curr == -1 ) throw new IllegalStateException();
			state[ curr ] = REMOVED;
#if #keys(reference)
			key[ curr ] = KEY_GENERIC_CAST HashCommon.REMOVED;
#endif
			if ( curr == prev ) {
				/* If the last operation was a next(), we are removing an entry that preceeds
				   the current index, and thus we must decrement it. */
				index--;
				prev = link[ curr ] ^ next;
			}
			else next = link[ curr ] ^ prev; // curr == next

			count--;
			/* Now we manually fix the pointers. Because of our knowledge of next
			   and prev, this is going to be faster than calling fixPointers(). */
			if ( prev == -1 ) first = next;
			else link[ prev ] ^= curr ^ next;
			if ( next == -1 ) last = prev;
			else link[ next ] ^= curr ^ prev;
			curr = -1;
		}
	}


	/** Returns a type-specific list iterator on the elements in this set, starting from a given element of the set.
	 *
	 * <P>This method provides an iterator positioned immediately after the
	 * given element. That is, the next call to <code>previous()</code> will
	 * return <code>from</code>, whereas the next call to <code>next()</code>
	 * will return the element immediately after <code>from</code>. This
	 * allows to call <code>iterator(last())</code> and obtain an iterator starting
	 * from the end of the iteration order.
	 *
	 * <P>Because of the way linking is implemented, generating an iterator using this method
	 * requires constant time only if the argument is <code>last()</code>. In all other cases,
	 * a linear search for the given element will be necessary.
	 *
	 * <P>Note that this method returns a bidirectional iterator, which, however, can be safely cast to 
	 * a type-specific list iterator.
	 *
	 * @param from an element to start from.
	 * @return a type-specific list iterator starting at the given element.
	 * @throws IllegalArgumentException if <code>from</code> does not belong to the set.
	 */
	public KEY_BIDI_ITERATOR KEY_GENERIC iterator( KEY_GENERIC_TYPE from ) {
		return new SetIterator( from );
	}

	public KEY_BIDI_ITERATOR KEY_GENERIC iterator() {
		return new SetIterator();
	}

#else	 

	/** An iterator over a hash set. */

	private class SetIterator extends KEY_ABSTRACT_ITERATOR KEY_GENERIC {
		/** The index of the next entry to be returned. */
		int pos = 0;
		/** The index of the last entry that has been returned. */
		int last = -1;
		/** A downward counter measuring how many entries have been returned. */
		int c = count;
		
		{ 
			final byte state[] = OPEN_DOUBLE_HASH_SET.this.state;
			final int n = state.length;
			
			if ( c != 0 ) while( pos < n && state[pos] != OCCUPIED ) pos++;
		}
		
		public boolean hasNext() {
			return c != 0 && pos < OPEN_DOUBLE_HASH_SET.this.state.length;
		}
		
		public KEY_GENERIC_TYPE NEXT_KEY() {
			KEY_GENERIC_TYPE retVal;
			final byte state[] = OPEN_DOUBLE_HASH_SET.this.state;
			final int n = state.length;
			
			if ( ! hasNext() ) throw new NoSuchElementException();
			retVal = key[ last = pos ];
			if ( --c != 0 ) do pos++; while( pos < n && state[ pos ] != OCCUPIED );
			
			return retVal;
		}
		
		@SuppressWarnings("unchecked")
		public void remove() {
			if ( last == -1 || state[ last ] != OCCUPIED ) throw new IllegalStateException();
			state[last] = REMOVED;
#if #keys(reference)
			key[ last ] = KEY_GENERIC_CAST HashCommon.REMOVED;
#endif
			count--;
		}
	}

	public KEY_ITERATOR KEY_GENERIC iterator() {
		return new SetIterator();
	}

#endif



	/** Rehashes this set without changing the table size.
	 *
	 * <P>This method should be called when the set underwent numerous
	 * deletions and insertions.  In this case, free entries become rare, and
	 * unsuccessful searches require probing <em>all</em> entries.  For
	 * reasonable load factors this method is linear in the number of entries.
	 * You will need as much additional free memory as that occupied by the
	 * table.
	 *
	 * <P>If you need to reduce the table siza to fit exactly
	 * this set, you must use {@link #trim()}.
	 *
	 * @return true if there was enough memory to rehash the set, false otherwise.
	 * @see #trim()
	 */

	public boolean rehash() {
		try {
			rehash( p );
		}
		catch( OutOfMemoryError cantDoIt ) { return false; }
		return true;
	}


	/** Rehashes this set, making the table as small as possible.
	 * 
	 * <P>This method rehashes the table to the smallest size satisfying the
	 * load factor. It can be used when the set will not be changed anymore, so
	 * to optimize access speed (by collecting deleted entries) and size.
	 *
	 * <P>If the table size is already the minimum possible, this method
	 * does nothing. If you want to guarantee rehashing, use {@link #rehash()}.
	 *
	 * @return true if there was enough memory to trim the set.
	 * @see #trim(int)
	 * @see #rehash()
	 */

	public boolean trim() {
		int l = Arrays.binarySearch( PRIMES, (int)( count / f ) + 1 );
		if ( l < 0 ) l = -l - 1;
		if ( l >= p ) return true;
		try {
			rehash( l );
		}
		catch(OutOfMemoryError cantDoIt) { return false; }
		return true;
	}

	/** Rehashes this set if the table is too large.
	 * 
	 * <P>Let <var>N</var> be the smallest table size that can hold
	 * <code>max(n,{@link #size()})</code> entries, still satisfying the load factor. If the current
	 * table size is smaller than or equal to <var>N</var>, this method does
	 * nothing. Otherwise, it rehashes this set in a table of size
	 * <var>N</var>.
	 *
	 * <P>This method is useful when reusing sets.  {@linkplain #clear() Clearing a
	 * set} leaves the table size untouched. If you are reusing a set
	 * many times, you can call this method with a typical
	 * size to avoid keeping around a very large table just
	 * because of a few large transient sets.
	 *
	 * @param n the threshold for the trimming.
	 * @return true if there was enough memory to trim the set.
	 * @see #trim()
	 * @see #rehash()
	 */

	public boolean trim( final int n ) {
		int l = Arrays.binarySearch( PRIMES, (int)( Math.min( Integer.MAX_VALUE - 1, Math.max( n, count ) / f ) ) + 1 );
		if ( l < 0 ) l = -l - 1;
		if ( p <= l ) return true;
		try {
			rehash( l );
		}
		catch( OutOfMemoryError cantDoIt ) { return false; }
		return true;
	}

	/** Resizes the set.
	 *
	 * <P>This method implements the basic rehashing strategy, and may be
	 * overriden by subclasses implementing different rehashing strategies (e.g.,
	 * disk-based rehashing). However, you should not override this method
	 * unless you understand the internal workings of this class.
	 *
	 * @param newP the new size as an index in {@link Hash#PRIMES}.
	 */

	@SuppressWarnings("unchecked")
	protected void rehash( final int newP ) {
#ifdef Linked
		int i = first, j = count, prev = -1, newPrev = -1, t, k2i, h1, h2;
#else
		int i = 0, j = count, k2i, h1, h2;
		final byte state[] = this.state;
#endif

		//System.err.println("Rehashing to size " +  PRIMES[newP] + "...");

		KEY_GENERIC_TYPE k;

		final int newN = PRIMES[ newP ];
		final KEY_GENERIC_TYPE key[] = this.key, newKey[] = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[ newN ];
		final byte newState[] = new byte[ newN ];
#ifdef Linked
		final int link[] = this.link, newLink[] = new int[ newN ];
		first = -1;
#endif

		while( j-- != 0 ) {

#ifndef Linked
			while( state[ i ] != OCCUPIED ) i++;
#endif

			k = key[ i ];
			k2i = KEY2INTHASH( k ) & 0x7FFFFFFF;

			h1 = k2i % newN;

			if ( newState[ h1 ] != FREE ) {
				h2 = ( k2i % ( newN - 2 ) ) + 1;
				do {
					h1 += h2;
					if ( h1 >= newN || h1 < 0 ) h1 -= newN;
				} while( newState[ h1 ] != FREE );
			}
				
			newState[ h1 ] = OCCUPIED;
			newKey[ h1 ] = k;

#ifdef Linked
			t = i;
			i = link[ i ] ^ prev;
			prev = t;

			if ( first != -1 ) {
				newLink[ newPrev ] ^= h1;
				newLink[ h1 ] = newPrev;
				newPrev = h1;
			}
			else {
				newPrev = first = h1;
				newLink[ h1 ] = -1;
			}
#else
			i++;
#endif
		}

		p = newP;
		free = newN - count;
		maxFill = (int)( newN * f );
		this.key = newKey;
		this.state = newState;
#ifdef Linked
		this.link = newLink;
		this.last = newPrev;
		if ( newPrev != -1 ) newLink[ newPrev ] ^= -1; 
#endif
	}

	public int size() {
		return count;
	}

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



	/** Returns a deep copy of this set. 
	 *
	 * <P>This method performs a deep copy of this hash set; 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 set.
	 */

	@SuppressWarnings("unchecked")
	public OPEN_DOUBLE_HASH_SET KEY_GENERIC clone() {
		OPEN_DOUBLE_HASH_SET KEY_GENERIC c;
		try {
			c = (OPEN_DOUBLE_HASH_SET KEY_GENERIC)super.clone();
		}
		catch(CloneNotSupportedException cantHappen) {
			throw new InternalError();
		}
		c.key = key.clone();
		c.state = state.clone();
#ifdef Linked
		c.link = link.clone();
#endif
#ifdef Custom
		c.strategy = strategy;
#endif
		return c;
	}

	/** Returns a hash code for this set.
	 *
	 * This method overrides the generic method provided by the superclass. 
	 * Since <code>equals()</code> is not overriden, it is important
	 * that the value returned by this method is the same value as
	 * the one returned by the overriden method.
	 *
	 * @return a hash code for this set.
	 */


	public int hashCode() {
		int h = 0, i = 0, j = count;
		while( j-- != 0 ) {
			while( state[ i ] != OCCUPIED ) i++;
#if #keys(reference)
			if ( this != key[ i ] )
#endif
				h += KEY2JAVAHASH( key[ i ] );
			i++;
		}
		return h;
	}


	private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
		final KEY_ITERATOR KEY_GENERIC i = iterator();
		int j = count;
		s.defaultWriteObject();
		while( j-- != 0 ) s.WRITE_KEY( i.NEXT_KEY() );
	}


	@SuppressWarnings("unchecked")
	private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
		s.defaultReadObject();
		// We restore the default growth factor.
		growthFactor = Hash.DEFAULT_GROWTH_FACTOR;
		// Note that we DO NOT USE the stored p. See CHANGES.
		p = Arrays.binarySearch( PRIMES, (int)( count / f ) + 1 );
		if ( p < 0 ) p = -p - 1;

		final int n = PRIMES[ p ];
		maxFill = (int)( n * f );
		free = n - count;;
		
		final KEY_GENERIC_TYPE key[] = this.key = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[ n ];
		final byte state[] = this.state = new byte[ n ];
#ifdef Linked
		final int link[] = this.link = new int[ n ];
		int prev = -1;
		first = last = -1;
#endif

		int i, k2i, h1, h2;
		KEY_GENERIC_TYPE k;

		i = count;
		while( i-- != 0 ) {

			k = KEY_GENERIC_CAST s.READ_KEY();
			k2i = KEY2INTHASH( k ) & 0x7FFFFFFF;

			h1 = k2i % n;

			if ( state[ h1 ] != FREE ) {
				h2 = ( k2i % ( n - 2 ) ) + 1;
				do {
					h1 += h2;
					if ( h1 >= n || h1 < 0 ) h1 -= n;
				} while( state[ h1 ] != FREE );
			}

			state[ h1 ] = OCCUPIED;
			key[ h1 ] = k;

#ifdef Linked
			if ( first != -1 ) {
				link[ prev ] ^= h1;
				link[ h1 ] = prev;
				prev = h1;
			}
			else {
				prev = first = h1;
				link[ h1 ] = -1;
			}
#endif
		}

#ifdef Linked
		last = prev;
		if ( prev != -1 ) link[ prev ] ^= -1; 
#endif

		if ( ASSERTS ) checkTable();
	}


#ifdef ASSERTS_CODE
	private void checkTable() {
		int n = state.length;
		while( n-- != 0 ) 
			if ( state[ n ] == OCCUPIED && ! contains( key[ n ] ) ) 
				throw new AssertionError( "Hash table has key " + key[ n ] + " marked as occupied, but the key does not belong to the table" );

#ifdef Linked
		KEY_BIDI_ITERATOR KEY_GENERIC i = iterator();
		KEY_GENERIC_TYPE k;
		n = size();
		while( n-- != 0 ) 
			if ( ! contains( k = i.NEXT_KEY() ) ) 
				throw new AssertionError( "Linked hash table forward enumerates key " + k + ", but the key does not belong to the table" );

		if ( i.hasNext() ) throw new AssertionError( "Forward iterator not exhausted" );

		n = size();
		if ( n > 0 ) {
			i = iterator( LAST() );
			while( n-- != 0 ) 
				if ( ! contains( k = i.PREV_KEY() ) ) 
					throw new AssertionError( "Linked hash table backward enumerates key " + k + ", but the key does not belong to the table" );
			
			if ( i.hasPrevious() ) throw new AssertionError( "Previous iterator not exhausted" );
		}
#endif
	}
#else
	private void checkTable() {}
#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(); 
#elif #keyclass(Object)
#ifdef Custom
		int i = r.nextInt( 3 );
		byte a[] = new byte[ i ];
		while( i-- != 0 ) a[ i ] = (byte)r.nextInt();
		return a;
#else
		return Integer.toBinaryString( r.nextInt() );
#endif
#else
		return new java.io.Serializable() {};
#endif
	}


	private static final class ArrayComparator implements java.util.Comparator {
		public int compare( Object a, Object b ) {
			byte[] aa = (byte[])a;
			byte[] bb = (byte[])b;
			int length = Math.min( aa.length, bb.length );
			for( int i = 0; i < length; i++ ) {
				if ( aa[ i ] < bb[ i ] ) return -1;
				if ( aa[ i ] > bb[ i ] ) return 1;
			}
			return aa.length == bb.length ? 0 : ( aa.length < bb.length ? -1 : 1 );
		}
	}

	private static final class MockSet extends java.util.TreeSet {
		private java.util.List list = new java.util.ArrayList();

		public MockSet( java.util.Comparator c ) { super( c ); }

		public boolean add( Object k ) {
			if ( ! contains( k ) ) list.add( k );
			return super.add( k );
		}

		public boolean addAll( Collection c ) {
			java.util.Iterator i = c.iterator();
			boolean result = false;
			while( i.hasNext() ) result |= add( i.next() );
			return result;
		}

		public boolean removeAll( Collection c ) {
			java.util.Iterator i = c.iterator();
			boolean result = false;
			while( i.hasNext() ) result |= remove( i.next() );
			return result;
		}

		public boolean remove( Object k ) {
			if ( contains( k ) ) {
				int i = list.size();
				while( i-- != 0 ) if ( comparator().compare( list.get( i ), k ) == 0 ) {
					list.remove( i );
					break;
				}
			}
			return super.remove( k );
		}

		private void justRemove( Object k ) { super.remove( k ); }

		public java.util.Iterator iterator() {
			return new java.util.Iterator() {
					final java.util.Iterator iterator = list.iterator();
					Object curr;
					public Object next() { return curr = iterator.next(); }
					public boolean hasNext() { return iterator.hasNext(); }
					public void remove() { 
						justRemove( curr );
						iterator.remove(); 
					}
				};
		}
	}

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

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

	private static void speedTest( int n, float f, boolean comp ) {
#ifndef Custom
		int i, j;
		OPEN_DOUBLE_HASH_SET m;
#ifdef Linked
		java.util.LinkedHashSet t;
#else
		java.util.HashSet t;
#endif

		KEY_TYPE k[] = new KEY_TYPE[n];
		KEY_TYPE nk[] = new KEY_TYPE[n];
		long ms;

		for( i = 0; i < n; i++ ) {
			k[i] = genKey();
			nk[i] = genKey();
		}
		  
		double totAdd = 0, totYes = 0, totNo = 0, totIter = 0, totRemYes = 0, totRemNo = 0, d;

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

#ifdef Linked
			t = new java.util.LinkedHashSet( 16 );
#else
			t = new java.util.HashSet( 16 );
#endif

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

			/* We check for pairs in t. */
			ms = System.currentTimeMillis();
			for( i = 0; i < n;  i++ ) t.contains( 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.contains( 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( java.util.Iterator it = t.iterator(); it.hasNext(); it.next() );
			d = 1.0 * n / (System.currentTimeMillis() - ms );
			if ( j > 2 ) totIter += d; 				
			System.out.print("Iter: " + format( d ) +" K/s " );
				
			/* We delete pairs not in t. */
			ms = System.currentTimeMillis();
			for( i = 0; i < n;  i++ ) t.remove( KEY2OBJ( nk[i] ) );
			d = 1.0 * n / (System.currentTimeMillis() - ms );
			if ( j > 2 ) totRemNo += d; 				
			System.out.print("RemNo: " + format( d ) +" K/s " );
				
			/* We delete pairs in t. */
			ms = System.currentTimeMillis();
			for( i = 0; i < n;  i++ ) t.remove( KEY2OBJ( k[i] ) );
			d = 1.0 * n / (System.currentTimeMillis() - ms );
			if ( j > 2 ) totRemYes += d; 				
			System.out.print("RemYes: " + format( d ) +" K/s " );
				
			System.out.println();
		}

		System.out.println();
		System.out.println( "java.util Add: " + format( totAdd/(j-3) ) + " K/s Yes: " + format( totYes/(j-3) ) + " K/s No: " + format( totNo/(j-3) ) + " K/s Iter: " + format( totIter/(j-3) ) + " K/s RemNo: " + format( totRemNo/(j-3) ) + " K/s RemYes: " + format( totRemYes/(j-3) ) + "K/s" );

		System.out.println();

		totAdd = totYes = totNo = totIter = totRemYes = totRemNo = 0;
		}

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

			m = new OPEN_DOUBLE_HASH_SET( 16, f );

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

			/* We check for pairs in m. */
			ms = System.currentTimeMillis();
			for( i = 0; i < n;  i++ ) m.contains( 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.contains( 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. */
			ms = System.currentTimeMillis();
			for( KEY_ITERATOR it = (KEY_ITERATOR)m.iterator(); it.hasNext(); it.NEXT_KEY() );
			d = 1.0 * n / (System.currentTimeMillis() - ms );
			if ( j > 2 ) totIter += d; 	 
			System.out.print("Iter: " + format( d ) +" K/s " );

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

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

			System.out.println();
		}


		System.out.println();
		System.out.println( "fastutil  Add: " + format( totAdd/(j-3) ) + " K/s Yes: " + format( totYes/(j-3) ) + " K/s No: " + format( totNo/(j-3) ) + " K/s Iter: " + format( totIter/(j-3) ) + " K/s RemNo: " + format( totRemNo/(j-3) ) + " K/s RemYes: " + format( totRemYes/(j-3) ) + " K/s" );

		System.out.println();
#endif
	}


	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 void test( int n, float f ) {
		int c;
#ifdef Custom
		OPEN_DOUBLE_HASH_SET m = new OPEN_DOUBLE_HASH_SET(Hash.DEFAULT_INITIAL_SIZE, f, it.unimi.dsi.fastutil.bytes.ByteArrays.HASH_STRATEGY);
#else
		OPEN_DOUBLE_HASH_SET m = new OPEN_DOUBLE_HASH_SET(Hash.DEFAULT_INITIAL_SIZE, f);
#endif
#ifdef Linked
#ifdef Custom
		java.util.Set t = new MockSet(new ArrayComparator());
#else
		java.util.Set t = new java.util.LinkedHashSet();
#endif
#else
#ifdef Custom
		java.util.Set t = new java.util.TreeSet(new ArrayComparator());
#else 
		java.util.Set t = new java.util.HashSet();
#endif
#endif

		/* First of all, we fill t with random data. */

		for(int i=0; i<n;  i++ ) t.add(KEY2OBJ(genKey()));
		  
		/* Now we add to m the same data */
		  
		m.addAll(t); 

		if (!m.equals(t)) System.out.println("Error (" + seed + "): !m.equals(t) after insertion");
		if (!t.equals(m)) System.out.println("Error (" + seed + "): !t.equals(m) after insertion");

		/* Now we check that m actually holds that data. */
		  
		for(java.util.Iterator i=t.iterator(); i.hasNext();  ) {
			Object e = i.next();
			if (!m.contains(e)) {
				System.out.println("Error (" + seed + "): m and t differ on a key ("+e+") after insertion (iterating on t)");
				System.exit( 1 );
			}
		}

		/* Now we check that m actually holds that data, but iterating on m. */
		  
		for(java.util.Iterator i=m.iterator(); i.hasNext();  ) {
			Object e = i.next();
			if (!t.contains(e)) {
				System.out.println("Error (" + seed + "): m and t differ on a key ("+e+") after insertion (iterating on m)");
				System.exit( 1 );
			}
		}

		/* 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();
			if (m.contains(T) != t.contains(KEY2OBJ(T))) {
				System.out.println("Error (" + seed + "): divergence in keys 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();
			if (m.contains(KEY2OBJ(T)) != t.contains(KEY2OBJ(T))) {
				System.out.println("Error (" + seed + "): divergence between t and m (standard method)");
				System.exit( 1 );
			}
		}


		/* 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();
			if (m.add(KEY2OBJ(T)) != t.add(KEY2OBJ(T))) {
				System.out.println("Error (" + seed + "): divergence in add() between t and m");
				System.exit( 1 );
			}
			T = genKey();
			if (m.remove(KEY2OBJ(T)) != t.remove(KEY2OBJ(T))) {
				System.out.println("Error (" + seed + "): divergence in remove() between t and m");
				System.exit( 1 );
			}
		}

		if (!m.equals(t)) System.out.println("Error (" + seed + "): !m.equals(t) after removal");
		if (!t.equals(m)) System.out.println("Error (" + seed + "): !t.equals(m) after removal");

		/* Now we check that m actually holds that data. */
		  
		for(java.util.Iterator i=t.iterator(); i.hasNext();  ) {
			Object e = i.next();
			if (!m.contains(e)) {
				System.out.println("Error (" + seed + "): m and t differ on a key ("+e+") after removal (iterating on t)");
				System.exit( 1 );
			}
		}

		/* Now we check that m actually holds that data, but iterating on m. */
		  
		for(java.util.Iterator i=m.iterator(); i.hasNext();  ) {
			Object e = i.next();
			if (!t.contains(e)) {
				System.out.println("Error (" + seed + "): m and t differ on a key ("+e+") after removal (iterating on m)");
				System.exit( 1 );
			}
		}

		/* Now we make m into an array, make it again a set and check it is OK. */
		KEY_TYPE a[] = m.TO_KEY_ARRAY();
		  
#ifdef Custom
		if (!new OPEN_DOUBLE_HASH_SET(a, m.strategy()).equals(m))
			System.out.println("Error (" + seed + "): toArray() output (or array-based constructor) is not OK");
#else
		if (!new OPEN_DOUBLE_HASH_SET(a).equals(m))
			System.out.println("Error (" + seed + "): toArray() output (or array-based constructor) is not OK");
#endif

		/* Now we check cloning. */

		ensure( m.equals( ((OPEN_DOUBLE_HASH_SET)m).clone() ), "Error (" + seed + "): m does not equal m.clone()" );
		ensure( ((OPEN_DOUBLE_HASH_SET)m).clone().equals( m ), "Error (" + seed + "): m.clone() does not equal m" );

		int h = m.hashCode();

		/* Now we save and read m. */

		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);
				
			m = (OPEN_DOUBLE_HASH_SET)ois.readObject();
			ois.close();
			ff.delete();
		}
		catch(Exception e) {
			e.printStackTrace();
			System.exit( 1 );
		}

#if !#keyclass(Reference)
		if (m.hashCode() != h) System.out.println("Error (" + seed + "): hashCode() changed after save/read");

		/* Now we check that m actually holds that data, but iterating on m. */
		  
		for(java.util.Iterator i=m.iterator(); i.hasNext();  ) {
			Object e = i.next();
			if (!t.contains(e)) {
				System.out.println("Error (" + seed + "): m and t differ on a key ("+e+") after save/read");
				System.exit( 1 );
			}
		}
#else
		m.clear();
		m.addAll( t );
#endif

		/* 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();
			if (m.add(KEY2OBJ(T)) != t.add(KEY2OBJ(T))) {
				System.out.println("Error (" + seed + "): divergence in add() between t and m after save/read");
				System.exit( 1 );
			}
			T = genKey();
			if (m.remove(KEY2OBJ(T)) != t.remove(KEY2OBJ(T))) {
				System.out.println("Error (" + seed + "): divergence in remove() between t and m after save/read");
				System.exit( 1 );
			}
		}

		if (!m.equals(t)) System.out.println("Error (" + seed + "): !m.equals(t) after post-save/read removal");
		if (!t.equals(m)) System.out.println("Error (" + seed + "): !t.equals(m) after post-save/read removal");


#ifdef Linked

				 
		/* Now we play with iterators, but only in the linked case. */

		{
			java.util.ListIterator i, j;
			Object I, J;
			i = (java.util.ListIterator)m.iterator(); 
			j = new java.util.LinkedList( t ).listIterator(); 

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

				if ( r.nextFloat() < .8 && i.hasNext() ) {
#ifdef Custom
					ensure( m.strategy().equals( i.next(), J = j.next() ), "Error (" + seed + "): divergence in next()" );
#else
					ensure( i.next().equals( J = j.next() ), "Error (" + seed + "): divergence in next()" );
#endif
					if ( r.nextFloat() < 0.5 ) {
						i.remove();
						j.remove();
						t.remove( J );
					}
				}
				else if ( r.nextFloat() < .2 && i.hasPrevious() ) {
#ifdef Custom
					ensure( m.strategy().equals( i.previous(), J = j.previous() ), "Error (" + seed + "): divergence in previous()" );
#else
					ensure( i.previous().equals( J = j.previous() ), "Error (" + seed + "): divergence in previous()" );
#endif
					if ( r.nextFloat() < 0.5 ) {
						i.remove();
						j.remove();
						t.remove( J );
					}
				}

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

			}

		}

		if ( t.size() > 0 ) {
			java.util.ListIterator i, j;
			Object J;
			j = new java.util.LinkedList( t ).listIterator(); 
			int e = r.nextInt( t.size() );
			Object from;
			do from = j.next(); while( e-- != 0 );

			i = (java.util.ListIterator)m.iterator( KEY_OBJ2TYPE( from ) ); 

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

				if ( r.nextFloat() < .8 && i.hasNext() ) {
#ifdef Custom
					ensure( m.strategy().equals( i.next(), J = j.next() ), "Error (" + seed + "): divergence in next() (iterator with starting point " + from + ")" );
#else
					ensure( i.next().equals( J = j.next() ), "Error (" + seed + "): divergence in next() (iterator with starting point " + from + ")" );
#endif

					if ( r.nextFloat() < 0.5 ) {
						i.remove();
						j.remove();
						t.remove( J );
					}
				}
				else if ( r.nextFloat() < .2 && i.hasPrevious() ) {
#ifdef Custom
					ensure( m.strategy().equals( i.previous(), J = j.previous() ), "Error (" + seed + "): divergence in previous() (iterator with starting point " + from + ")" );
#else
					ensure( i.previous().equals( J = j.previous() ), "Error (" + seed + "): divergence in previous() (iterator with starting point " + from + ")" );
#endif

					if ( r.nextFloat() < 0.5 ) {
						i.remove();
						j.remove();
						t.remove( J );
					}
				}

				ensure( i.nextIndex() == j.nextIndex(), "Error (" + seed + "): divergence in nextIndex() (iterator with starting point " + from + ")" );
				ensure( i.previousIndex() == j.previousIndex(), "Error (" + seed + "): divergence in previousIndex() (iterator with starting point " + from + ")" );

			}

		}

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



#endif

		/* Now we take out of m everything, and check that it is empty. */

		for(java.util.Iterator i=m.iterator(); i.hasNext(); ) { i.next(); i.remove();} 

		if (!m.isEmpty())  {
			System.out.println("Error (" + seed + "): m is not empty (as it should be)");
			System.exit( 1 );
		}

#if #keyclass(Integer) || #keyclass(Long)
		m = new OPEN_DOUBLE_HASH_SET(n, f);
		t.clear();
		int x;

		/* Now we torture-test the hash table. This part is implemented only for integers and longs. */

		int p = m.state.length;

		for(int i=0; i<p; i++) {
			for (int j=0; j<20; j++) {
				m.add(i+(r.nextInt() % 10)*p);
				m.remove(i+(r.nextInt() % 10)*p);
			}

			for (int j=-10; j<10; j++) m.remove(i+j*p);
		}
		  
		t.addAll(m);

		/* Now all table entries are REMOVED. */
 
		int k = 0;
		for(int i=0; i<(p*f)/10; i++) {
			for (int j=0; j<10; j++) {
				k++;
				x = i+(r.nextInt() % 10)*p;
				if (m.add(x) != t.add(KEY2OBJ(x)))
					System.out.println("Error (" + seed + "): m and t differ on a key during torture-test insertion.");
			}
		}

		if (!m.equals(t)) System.out.println("Error (" + seed + "): !m.equals(t) after torture-test insertion");
		if (!t.equals(m)) System.out.println("Error (" + seed + "): !t.equals(m) after torture-test insertion");

		for(int i=0; i<(p*f)/10; i++) {
			for (int j=0; j<10; j++) {
				x = i+(r.nextInt() % 10)*p;
				if (m.remove(x) != t.remove(KEY2OBJ(x)))
					System.out.println("Error (" + seed + "): m and t differ on a key during torture-test removal.");
			}
		}

		if (!m.equals(t)) System.out.println("Error (" + seed + "): !m.equals(t) after torture-test removal");
		if (!t.equals(m)) System.out.println("Error (" + seed + "): !t.equals(m) after torture-test removal");

		if (!m.equals(m.clone())) System.out.println("Error (" + seed + "): !m.equals(m.clone()) after torture-test removal");
		if (!((OPEN_DOUBLE_HASH_SET)m.clone()).equals(m)) System.out.println("Error (" + seed + "): !m.clone().equals(m) after torture-test removal");

		m.rehash();

		if (!m.equals(t)) System.out.println("Error (" + seed + "): !m.equals(t) after rehash()");
		if (!t.equals(m)) System.out.println("Error (" + seed + "): !t.equals(m) after rehash()");

		m.trim();

		if (!m.equals(t)) System.out.println("Error (" + seed + "): !m.equals(t) after trim()");
		if (!t.equals(m)) System.out.println("Error (" + seed + "): !t.equals(m) after trim()");
#endif

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


	public static void main( String args[] ) {
		float f = Hash.DEFAULT_LOAD_FACTOR;
		int n  = Integer.parseInt(args[1]);
		if (args.length>2) f = Float.parseFloat(args[2]);
		if ( args.length > 3 ) r = new java.util.Random( seed = Long.parseLong( args[ 3 ] ) );
		  
		try {
			if ("speedTest".equals(args[0]) || "speedComp".equals(args[0])) speedTest( n, f, "speedComp".equals(args[0]) );
			else if ( "test".equals( args[0] ) ) test(n, f);
		} catch( Throwable e ) {
			e.printStackTrace( System.err );
			System.err.println( "seed: " + seed );
		}
	}

#endif

}
