/*
* `gnu.iou'
* Copyright (C) 2006 John Pritchard.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
* 02111-1307 USA
*/
package gnu.iou ;
/**
* Integer map for an efficient and compact map from integers to
* objects. Maintains input order for keys and values. The integer
* value <tt>`Long.MIN_VALUE' (0xffffffffffffffff)</tt> is a special
* <tt>`NIL'</tt> key value that cannot be used as a key, it
* represents the empty (or "undefined") key. In normal usage it is
* not possible to employ the NIL key as keys are 32 bit integers.
*
* <pre>
* STANDARD DICTIONARY API
*
* Object put (Object key, Object value)
*
* Object get (Object key)
*
* INTMAP DICTIONARY API
*
* Object put (int key, Object value)
*
* Object get (int key)
*
* </pre>
*
* <p> The standard dictionary API simply wraps the int- key
* dictionary API. Users should not use the standard dictionary API
* unless they have object keys in memory already. The principal
* purpose of intmap is for applications that need to avoid creating
* objects for keys that are integer numbers using a java Hashtable.
*
* <p> A good object key should be an <tt>`Integer'</tt> class object,
* or any <tt>`Number'</tt> subclass, <tt>`Character'</tt> class
* object, or have a hashCode value that is consistent with respect to
* the semantic value (content) of the object. For example, both the
* AWT <tt>`Color'</tt> class and <tt>`InetAddress'</tt> class make
* good keys for this table because their hashcodes are a consistent
* and semantically meaningful value (a color value and an IP address,
* respectively).
*
* <p> For good keys, the intmap is an excellent lookup table,
* performing marginally faster than a java Hashtable, while having a
* heavier memory footprint (with its <tt>`keys'</tt> and
* <tt>`vals'</tt> arrays in addition to the index table). The intmap
* weighs more than the java Hashtable, but is faster and provides
* input- ordered output (storage).
*
* <p> Most classes of object keys will be used solely for an
* ambiguous <tt>`hashCode'</tt>, which in most cases will lead to
* unpredictable and undesireable behavior, and cannot be recommended.
*
* <p> The <tt>`String'</tt> class is a very bad intmap key because
* its hashcode can represent only part of the identity of the
* semantic value, or content, of most strings (ones more than four
* bytes long). However, even short strings of four or fewer
* characters are not good intmap keys because the string's hash
* function is not "crisp" or "tight".
*
* <p><b>API Caution</b>
*
* <p> Note that <i>indeces</i> and <i>keys</i> are two distinct and
* separate spaces. An integer key must be mapped into an integer
* index in order to access the <tt>`keys'</tt> and <tt>`vals'</tt>
* arrays (the function <tt>`indexOf(int)'</tt> performs the mapping
* from key to index). An index directly retrieves a key or value
* from the <tt>`keys'</tt> and <tt>`vals'</tt> arrays.
*
* <p><b>Not Synchronized</b>
*
* <p> Note that the `intmap' is not multi-thread safe. This class is
* optimized for high frequency, single- threaded use as required for
* thread specific data structures.
*
* @author John Pritchard */
public class intmap
extends hasharray
{
public long[] keys = null;
public Object[] vals = null;
public intmap(int initial, float load){
super(initial,load);
this.keys = new long[this.table.grow];
this.vals = new Object[this.table.grow];
}
public intmap(int initial){
super(initial);
this.keys = new long[this.table.grow];
this.vals = new Object[this.table.grow];
}
public intmap(){
super();
this.keys = new long[this.table.grow];
this.vals = new Object[this.table.grow];
}
public int[] keyary(){
return this._keyary();
}
protected final int[] _keyary(){
int many = this.count;
if ( 0 < many){
long[] keys = this.keys;
int[] ret = new int[many];
for (int idx = 0; idx < many; idx++)
ret[idx] = (int)(keys[idx] & 0xffffffffL);
return ret;
}
else
return null;
}
public Object[] valary(){
return this._valary();
}
protected final Object[] _valary(){
int many = this.count;
if ( 0 < many){
Object[] vals = this.vals;
Object[] ret = new Object[many];
System.arraycopy(vals,0,ret,0,many);
return ret;
}
else
return null;
}
public Object[] valary(Class arycla){
return this._valary(arycla);
}
protected final Object[] _valary(Class arycla){
if (null == arycla)
return this.valary();
else {
int many = this.count;
if ( 0 < many){
Object[] vals = this.vals;
Object[] ret = (Object[])java.lang.reflect.Array.newInstance(arycla,many);
System.arraycopy(vals,0,ret,0,many);
return ret;
}
else
return null;
}
}
public Object[] valary_filter(Class arycla){
return this._valary_filter(arycla);
}
protected final Object[] _valary_filter(Class arycla){
if (null == arycla)
return this.valary();
else {
int many = this.count;
if ( 0 < many){
Object vals[] = this.vals, val;
Object[] ret = (Object[])java.lang.reflect.Array.newInstance(arycla,many);
int rc = 0;
for (int cc = 0; cc < many; cc++){
val = vals[cc];
if (null != val && arycla.isAssignableFrom(val.getClass()))
ret[rc++] = val;
}
if (1 > rc)
return null;
else {
if (many != rc){
Object[] trunc = (Object[])java.lang.reflect.Array.newInstance(arycla,rc);
System.arraycopy(ret,0,trunc,0,rc);
return trunc;
}
else
return ret;
}
}
else
return null;
}
}
public long lastKey(){
return this._lastKey();
}
protected final long _lastKey(){
int idx = (this.count-1);
if (-1 < idx)
return this.keys[idx];
else
return KL_NIL;
}
public Object lastValue(){
return this._lastValue();
}
protected final Object _lastValue(){
int idx = (this.count-1);
if (-1 < idx)
return this.vals[idx];
else
return null;
}
public void lastValue( Object val){
this._lastValue(val);
}
protected final void _lastValue( Object val){
int idx = (this.count-1);
if (-1 < idx)
this.vals[idx] = val;
}
public java.util.Enumeration keys(){
return this._keys();
}
protected final java.util.Enumeration _keys(){
return new Enumerator3(this.keys, this.count);
}
public java.util.Enumeration elements(){
return this._elements();
}
protected final java.util.Enumeration _elements(){
return new Enumerator1(this.vals, this.count);
}
public boolean containsValue(Object value){
return this.contains(value);
}
public boolean _containsValue(Object value){
return this._contains(value);
}
public boolean contains(Object value){
return this._contains(value);
}
protected final boolean _contains(Object value){
if (value == null)
return false;
else {
Object sval, vals[] = this.vals;
for (int ti = (this.count-1) ; 0 <= ti; ti--){
sval = vals[ti];
if (null != sval && (value == sval || sval.equals(value))){
return true;
}
}
return false;
}
}
public boolean containsKey(int key){
return this._containsKey(key);
}
protected final boolean _containsKey(int key){
return (null != this._lookup(key));
}
public int indexOf(int key){
return this._indexOf(key);
}
protected final int _indexOf(int key){
Index.Entry e = this._lookup(key);
if (null == e)
return -1;
else
return e.aryix;
}
public int indexOf( int key, int fromIdx){
return this._indexOf(key,fromIdx);
}
protected final int _indexOf( int key, int fromIdx){
long lkey = key;
int many = this.count;
long keys[] = this.keys;
if (-1 < fromIdx && fromIdx < many){
for ( int idx = fromIdx; idx < many; idx++){
if (lkey == keys[idx])
return idx;
}
}
return -1;
}
public int lastIndexOf( int key){
return this._lastIndexOf(key);
}
protected final int _lastIndexOf( int key){
return this.lastIndexOf(key,(this.count-1));
}
public int lastIndexOf( int key, int fromIdx){
return this._lastIndexOf(key,fromIdx);
}
protected final int _lastIndexOf( int key, int fromIdx){
long lkey = key;
int many = this.count;
long keys[] = this.keys;
if (-1 < fromIdx && fromIdx < many){
for ( int idx = fromIdx; -1 < idx; idx--){
if (lkey == keys[idx])
return idx;
}
}
return -1;
}
public int indexOfValue( Object val){
return this.indexOfValue(val,0);
}
public int indexOfValue( Object val, int fromIdx){
return this._indexOfValue(val,fromIdx);
}
protected final int _indexOfValue( Object val, int fromIdx){
if (null == val)
return -1;
else {
int many = this.count;
Object vals[] = this.vals;
if (-1 < fromIdx && fromIdx < many){
for ( int idx = fromIdx; idx < many; idx++){
if (val == vals[idx])
return idx;
}
return -1;
}
else
return -1;
}
}
public int lastIndexOfValue( Object val){
return this.lastIndexOfValue(val,(this.count-1));
}
public int lastIndexOfValue( Object val, int fromIdx){
return this._lastIndexOfValue(val,fromIdx);
}
protected final int _lastIndexOfValue( Object val, int fromIdx){
if (null == val)
return -1;
else {
int many = this.count;
Object vals[] = this.vals;
if (-1 < fromIdx && fromIdx < many){
for ( int idx = fromIdx; -1 < idx; idx--){
if (val == vals[idx])
return idx;
}
return -1;
}
else
return -1;
}
}
public int indexOfValueClass( Class sup){
return this.indexOfValueClass(sup,-1);
}
public int indexOfValueClass( Class sup, int from){
return this._indexOfValueClass(sup,from);
}
protected final int _indexOfValueClass( Class sup, int from){
int count = this.count;
Object vals[] = this.vals, val;
if (null != sup && from < count){
if (0 > from) from = 0;
for (int idx = from; idx < count; idx++){
val = vals[idx];
if (null != val && sup.isAssignableFrom(val.getClass()))
return idx;
}
}
return -1;
}
public int lastIndexOfValueClass( Class sup){
return this.lastIndexOfValueClass(sup,Integer.MAX_VALUE);
}
public int lastIndexOfValueClass( Class sup, int from){
return this._lastIndexOfValueClass(sup,from);
}
protected final int _lastIndexOfValueClass( Class sup, int from){
int count = this.count;
Object vals[] = this.vals, val;
if (null != sup && -1 < from){
if (from >= count) from = (count-1);
for (int idx = from; -1 < idx; idx--){
val = vals[idx];
if (null != val && sup.isAssignableFrom(val.getClass()))
return idx;
}
}
return -1;
}
public Object get(Object key){
return this._get(key);
}
protected final Object _get(Object key){
if (null == key)
return null;
else if (!(key instanceof java.lang.Number))
throw new IllegalArgumentException("key "+key.getClass());
else
return this.get( ((java.lang.Number)key).intValue());
}
public Object get(int key){
int idx = this.indexOf(key);
if (-1 < idx && idx < this.count)
return this.vals[idx];
else
return null;
}
protected final Object _get(int key){
int idx = this._indexOf(key);
if (-1 < idx && idx < this.count)
return this.vals[idx];
else
return null;
}
public Object keyO(int idx){
return this._keyO(idx);
}
protected final Object _keyO(int idx){
if (-1 < idx && idx < this.count){
long lkey = this.keys[idx];
if (KL_NIL == lkey)
return null;
else
return new java.lang.Integer( (int)lkey);
}
else
return KO_NIL;
}
public int key(int idx){
return this._key(idx);
}
protected final int _key(int idx){
if (-1 < idx && idx < this.count){
long lkey = this.keys[idx];
if (KL_NIL == lkey)
return ZED;
else
return (int)lkey;
}
else
return ZED;
}
public long keyL(int idx){
return this._keyL(idx);
}
protected final long _keyL(int idx){
if (-1 < idx && idx < this.count)
return this.keys[idx];
else
return KL_NIL;
}
public Object value(int idx){
return this._value(idx);
}
protected final Object _value(int idx){
if (-1 < idx && idx < this.count)
return this.vals[idx];
else
return null;
}
public Object value(int idx, Object value){
return this._value(idx,value);
}
protected final Object _value(int idx, Object value){
if (-1 < idx && idx < this.count)
return (this.vals[idx] = value);
else
return null;
}
/**
* Replace the keyed value
*/
public Object put(Object key, Object value){
return this._put(key,value);
}
protected final Object _put(Object key, Object value){
if (null == key)
return null;
else if (!(key instanceof java.lang.Number))
throw new IllegalArgumentException("key "+key.getClass());
else
return this.put( ((java.lang.Number)key).intValue(), value);
}
public Object put(int key, Object value){
return this._put(key,value);
}
protected final Object _put(int key, Object value){
Index.Entry ent = this.table.store(this,key);
int aryix = ent.aryix;
if (Index.Entry.XINIT == aryix){
ent.aryix = this._vadd_(key,value);
return null;/*(new)*/
}
else {
Object old = this.vals[aryix];
this.vals[aryix] = value;
return old;
}
}
/**
* Add a potentially duplicate key. It enters the index (get- put
* "key" interface) when it is unique, or as a duplicate when the
* key (former) is removed.
*/
public int add( int key, Object val){
return this._add(key,val);
}
protected final int _add( int key, Object val){
Index.Entry ent = this.table.add(this,key);
int aryix = ent.aryix;
if (Index.Entry.XINIT == aryix){
ent.aryix = this._vadd_(key,val);
return ent.aryix;
}
else {
this.vals[aryix] = val;
return aryix;
}
}
/**
* <p> Insert the argument pair. If the key has been indexed
* before, the new key is inserted into the index before it.</p>
*/
public int insert( int idx, int key, Object val){
return this._insert(idx,key,val);
}
protected final int _insert( int idx, int key, Object val){
if (null == val)
return -1;
else if (0 > idx)
return this.add(key,val);
else {
long[] keys = this.keys;
Object[] vals = this.vals;
int len = keys.length;
long[] kcopier = new long[len+1];// simple aglo: always grow by one
Object[] vcopier = new Object[len+1];//
if ( 0 == idx){
System.arraycopy(keys,0,kcopier,1,len);
System.arraycopy(vals,0,vcopier,1,len);
kcopier[idx] = key;
vcopier[idx] = val;
}
else if (idx == (len-1)){
System.arraycopy(keys,0,kcopier,0,len);
System.arraycopy(vals,0,vcopier,0,len);
kcopier[idx] = key;
vcopier[idx] = val;
}
else {
System.arraycopy(keys,0,kcopier,0,idx);
System.arraycopy(vals,0,vcopier,0,idx);
System.arraycopy(keys,idx,kcopier,idx+1,(len-idx));//copied (many=idx) above
System.arraycopy(vals,idx,vcopier,idx+1,(len-idx));
kcopier[idx] = key;
vcopier[idx] = val;
}
this.keys = kcopier;
this.vals = vcopier;
this.count += 1;
Index.Entry ent = this.table.insert(this,idx,key);
if (Index.Entry.XINIT == ent.aryix){
ent.aryix = idx;
return idx;
}
else
throw new IllegalStateException("BBBUGGGG");
}
}
/**
* <p> Replace the key- value pair at key- value index 'idx' with
* the argument pair.</p>
*/
public int replace( int idx, int nkey, Object nval){
return this._replace(idx,nkey,nval);
}
protected final int _replace( int idx, int nkey, Object nval){
if (null == nval)
return -1;
else if (0 > idx || idx >= this.count)
return this.add(nkey,nval);
else {
long okey = this.keys[idx];
if (okey == nkey){
this.vals[idx] = nval;
return idx;
}
else {
Index.Entry ent = this.table.replace(this,idx,nkey,okey);
ent.aryix = idx;
this.keys[idx] = nkey;
this.vals[idx] = nval;
return idx;
}
}
}
/**
* <p> Append new key- value pair. If the key has been indexed
* before, the new key is appended into the index after it. </p>
*/
public int append( int nkey, Object nval){
return this._append(nkey,nval);
}
protected final int _append( int nkey, Object nval){
if (null == nval)
return -1;
else {
int idx = this._indexOf(nkey);
if (0 > idx)
return this.add(nkey,nval);
else {
idx = this._vadd_(nkey,nval);
//
Index.Entry ent = this.table.append(this,nkey);
ent.aryix = idx;
return idx;
}
}
}
/**
* Add to data vectors, no index activity. Manage optimistic
* vectors.
*/
protected final int _vadd_ ( int key, Object val){
int idx = this.count, len = this.keys.length;
if ( idx >= (len-1)){
// grow rate
int grow = (len/3);
if (this.table.grow > grow)
grow = this.table.grow;
//
int nlen = len+(grow);
// grow keys
long[] k_copier = new long[nlen];
System.arraycopy(keys,0,k_copier,0,len);
keys = k_copier;
// grow vals
Object[] v_copier = new Object[nlen];
System.arraycopy(vals,0,v_copier,0,len);
vals = v_copier;
}
this.keys[idx] = key;
this.vals[idx] = val;
this.count += 1;
return idx;
}
/**
* Drop slot by index
*/
public Object drop( int idx){
return this._drop(idx);
}
protected final Object _drop( int idx){
if (0 > idx)
return null;
else {
long key = this.keys[idx];
if (KL_NIL == key)
return null;
else {
int ikey = (int)key;
int kidx = this._indexOf(ikey);
if ( kidx == idx){
/*
* (this.keys[idx]) is the first indexed key
*/
return this._remove(ikey);
}
else {
if (-1 < kidx)
/*
* (this.keys[idx]) is not the first indexed key
*/
this.table.remove(this,idx);
Object old = this.vals[idx];
shift(this.keys,idx);
shift(this.vals,idx);
this.count--;
return old;
}
}
}
}
public Object remove( Object key){
return this._remove(key);
}
protected final Object _remove( Object key){
if (null == key)
return null;
else if (!(key instanceof java.lang.Number))
throw new java.lang.IllegalArgumentException("key "+key.getClass());
else {
int kei = ((java.lang.Number)key).intValue();
return this.remove(kei);
}
}
/**
* Remove key and value, index any latter identical key, truncate
* data arrays (keys and vals), scan index table and decrement
* pointer indeces for truncated data arrays.
*/
public Object remove( int key){
return this._remove(key);
}
protected final Object _remove( int key){
Index.Entry dropped = this.table.remove(this,key);
if (null != dropped){
int aryix = dropped.aryix;
if (-1 < aryix){
long keys[] = this.keys;
Object vals[] = this.vals;
Object ret = vals[aryix];
keys[aryix] = KL_NIL;
vals[aryix] = null;
shift(keys,aryix);
shift(vals,aryix);
this.count--;
return ret;
}
}
return null;
}
public void clear(){
this._clear();
}
protected final void _clear(){
this.table.clear(this);
long[] keys = this.keys;
Object[] vals = this.vals;
for (int index = 0, count = this.count; index < count; index++){
keys[index] = KL_NIL;
vals[index] = null;
}
this.count = 0;
}
public final intmap cloneIntmap(){
intmap t = (intmap)super.cloneHasharray();
t.keys = (long[])this.keys.clone();
t.vals = (Object[])this.vals.clone();
return t;
}
}