package is2.data;



import java.util.Arrays;

final public class IntIntHash  {


	protected int _size;
	protected int _free;
	protected float _loadFactor;
	public int _maxSize;
	protected int _autoCompactRemovesRemaining;
	protected float _autoCompactionFactor;
	public int _set[];
	private  int _values[];


	public IntIntHash() {
		this(102877, 0.5F);
	}


	public IntIntHash(int initialCapacity, float loadFactor) {
		_loadFactor = loadFactor;
		_autoCompactionFactor = loadFactor;
		setUp((int)Math.ceil(initialCapacity / loadFactor));
	}


	public int size()  { return _size;}

	public void ensureCapacity(int desiredCapacity) {

		if(desiredCapacity > _maxSize - size()) {
			rehash(PrimeFinder.nextPrime((int)Math.ceil((desiredCapacity + size()) / _loadFactor) + 1));
			computeMaxSize(capacity());
		}
	}

	public void compact()   {
		rehash(PrimeFinder.nextPrime((int)Math.ceil(size() / _loadFactor) + 1));
		computeMaxSize(capacity());
		if(_autoCompactionFactor != 0.0F) computeNextAutoCompactionAmount(size());
	}

	public void setAutoCompactionFactor(float factor) {
		if(factor < 0.0F)  {
			throw new IllegalArgumentException((new StringBuilder()).append("Factor must be >= 0: ").append(factor).toString());
		} else
		{
			_autoCompactionFactor = factor;
			return;
		}
	}

	public float getAutoCompactionFactor() {     return _autoCompactionFactor; }


	private void computeMaxSize(int capacity)
	{
		_maxSize = Math.min(capacity - 1, (int)Math.floor(capacity * _loadFactor));
		_free = capacity - _size;
	}

	private void computeNextAutoCompactionAmount(int size)
	{
		if(_autoCompactionFactor != 0.0F)
			_autoCompactRemovesRemaining = Math.round(size * _autoCompactionFactor);
	}

	protected final void postInsertHook(boolean usedFreeSlot)
	{
		if(usedFreeSlot)  _free--;
		if(++_size > _maxSize || _free == 0) {
			int newCapacity = _size <= _maxSize ? capacity() : PrimeFinder.nextPrime(capacity() << 1);
			rehash(newCapacity);
			computeMaxSize(capacity());
		}
	}

	protected int calculateGrownCapacity() { return capacity() << 1; }

	protected int capacity()  { return _values.length; }

	public boolean contains(int val)   { return index(val) >= 0;}

	private int index(int v) {

		int length = _set.length;
		int index =  Math.abs((computeHashCode(v) /*& 2147483647*/ ) % length);
		
		while(true) {
			// first
			long l =_set[index];
			if (l == 0) {
	//			good++;
				return -1;
			}
			// second 
			if (l == v) {
				return index;
			}
			if(--index < 0) index += length;			
		} 
			//return -1;
	}

	protected int insertionIndex(long val)
	{
		int length = _set.length;
		int index =  Math.abs((computeHashCode(val) /*& 2147483647*/ ) % length);
		while(true) {
			if(_set[index] == 0)  return index;
			if(_set[index] == val) return -index - 1;
			if(--index < 0) index += length;

		} 
	}

	public int computeHashCode(long value)
	{
			return (int)(( value ^ (value&0xffffffff00000000L) >>> 32 ) *31);//0x811c9dc5 ^ // 29
	}






	protected int setUp(int initialCapacity)
	{
		int capacity = PrimeFinder.nextPrime(initialCapacity);
		computeMaxSize(capacity);
		computeNextAutoCompactionAmount(initialCapacity);
		_set = new int[capacity];
		_values = new int[capacity];
		return capacity;
	}

	public void put(int key, int value)
	{
		int index = insertionIndex(key);
	    doPut(key, value, index);
	}
	private void doPut(int key, int value, int index)
	{
		boolean isNewMapping = true;
		if(index < 0)
		{
			index = -index - 1;
			isNewMapping = false;
		}		
		_set[index] = key;
		_values[index] = value;
		if(isNewMapping) postInsertHook(true);
	
	}

	protected void rehash(int newCapacity)
	{
		int oldCapacity = _set.length;
		int oldKeys[] = _set;
		int oldVals[] = _values;
		_set = new int[newCapacity];
		_values = new int[newCapacity];
		int i = oldCapacity;
		
		while(true){
			if(i-- <= 0) break;
			if(oldVals[i] != 0) {
				int o = oldKeys[i];
				int index = insertionIndex(o);
				_set[index] = o;
				_values[index] = oldVals[i];
			}
		}
	}

	int index =0;
	
	
	public int get(int key)
	{
		int index = index(key);
		return index >= 0 ? _values[index] : 0;
	}

	
	public void clear()
	{
		_size = 0;
		_free = capacity();
		Arrays.fill(_set, 0, _set.length, 0);
	//	Arrays.fill(_values, 0, _values.length, 0);
	}

	public int remove(int key)
	{
		int prev = 0;
		int index = index(key);
		if(index >= 0)
		{
			prev = _values[index];
			_values[index] = 0;
			_set[index] = 0;
			_size--;
			if(_autoCompactionFactor != 0.0F)  {
				_autoCompactRemovesRemaining--;
				if( _autoCompactRemovesRemaining <= 0) compact();
			}
		}
		return prev;
	}


	public int[] getValues()
	{
		int vals[] = new int[size()];
		int v[] = _values;
		int i = v.length;
		int j = 0;
		do
		{
			if(i-- <= 0) break;
			if(v[i] != 0) vals[j++] = v[i];
		} while(true);
		return vals;
	}

	public int[] keys()
	{
		int keys[] = new int[size()];
		int k[] = _set;
		//   byte states[] = _states;
		int i = k.length;
		int j = 0;
		do
		{
			if(i-- <= 0)
				break;
			if(k[i] != 0)
				keys[j++] = k[i];
		} while(true);
		return keys;
	}


	/**
	 * @param index2
	 * @param i
	 * @return
	 */
	public boolean adjustValue(int key, int i) {
		int index = index(key);
		if (index >= 0){ 
			 _values[index] +=i;
			return true;
		}
		return false;
	}







}
