// HashtableName : IHTObject
// KeyType       : Object
// Compare       : ==
// HashFunction  : .hashCode()
// NotFound      : null
// Default size  : 32768
// Synchronized  : no
// Serializable  : no
// Profiling     : no 

package ovm.util;

/**
 * This Hashtable is generated by TGen. Its key type is Object.
 * @author Ben L. Titzer
 * @author Christian Grothoff
 * @author TGen2
 **/
public class IHTObject extends ovm.core.OVMBase {

    /**
     * If true, choose power of 2 sizes, otherwise, choose power of 2
     * - 1. 
     **/
    private static final boolean SIZE_P2 = false;
    /**
     * If true, use System.identityHashCode(o), otherwise, use
     * o.hashCode()
     **/
    private static final boolean HASH_ID = true;
    private int size_;
    private int mask_;
    private int numElems_; // tracks the size of the hashtable
    private int sizeCollisions_;
    private int maskCollisions_;
    private final static int DEFAULT_SIZE = 32767;
    final Object[] keys_;

    /**
     * If there is a collision, we keep the 'collided' elements in here (linked list).
     **/
     final Binding[] collisions_;
    private static final Object NOTFOUND = null;

    /**
     * Internal binding class. Stores a key, value, and a link
     * to the next binding in the chain.
     **/
    static private final class Binding extends ovm.core.OVMBase {
        Binding link;
        Object key;
        Binding(Binding link, Object key) {
            this.link = link;
            this.key = key;
        }
    }

    protected IHTObject(IHTObject other) {
        complementaryView_ = other.complementaryView_;
	this.size_ = other.size_;
        this.mask_ = other.mask_;
	this.sizeCollisions_ = other.sizeCollisions_;
        this.maskCollisions_ = other.maskCollisions_;
        this.collisions_ = other.collisions_;
        this.keys_ = other.keys_;
    }

    protected IHTObject complementaryView_;

    public IHTObject getReadOnlyView() {
        if (complementaryView_ == null) {
            complementaryView_ = new ReadOnly(this);
        }
        return complementaryView_;
    }

    private static class ReadOnly extends IHTObject {
        ReadOnly(IHTObject other) {
            super(other);
            complementaryView_ = other;
        }

        public IHTObject getReadOnlyView() {
            return this;
        }
        public void put(Object key) {
            if (contains(key))
                return;
            throw new ovm.util.ReadonlyViewException(key.toString());
        }
        public int size() {
            return complementaryView_.size();
        }
        public void remove(Object key) {
            throw new ovm.util.ReadonlyViewException();
        }

    }

    /**
     * Public default constructor.
     **/
    public IHTObject() {
        this(DEFAULT_SIZE);
    }

    /**
     * Constructor that specifies a hint size.
     **/
    public IHTObject(int hint) {
        int size = 16;
        while (size < hint)
            size *= 2;
	if (SIZE_P2) {
	    size_ = size;
	    mask_ = size - 1;
	    sizeCollisions_ = size >> 3;
	    maskCollisions_ = sizeCollisions_ - 1;
	} else {
	    size_ = size - 1;
	    sizeCollisions_ = size_ >> 3;
	}
        /* make collision table 1/8th of the size of the main table */
        this.collisions_ = new Binding[sizeCollisions_];
        this.keys_ = new Object[size_];

    }

    /**
     * Return the size (number of elements currently present) of the hashtable.
     **/
    public int size() {
        return numElems_;
    }

    public boolean contains(Object key) {
        return get(key) != NOTFOUND;
    }

    public void add(Object key) {
        put(key);
    }

    public void addAll(IHTObject other) {
	if (size_ == other.size_) {
	    for (int i = 0; i < size_; i++) {
		Object o = other.keys_[i];
		if (o == null)
		    continue;
		Object t = keys_[i];
		if (o != t) {
		    if (t == null) {
			keys_[i] = o;
			numElems_++;
		    } else
			insertInCollisions((HASH_ID
					    ? System.identityHashCode(o)
					    : o.hashCode()) % sizeCollisions_,
					   o);
		}
	    }
	    for (int i = 0; i < other.sizeCollisions_; i++) {
		Binding b = other.collisions_[i];
		while (b != null) {
		    insertInCollisions((HASH_ID
					? System.identityHashCode(b.key)
					: b.key.hashCode()) % sizeCollisions_,
				       b.key);
		    b = b.link;
		}
	    }
	} else 
	    for (Iterator it = other.getIterator(); it.hasNext(); ) 
		add(it.next());
    }

    public void clear() {
	if (numElems_ > 0) {
	    for (int i = 0; i < size_; i++)
		keys_[i] = null;
	    for (int i = 0; i < sizeCollisions_; i++)
		collisions_[i] = null;
	    numElems_ = 0;
	}
    }

    /**
     * Get a key matching the given key based on some equals function.
     **/
    public final Object get(Object key) {
	int hash0 = HASH_ID ? System.identityHashCode(key) : key.hashCode();
	int hash = SIZE_P2 ? hash0 & mask_ : hash0 % size_;
        Object keys_hash = keys_[hash];
        if (keys_hash == null) {

            return NOTFOUND;
        }
        if (key == (keys_hash)) {

            return keys_hash;
        }
	int hashCollisions = (SIZE_P2
			      ? hash & maskCollisions_
			      : hash0 % sizeCollisions_);
        return findInCollisions(hashCollisions, key);
    }

    /**
     * Put a key into the hashtable. Checks to see if
     * the key is already in the hashtable, and if not, inserts
     * it into the hashtable.
     **/
    public void put(Object key) {
	int hash0 = HASH_ID ? System.identityHashCode(key) : key.hashCode(); 
	int hash =  SIZE_P2 ? hash0 & mask_ : hash0 % size_;
        Object keys_hash = keys_[hash];
        if (keys_hash == null) { // simple insert
            keys_[hash] = key;
            numElems_++;

            return;
        }
        if (key == (keys_hash)) { // replace

            return;
        }
        // collision
        int hashCollision = (SIZE_P2
			     ? hash & maskCollisions_
			     : hash0 % sizeCollisions_);
        insertInCollisions(hashCollision, key);

    }

    /**
     * Private function to search the collision table for a key.
     * Searches the collisions_ table starting at the given offset until
     * either the correct Binding is found or the end of the list is reached.
     **/
    private final Object findInCollisions(int offset, Object key) {
        Binding p;

        for (p = collisions_[offset]; p != null; p = p.link) {
            if (key == (p.key)) {
                return p.key;
            }

        }
        return NOTFOUND;
    }

    /**
     * Private function to insert a key into the collision table.
     * Searches the collisions_ table starting at the given offset and
     * continues until either the correct Binding is found, or the end of
     * the list is reached. If no Binding is found with the correct key, it
     * will create a new Binding to hold the key.
     **/
    private final void insertInCollisions(int offset, Object key) {
        Binding h = collisions_[offset], p;

        for (p = h; p != null; p = p.link) {
            if (key == (p.key)) {

                return;
            }

        }

        numElems_++;
        collisions_[offset] = new Binding(h, key);
    }

    public void remove(Object key) {
	int hash0 = HASH_ID ? System.identityHashCode(key) : key.hashCode();
	int hash = SIZE_P2 ? hash0 & mask_ : hash0 % size_;
        Object result = keys_[hash];
        if (result == NOTFOUND)
            return;
        if (key == (result)) {
            int offset = (SIZE_P2 ? hash & maskCollisions_
			  : hash0 % sizeCollisions_);
            Binding p = collisions_[offset];
            numElems_--;
	    // Pull an element up from collisions
            for (; p != null; p = p.link) {
		int chash0 = (HASH_ID
			      ? System.identityHashCode(p.key)
			      : p.key.hashCode());
		int chash = SIZE_P2 ? chash0 & mask_ : chash0 % size_;
                if (chash == hash)
                    break;
	    }
            if (p == null)
                keys_[hash] = NOTFOUND;
            else {
                Binding prev = collisions_[offset];
                if (prev == p)
                    collisions_[offset] = p.link;
                else {
                    while (prev.link != p)
                        prev = prev.link;
                    prev.link = p.link;
                }
                keys_[hash] = p.key;
            }
        } else {
            int offset = (SIZE_P2
			  ? hash & maskCollisions_
			  : hash0 % sizeCollisions_);
            Binding p = collisions_[offset];
            for (; p != null; p = p.link)
                if (key == (p.key))
                    break;
            if (p != null) {
                numElems_--;
                Binding prev = collisions_[offset];
                if (prev == p) {
                    collisions_[offset] = p.link;
                } else {
                    while (prev.link != p)
                        prev = prev.link;
                    prev.link = p.link;
                }
            }
        }
    }

    public void dumpCollisions() {
	final boolean SHOW_COLLISION_HIST = false;
	final boolean SHOW_COLLISION_VALUES = false;

	System.out.println("table contains " + numElems_ +
			   " elements in " + size_ + " buckets");
	int direct = 0;
	for (int i = 0; i < size_; i++)
	    if (keys_[i] != null)
		direct++;
	System.out.println(direct + " stored directly");
	if (!(SHOW_COLLISION_HIST || SHOW_COLLISION_VALUES))
	    return;
	for (int i = 0; i < sizeCollisions_; i++) {
	    Binding b = collisions_[i];
	    if (b == null)
		continue;
	    if (SHOW_COLLISION_HIST) {
		int depth = 0;
		while (b  != null) {
		    b = b.link;
		    depth++;
		}
		System.out.println(Integer.toHexString(i) + " => "
				   + depth + " collisions");
	    }
	    if (SHOW_COLLISION_VALUES) {
		System.out.println(Integer.toHexString(i)
				   + " collisions:");
		while (b != null) {
		    int hash = (HASH_ID
				? System.identityHashCode(b.key)
				: b.key.hashCode());
		    try {
			System.out.println(Integer.toHexString(hash)
					   + " => " + b.key);
		    } catch (RuntimeException _) { }
		    b = b.link;
		}
	    }
	}
    }

    public Iterator getIterator() {
        return new Iterator();
    }

    public final class Iterator {
        private int position_;
        private boolean iteratingOverKeys_;
        private Binding currentBinding_;
        private boolean reachEnd;

        Iterator() {
            currentBinding_ = collisions_[0];
            position_ = -1;
            iteratingOverKeys_ = true;
            reachEnd = false;
            findNext();
        }
        public boolean hasNext() {
            return !reachEnd;
        }
        public Object next() {
            Object res;
            if (iteratingOverKeys_) {
                res = keys_[position_];
            } else {
                res = currentBinding_.key;
            }
            findNext();
            return res;
        }
        public void remove() {
            throw new Error("Remove Not Implemented");
        }

        private void findNext() {
            if (iteratingOverKeys_) {
                position_++;
                while (position_ < keys_.length && keys_[position_] == null)
                    position_++;
                if (position_ == keys_.length) {
                    iteratingOverKeys_ = false;
                    position_ = 0;
                    while (position_ < collisions_.length
                        && collisions_[position_] == null)
                        position_++;
                    if (position_ == collisions_.length)
                        reachEnd = true;
                    else
                        currentBinding_ = collisions_[position_];
                }
            } else {
                currentBinding_ = currentBinding_.link;
                if (currentBinding_ == null) {
                    position_++;
                    while (position_ < collisions_.length
                        && collisions_[position_] == null)
                        position_++;
                    if (position_ == collisions_.length)
                        reachEnd = true;
                    else
                        currentBinding_ = collisions_[position_];
                }
            }
        }
    } // End Iterator

} // End IHTObject
