package ch.idsia.blip.core.utils.data.hash; import ch.idsia.blip.core.utils.data.HashFunctions; import ch.idsia.blip.core.utils.data.set.TPrimitiveHash; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; /** * An open addressed hashing implementation for int/double primitive entries. * <p> * Created: Sun Nov 4 08:56:06 2001 * * @author Eric D. Friedman * @author Rob Eden * @author Jeff Randall * @version $Id: _K__V_Hash.template,v 1.1.2.6 2009/11/07 03:36:44 robeden Exp $ */ abstract public class TIntDoubleHash extends TPrimitiveHash { static final long serialVersionUID = 1L; /** * the set of ints */ transient int[] _set; /** * key that represents null * <p> * NOTE: should not be modified after the Hash is created, but is * not final because of Externalization */ int no_entry_key; /** * value that represents null * <p> * NOTE: should not be modified after the Hash is created, but is * not final because of Externalization */ double no_entry_value; boolean consumeFreeSlot; /** * Creates a new <code>T#E#Hash</code> instance with the default * capacity and load factor. */ TIntDoubleHash() { super(); no_entry_key = (int) 0; no_entry_value = (double) 0; } /** * Creates a new <code>T#E#Hash</code> instance whose capacity * is the next highest prime above <tt>initialCapacity + 1</tt> * unless that value is already prime. * * @param initialCapacity an <code>int</code> value */ TIntDoubleHash(int initialCapacity) { super(initialCapacity); no_entry_key = (int) 0; no_entry_value = (double) 0; } /** * Creates a new <code>TIntDoubleHash</code> instance with a prime * value at or near the specified capacity and load factor. * * @param initialCapacity used to find a prime capacity for the table. * @param loadFactor used to calculate the threshold over which * rehashing takes place. */ TIntDoubleHash(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor); no_entry_key = (int) 0; no_entry_value = (double) 0; } /** * Creates a new <code>TIntDoubleHash</code> instance with a prime * value at or near the specified capacity and load factor. * * @param initialCapacity used to find a prime capacity for the table. * @param loadFactor used to calculate the threshold over which * rehashing takes place. * @param no_entry_value value that represents null */ TIntDoubleHash(int initialCapacity, float loadFactor, int no_entry_key, double no_entry_value) { super(initialCapacity, loadFactor); this.no_entry_key = no_entry_key; this.no_entry_value = no_entry_value; } /** * Returns the value that is used to represent null as a key. The default * value is generally zero, but can be changed during construction * of the collection. * * @return the value that represents null */ public int getNoEntryKey() { return no_entry_key; } /** * Returns the value that is used to represent null. The default * value is generally zero, but can be changed during construction * of the collection. * * @return the value that represents null */ public double getNoEntryValue() { return no_entry_value; } /** * initializes the hashtable to a prime capacity which is at least * <tt>initialCapacity + 1</tt>. * * @param initialCapacity an <code>int</code> value * @return the actual capacity chosen */ protected int setUp(int initialCapacity) { int capacity; capacity = super.setUp(initialCapacity); _set = new int[capacity]; return capacity; } /** * Searches the set for <tt>val</tt> * * @param val an <code>int</code> value * @return a <code>boolean</code> value */ boolean contains(int val) { return index(val) >= 0; } /** * Releases the element currently stored at <tt>index</tt>. * * @param index an <code>int</code> value */ protected void removeAt(int index) { _set[index] = no_entry_key; super.removeAt(index); } /** * Locates the index of <tt>val</tt>. * * @param key an <code>int</code> value * @return the index of <tt>val</tt> or -1 if it isn't in the set. */ int index(int key) { int hash, probe, index, length; final byte[] states = _states; final int[] set = _set; length = states.length; hash = HashFunctions.hash(key) & 0x7fffffff; index = hash % length; byte state = states[index]; if (state == FREE) { return -1; } if (state == FULL && set[index] == key) { return index; } return indexRehashed(key, index, hash); } private int indexRehashed(int key, int index, int hash) { // see Knuth, p. 529 int length = _set.length; int probe = 1 + (hash % (length - 2)); final int loopIndex = index; do { index -= probe; if (index < 0) { index += length; } byte state = _states[index]; // if (state == FREE) { return -1; } // if (key == _set[index] && state != REMOVED) { return index; } } while (index != loopIndex); return -1; } /** * Locates the index at which <tt>val</tt> can be inserted. if * there is already a value equal()ing <tt>val</tt> in the set, * returns that value as a negative integer. * * @param val an <code>int</code> value * @return an <code>int</code> value */ int insertKey(int val) { int hash, index; hash = HashFunctions.hash(val) & 0x7fffffff; index = hash % _states.length; byte state = _states[index]; consumeFreeSlot = false; if (state == FREE) { consumeFreeSlot = true; insertKeyAt(index, val); return index; // empty, all done } if (state == FULL && _set[index] == val) { return -index - 1; // already stored } // already FULL or REMOVED, must probe return insertKeyRehash(val, index, hash, state); } private int insertKeyRehash(int val, int index, int hash, byte state) { // compute the double hash final int length = _set.length; int probe = 1 + (hash % (length - 2)); final int loopIndex = index; int firstRemoved = -1; /** * Look until FREE slot or we start to loop */ do { // Identify first removed slot if (state == REMOVED && firstRemoved == -1) { firstRemoved = index; } index -= probe; if (index < 0) { index += length; } state = _states[index]; // A FREE slot stops the search if (state == FREE) { if (firstRemoved != -1) { insertKeyAt(firstRemoved, val); return firstRemoved; } else { consumeFreeSlot = true; insertKeyAt(index, val); return index; } } if (state == FULL && _set[index] == val) { return -index - 1; } // Detect loop } while (index != loopIndex); // We inspected all reachable slots and did not find a FREE one // If we found a REMOVED slot we return the first one found if (firstRemoved != -1) { insertKeyAt(firstRemoved, val); return firstRemoved; } // Can a resizing strategy be found that resizes the set? throw new IllegalStateException( "No free or removed slots available. Key set full?!!"); } private void insertKeyAt(int index, int val) { _set[index] = val; // insert value _states[index] = FULL; } protected int XinsertKey(int key) { int hash, probe, index, length; final byte[] states = _states; final int[] set = _set; length = states.length; hash = HashFunctions.hash(key) & 0x7fffffff; index = hash % length; byte state = states[index]; consumeFreeSlot = false; if (state == FREE) { consumeFreeSlot = true; set[index] = key; states[index] = FULL; return index; // empty, all done } else if (state == FULL && set[index] == key) { return -index - 1; // already stored } else { // already FULL or REMOVED, must probe // compute the double hash probe = 1 + (hash % (length - 2)); // if the slot we landed on is FULL (but not removed), probe // until we find an empty slot, a REMOVED slot, or an element // equal to the one we are trying to insert. // finding an empty slot means that the value is not present // and that we should use that slot as the insertion point; // finding a REMOVED slot means that we need to keep searching, // however we want to remember the offset of that REMOVED slot // so we can reuse it in case a "new" insertion (thread.e. not an update) // is possible. // finding a matching value means that we've found that our desired // key is already in the table if (state != REMOVED) { // starting at the natural offset, probe until we find an // offset that isn't full. do { index -= probe; if (index < 0) { index += length; } state = states[index]; } while (state == FULL && set[index] != key); } // if the index we found was removed: continue probing until we // locate a free location or an element which equal()s the // one we have. if (state == REMOVED) { int firstRemoved = index; while (state != FREE && (state == REMOVED || set[index] != key)) { index -= probe; if (index < 0) { index += length; } state = states[index]; } if (state == FULL) { return -index - 1; } else { set[index] = key; states[index] = FULL; return firstRemoved; } } // if it's full, the key is already stored if (state == FULL) { return -index - 1; } else { consumeFreeSlot = true; set[index] = key; states[index] = FULL; return index; } } } /** * {@inheritDoc} */ public void writeExternal(ObjectOutput out) throws IOException { // VERSION out.writeByte(0); // SUPER super.writeExternal(out); // NO_ENTRY_KEY out.writeInt(no_entry_key); // NO_ENTRY_VALUE out.writeDouble(no_entry_value); } /** * {@inheritDoc} */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // VERSION in.readByte(); // SUPER super.readExternal(in); // NO_ENTRY_KEY no_entry_key = in.readInt(); // NO_ENTRY_VALUE no_entry_value = in.readDouble(); } } // TIntDoubleHash