/* * `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 ; /** * <p> Abstract base hash array maintains input order over indexed * keys for a map from source (key) to target (value) values * (maintained internally). This is the infrastructure for a broad * variety of subclasses including not only specialized map key and * value types, but also multiple map indeces. </p> * * <h3>Not Synchronized</h3> * * <p> This class is not multi- thread safe, it is intended for use by * a single thread- user. External thread safety must be applied in * multi- threaded applications. </p> * * <h3>Multiple key instances</h3> * * <p> The key array may contain multiple identical keys. Dictionary * usage implies one key, array usage may imply additional * instances. </p> * * <h3>Multiple key and value types</h3> * * <p> This abstract base class supports both object and primitive * types for both keys and values. A primitive integer type key is * supported through a 64 bit long integer hash to represent a 31 bit * value (32 bit positive integer) plus a special internal value * (NIL). </p> * * @author John Pritchard */ public abstract class hasharray extends java.util.Dictionary implements java.lang.Cloneable { public final static int ZED = 0; public final static Object KO_NIL = null; public final static long KL_NIL = Long.MIN_VALUE; public final static long HASH_MASK = 0x7FFFFFFFL; /** * <p> Set a range of integer keys to special value NIL. </p> */ protected final static void NILKEYS ( long[] keys, int from, int many){ for ( int cc = from; cc < many; cc++) keys[cc] = KL_NIL; } /** * <p> Extensible index for the hash vector. Object map * subclasses can add indeces along- side the default object * index, 'table'. </p> * * <p> Each index has its own hash function. The default, defined * here, calls the instance object hash code method.</p> * * @author John Pritchard */ public static class Index implements Cloneable { /** * <p> Extensible index table record</p> * * @see hasharray$Index#newEntry() */ public static class Entry implements Cloneable { public final static int XINIT = -1; /** * Thirty two bit value, or high bit on for nil. * @see hasharray.KL_NIL */ public final long hash; /** <p> Initialized to {@link #XINIT}. </p> */ public int aryix = XINIT; protected Entry next; public Entry(long hash){ super(); this.hash = hash; } /** * @return Clean 31 bit value (positive integer) for table * arithmetic (modulo table length). */ public long hash(){ return (this.hash & HASH_MASK); } /** * <p> This method is called from the index. * </p> * @param caller Map * @param index Container * @param query Test key identity with internal value * @return Key identity */ public boolean kequals( hasharray caller, Index index, Object query){ if (XINIT < this.aryix){ Object key = caller.keyO(this.aryix); if (null != key) return key.equals(query); } return false; } /** * <p> This method is called from the index. * <pre> * return caller.keys[this.aryix].equals(query); * </pre> * </p> * @param caller Map * @param index Container * @param query Test key identity with internal value * @return Key identity */ public boolean kequals( hasharray caller, Index index, long query){ if (XINIT < this.aryix){ long key = caller.keyL(this.aryix); if (KL_NIL != key) return (key == query); } return false; } protected Object clone(){ return this.cloneEntry(); } protected Entry cloneEntry(){ try { Entry entry = (Entry)super.clone(); entry.next = (null != this.next)?((Entry)this.next.clone()):(null); return entry; } catch (CloneNotSupportedException cns){ throw new IllegalStateException(); } } } /** * <p> Copy the linked list to an array for the consistent * iteration over this list of table elements during changes * to the list. </p> */ public final static Entry[] ListCopy( Entry li){ if (null == li) return null; else if (null == li.next) return new Entry[]{li}; else { int bl = 10;/*(init optimistic output buffer) */ Entry[] re = new Entry[bl], copier; int rx = 0, rl = re.length; for (Entry pp = li; null != pp; rx += 1, pp = pp.next){ if (rx >= rl){ copier = new Entry[rl+bl];/*(grow output buffer optimistically) */ System.arraycopy(re,0,copier,0,rl); re = copier; rl += bl; } re[rx] = pp; } if (rx < rl){ /*(truncate optimistic buffer) */ copier = new Entry[rx]; System.arraycopy(re,0,copier,0,rx); return copier; } else return re; } } /** * <p> Append the element 'in' to the list 'li', maintaining * both the uniqueness of the element 'in' within the set 'li' * and the input order represented by each elements 'aryix' * value. </p> * * <p> The basic action here is to append 'in' to 'li', while * the 'aryix' value of 'li' can require an insertion for * maintaining the index table in the input order of the keys * and values arrays of the hasharray subclass. </p> * * @param li Table list * @param in Table element */ public final static Entry ListAppend( Entry li, Entry in){ if (null == li){ in.next = null; return in; } else if (null == in) throw new IllegalArgumentException(); else if (li == in) return li; else { int ix = in.aryix; if (Entry.XINIT == ix){ /* * Simple append of (in) to list (li). */ for (Entry lp = li; null != lp; lp = lp.next){ /* * Iterate over list (li) using list pointer (lp). */ if (in == lp) return li; else if (null == lp.next){ lp.next = in; in.next = null; return li; } } return li; } else { /* * Insert (in) into list (li) where (ix=in.aryix) < (lx=lp.aryix) */ int lx; Entry ll = null, lp = li; for (; null != lp; ll = lp, lp = lp.next){ if (in == lp) return li; else { lx = lp.aryix; if (ix < lx){ if (null == ll){ in.next = lp; return in; } else { in.next = lp; ll.next = in; return li; } } else if (ix == lx){ /* * Replace entry */ if (null == ll){ in.next = lp.next; lp.next = null;//delete(lp) return in; } else { ll.next = in; in.next = lp.next; return li; } } } } // ll.next = in; in.next = null; return li; } } } public Entry[] ListAppend(Entry[] list, Entry item){ if (null == list) return new Entry[]{item}; else { int list_len = list.length; switch(list_len){ case 1: return new Entry[]{list[0],item}; case 2: return new Entry[]{list[0],list[1],item}; case 3: return new Entry[]{list[0],list[1],list[2],item}; default: Entry[] nlist = new Entry[list_len+1]; java.lang.System.arraycopy(list,0,nlist,0,list_len); nlist[list_len] = item; return nlist; } } } /** * <p> Collision map. </p> */ protected Entry table[]; /** * <p> Current boundary for reindexing is table length times * load. </p> */ protected int threshold; /** * <p> A value between zero and one that defines the threshold * for reindexing. </p> */ protected float load; /** * <p> Internal capacity growth factor. </p> */ protected int grow; protected Index(int initial, float load){ super(); if ((initial <= 0) || (load <= 0f) || (load >= 1f)) throw new IllegalArgumentException(); else { if (0 == (initial & 1)) initial |= 1; this.load = load; this.table = new Entry[initial]; this.threshold = (int)((float)initial * load); this.grow = initial; } } protected Index(){ this(11,0.75f); } protected Entry newEntry(long hash){ return new Entry(hash); } /** * <p> This method is called from the indexing methods to call * the entry method. </p> * @param caller Map * @param index Container * @param query Test identity with internal value * @return Keys are identical */ protected boolean kequals( hasharray caller, Entry ent, Object query){ return ent.kequals(caller,this,query); } /** * <p> This method is called from the indexing methods to call * the entry method. </p> * @param caller Map * @param index Container * @param query Test identity with internal value * @return Keys are identical */ protected boolean kequals( hasharray caller, Entry ent, long query){ return ent.kequals(caller,this,query); } /** * @return Non negative 32 bit (31 bits) value for key hash code */ protected long hash(Object key){ return (key.hashCode() & Integer.MAX_VALUE); } /** * @return Non negative 32 bit (31 bits) value for key hash code */ protected long hash(long key){ return (key & HASH_MASK); } protected void clear(hasharray caller){ Entry table[] = this.table; for (int index = 0, tlen = table.length; index < tlen; index++){ table[index] = null; } } protected Object clone(){ return this.cloneIndex(); } protected Index cloneIndex(){ try { Index index = (Index)super.clone(); index.table = (Entry[])this.table.clone(); Entry ent; for (int ac = 0, an = this.table.length, bc, bn; ac < an; ac++){ ent = index.table[ac]; if (null != ent) index.table[ac] = ent.cloneEntry(); } return index; } catch (CloneNotSupportedException cns){ throw new IllegalStateException(); } } /** @return Found entry, or null */ protected Entry lookup(hasharray caller, Object key){ if (KO_NIL == key) return null; else { long hash = this.hash(key); Entry table[] = this.table; int index = ((int)(hash & HASH_MASK) % table.length); for (Entry e = table[index] ; e != null ; e = e.next){ if (hash == e.hash && this.kequals(caller,e,key)){ //caller.keys[e.aryix].equals(key)){ return e; } } return null; } } protected Entry lookup(hasharray caller, long key){ if (KL_NIL == key) return null; else { long hash = this.hash(key); Entry table[] = this.table; int index = ((int)(hash & HASH_MASK) % table.length); for (Entry e = table[index] ; e != null ; e = e.next){ if (hash == e.hash && this.kequals(caller,e,key)){ //caller.keys[e.aryix].equals(key)){ return e; } } return null; } } /** @return List or null */ protected Entry[] lookup_list(hasharray caller, Object key){ if (KO_NIL == key) return null; else { long hash = this.hash(key); Entry table[] = this.table; Entry list[] = null; int index = ((int)(hash & HASH_MASK) % table.length); for (Entry e = table[index] ; e != null ; e = e.next){ if (hash == e.hash && this.kequals(caller,e,key)){ list = ListAppend(list,e); } } return list; } } protected Entry[] lookup_list(hasharray caller, long key){ if (KL_NIL == key) return null; else { long hash = this.hash(key); Entry table[] = this.table; Entry list[] = null; int index = ((int)(hash & HASH_MASK) % table.length); for (Entry e = table[index] ; e != null ; e = e.next){ if (hash == e.hash && this.kequals(caller,e,key)){ list = ListAppend(list,e); } } return list; } } /** <p> Grow the table. </p> */ protected final void rehash(hasharray caller){ Entry ot[] = this.table, pp; int olen = ot.length, px, pn; int nlen = (olen + this.grow), cc, index; if (0 == (nlen & 1)) nlen |= 1; Entry nt[] = new Entry[nlen], pl[]; this.threshold = (int)(nlen * this.load); this.table = nt; for (cc = (olen-1); -1 < cc; cc--){ pl = ListCopy(ot[cc]); if (null != pl) for (px = 0, pn = pl.length; px < pn; px++){ pp = pl[px]; index = (((int)this.hash(pp.hash)) % nlen); nt[index] = ListAppend(nt[index],pp); } } } protected final boolean threshold(hasharray user){ return (user._size() >= this.threshold); } /** @return New entry */ protected Entry append( hasharray caller, Object key){ Entry table[] = this.table; long hash = this.hash(key); int index = ((int)(hash & HASH_MASK) % table.length); Entry ne = this.newEntry(hash); table[index] = ListAppend(table[index],ne); return ne; } protected Entry append( hasharray caller, long key){ Entry table[] = this.table; long hash = this.hash(key); int index = ((int)(hash & HASH_MASK) % table.length); Entry ne = this.newEntry(hash); table[index] = ListAppend(table[index],ne); return ne; } /** @return New entry */ protected Entry replace(hasharray caller, int idx, Object nkey, Object okey){ long nhash = this.hash(nkey); long ohash = this.hash(okey); Entry table[] = this.table; int nindex = (((int)(nhash & HASH_MASK)) % table.length); int oindex = (((int)(ohash & HASH_MASK)) % table.length); return this.replace(idx,nhash,ohash,table,nindex,oindex); } /** @return New entry */ protected Entry replace(hasharray caller, int idx, long nkey, long okey){ long nhash = this.hash(nkey); long ohash = this.hash(okey); Entry table[] = this.table; int nindex = (((int)(nhash & HASH_MASK)) % table.length); int oindex = (((int)(ohash & HASH_MASK)) % table.length); return this.replace(idx,nhash,ohash,table,nindex,oindex); } private Entry replace(int idx, long nhash, long ohash, Entry[] table, int nindex, int oindex){ Entry ne = this.newEntry(nhash), ie, le; /* * Drop OE */ for (le = null, ie = table[oindex]; null != ie; le = ie, ie = ie.next){ if (idx == ie.aryix){ if (null == le) table[oindex] = ie.next; else le.next = ie.next; } } /* * Append NE */ table[nindex] = ListAppend(table[nindex],ne); // return ne; } /** @return Dropped index entry */ protected Entry remove( hasharray caller, Object key){ if (KO_NIL == key) return null; else { Entry table[] = this.table; long hash = this.hash(key); int index = (((int)(hash & HASH_MASK)) % table.length); for (Entry ie = table[index], le = null ; ie != null ; le = ie, ie = ie.next){ if ((hash == ie.hash) && (this.kequals(caller,ie,key))) return this.remove_drop(le,ie,table,index); } return null; } } /** @return Dropped index entry */ protected Entry remove( hasharray caller, long key){ if (KL_NIL == key) return null; else { Entry table[] = this.table; long hash = this.hash(key); int index = (((int)(hash & HASH_MASK)) % table.length); for (Entry ie = table[index], le = null ; ie != null ; le = ie, ie = ie.next){ if ((hash == ie.hash) && (this.kequals(caller,ie,key))) return this.remove_drop(le,ie,table,index); } return null; } } private Entry remove_drop(Entry le, Entry ie, Entry[] table, int index){ Entry re = ie; int aryix = ie.aryix; /* * Drop IE */ if (null != le) le.next = ie.next; else table[index] = ie.next; /* * Sanitize RE=IE */ re.next = null; /* * Decrement pointers for remove */ for (int tc = 0, tlen = table.length; tc < tlen; tc++){ ie = table[tc]; while ( null != ie){ if (aryix < ie.aryix) ie.aryix -= 1; ie = ie.next; } } return re; } /** @return Dropped secondary (dupe) index entry */ protected Entry remove( hasharray caller, int idx){ Entry table[] = this.table, ie, le, re = null; int iearyix; scanindex: for (int tx = 0, tl = table.length; tx < tl; tx++){ for (le = null, ie = table[tx]; null != ie; le = ie, ie = ie.next){ iearyix = ie.aryix; if (idx < iearyix) /* * Decrement pointers for remove */ ie.aryix -= 1; else if (idx == iearyix){ /* * Drop IE */ re = ie; if (null == le) table[tx] = ie.next; else le.next = ie.next; re.next = null; } } } return re; } /** * @return Preexisting value, or null */ protected Entry store(hasharray caller, Object key){ if (null == key) return null; else { long hash = this.hash(key); Entry table[] = this.table; int index = (((int)(hash & HASH_MASK)) % table.length); /* * Lookup */ for (Entry ent = table[index] ; ent != null ; ent = ent.next){ if (ent.hash == hash){ if (this.kequals(caller,ent,key)) return ent; } } // if (this.threshold(caller)){ this.rehash(caller); return this.store(caller, key); } else { Entry nent = this.newEntry(hash); table[index] = ListAppend(table[index],nent); return nent; } } } protected Entry store(hasharray caller, long key){ if (KL_NIL == key) return null; else { long hash = key; Entry table[] = this.table; int index = ((int)(hash & HASH_MASK) % table.length); /* * Lookup */ for (Entry ent = table[index] ; ent != null ; ent = ent.next){ if (ent.hash == hash){ if (this.kequals(caller,ent,key)) return ent; } } // if (this.threshold(caller)){ this.rehash(caller); return this.store(caller, key); } else { Entry nent = this.newEntry(hash); table[index] = ListAppend(table[index],nent); return nent; } } } /** @return Existing or new entry */ protected Entry add( hasharray caller, Object key){ if (KO_NIL == key) return null; else if (this.threshold(caller)){ this.rehash(caller); return this.add(caller, key); } else { long hash = this.hash(key); Entry table[] = this.table; int index = (((int)(hash & HASH_MASK)) % table.length); Entry nent = this.newEntry(hash); table[index] = ListAppend(table[index],nent); return nent; } } protected Entry add( hasharray caller, long key){ if (KL_NIL == key) return null; else if (this.threshold(caller)){ this.rehash(caller); return this.add(caller, key); } else { long hash = this.hash(key); Entry table[] = this.table; int index = (((int)(hash & HASH_MASK)) % table.length); Entry nent = this.newEntry(hash); table[index] = ListAppend(table[index],nent); return nent; } } protected Entry insert(hasharray caller, int idx, Object key){ Entry table[] = this.table, re = null; long hash = this.hash(key); int index = (((int)(hash & HASH_MASK)) % table.length); Entry ne = this.newEntry(hash), ie; for (int ii = 0, il = table.length; ii < il; ii++){ if (ii == index){ ie = table[ii]; if (null == ie) table[ii] = ne; else { boolean nindexed = true; Entry le = null; for (; null != ie; le = ie, ie = ie.next){ if (hash == ie.hash){ if (this.kequals(caller,ie,key)){ if (ie.aryix <= idx){ ne.next = ie.next; ie.next = ne; } else if (null == le){ /* * Insert for (idx > ie.aryix) */ table[ii] = ne; ne.next = ie; } else { le.next = ne; ne.next = ie; } nindexed = false; } } if (ie != ne && idx <= ie.aryix) /* * Increment pointers for insert */ ie.aryix += 1; } if (nindexed){ ne.next = table[ii]; table[ii] = ne; } } }//(if (ii == index) else { for (ie = table[ii]; null != ie; ie = ie.next){ if (idx <= ie.aryix) /* * Increment pointers for insert */ ie.aryix += 1; } } } return ne; } protected Entry insert(hasharray caller, int idx, long key){ Entry table[] = this.table, re = null; long hash = this.hash(key); int index = (((int)(hash & HASH_MASK)) % table.length); Entry ne = this.newEntry(hash), ie; for (int ii = 0, il = table.length; ii < il; ii++){ if (ii == index){ ie = table[ii]; if (null == ie) table[ii] = ne; else { boolean nindexed = true; Entry le = null; for (; null != ie; le = ie, ie = ie.next){ if (hash == ie.hash){ if (this.kequals(caller,ie,key)){ if (ie.aryix <= idx){ ne.next = ie.next; ie.next = ne; } else if (null == le){ /* * Insert for (idx > ie.aryix) */ table[ii] = ne; ne.next = ie; } else { le.next = ne; ne.next = ie; } nindexed = false; } } if (ie != ne && idx <= ie.aryix) /* * Increment pointers for insert */ ie.aryix += 1; } if (nindexed){ ne.next = table[ii]; table[ii] = ne; } } }//(if (ii == index) else { for (ie = table[ii]; null != ie; ie = ie.next){ if (idx <= ie.aryix) /* * Increment pointers for insert */ ie.aryix += 1; } } } return ne; } } protected Index table; protected int count = 0; public hasharray(int initial, float load){ super(); this.table = this.newIndex(initial,load); } public hasharray(int initial){ this (initial, 0.75f); } /** * Default initial capacity is 11 elements. Default load factor * is three- quarters (of one). */ public hasharray(){ this( 11, 0.75f); } protected Index newIndex(int init, float load){ return new Index(init,load); } public void destroy(){ this.clear(); } public abstract void clear(); public int size(){ return this._size(); } protected final int _size(){ return this.count; } public boolean isEmpty(){ return this._isEmpty(); } protected final boolean _isEmpty(){ return (0 < this.count); } public boolean isNotEmpty(){ return this._isNotEmpty(); } protected final boolean _isNotEmpty(){ return (0 >= this.count); } /** The unused method throws UnsupportedOperationException */ public abstract Object keyO(int index); /** The unused method throws UnsupportedOperationException */ public abstract long keyL(int index); protected final Index.Entry _lookup( Object key){ return this.table.lookup(this,key); } protected final Index.Entry _lookup( long key){ return this.table.lookup(this,key); } protected final Index.Entry[] _lookup_list( Object key){ return this.table.lookup_list(this,key); } protected final Index.Entry[] _lookup_list( long key){ return this.table.lookup_list(this,key); } public void copy ( hasharray ano){ this._copy(ano); } protected final void _copy ( hasharray ano){ this.clear(); this.add(ano); } public void add ( hasharray ano){ this._add(ano); } protected final void _add ( hasharray ano){ if (null == ano) return; else { java.util.Enumeration keys = ano.keys(); Object k, v; while (keys.hasMoreElements()){ k = keys.nextElement(); v = ano.get(k); this.put(k,v); } } } public hasharray cloneHasharray(){ try { hasharray t = (hasharray)super.clone(); t.table = table.cloneIndex(); return t; } catch (CloneNotSupportedException e){ throw new InternalError(); } } /** * Print table with bracket wrappers, "name equals value, comma" * format, as in the following example. * * <pre> * "[" NAME1 "=" VALUE1 "," NAME2 "=" VALUE2 "]" * </pre> */ public String toString(){ return chbuf.cat( "[", toString('=',','), "]"); } /** * Print elements with characters "subinfix" and "infix" as in the * following example for two name, value pairs. * * <pre> * NAME1 subinfix VALUE1 infix NAME2 subinfix VALUE2 * </pre> * * <p> Uses <tt>"chbuf.append(Object)"</tt> which defaults to * <tt>"Object.toString()".</tt> * * @param subinfix Character between elements of a name- value * pair, eg, <tt>'='.</tt> * * @param infix Character between name- value pairs, eg, * <tt>','</tt> or <tt>'\n'</tt>. */ public String toString( char subinfix, char infix){ chbuf sb = new chbuf(); java.util.Enumeration keys = this.keys(); Object k; Object v; String ks, vs; for (int idx = 0; keys.hasMoreElements(); idx++){ k = keys.nextElement(); if (null != k){ ks = k.toString(); if ( null != ks){ if ( 0 < idx) sb.append(infix); sb.append(ks); v = this.get(k); if ( null != v){ vs = v.toString(); if ( null != vs){ sb.append(subinfix); sb.append(vs); } } } } } return sb.toString(); } /** * Copy over deleted index in buffer. Deleted means that the * value at index is ignored and simply overwritten by this * function. */ public final static void shift ( Object[] oary, int idx){ if ( 0 > idx) return ; else { int len = oary.length, len1 = oary.length-1; if ( 0 == idx) System.arraycopy( oary, 1, oary, 0, len1); else if ( idx == len1) oary[idx] = null; else if ( idx < len1) System.arraycopy( oary, (idx+1), oary, idx, len1-idx); } } /** * Copy over deleted index in buffer. Deleted means that the * value at index is ignored and simply overwritten by this * function. */ public final static void shift ( int[] iary, int idx){ if ( 0 > idx) return ; else { int len = iary.length, len1 = iary.length-1; if ( 0 == idx) System.arraycopy( iary, 1, iary, 0, len1); else if ( idx == len1) iary[idx] = ZED; else if ( idx < len1) System.arraycopy( iary, (idx+1), iary, idx, len1-idx); } } /** * Copy over deleted index in buffer. Deleted means that the * value at index is ignored and simply overwritten by this * function. */ public final static void shift ( long[] iary, int idx){ if ( 0 > idx) return ; else { int len = iary.length, len1 = iary.length-1; if ( 0 == idx) System.arraycopy( iary, 1, iary, 0, len1); else if ( idx == len1) iary[idx] = KL_NIL; else if ( idx < len1) System.arraycopy( iary, (idx+1), iary, idx, len1-idx); } } /** * <p> Object CAT with optional array component type.</p> * * @param a Optional object array * @param b Optional object array * @param c Optional component type */ public final static Object[] cat( Object[] a, Object[] b, Class c){ if (null == a) return b; else if (null == b) return a; else { int a_len = a.length; int b_len = b.length; int r_len = a_len+b_len; Object[] r; if (null != c) r = (Object[])java.lang.reflect.Array.newInstance(c,r_len); else r = new Object[r_len]; System.arraycopy(a,0,r,0,a_len); System.arraycopy(b,0,r,a_len,b_len); return r; } } /** * <p> Int CAT </p> * * @param a Optional array * @param b Optional array */ public final static int[] cat( int[] a, int[] b){ if (null == a) return b; else if (null == b) return a; else { int a_len = a.length; int b_len = b.length; int r_len = a_len+b_len; int[] r = new int[r_len]; System.arraycopy(a,0,r,0,a_len); System.arraycopy(b,0,r,a_len,b_len); return r; } } /** * <p> Long CAT </p> * * @param a Optional array * @param b Optional array */ public final static long[] cat( long[] a, long[] b){ if (null == a) return b; else if (null == b) return a; else { int a_len = a.length; int b_len = b.length; int r_len = a_len+b_len; long[] r = new long[r_len]; System.arraycopy(a,0,r,0,a_len); System.arraycopy(b,0,r,a_len,b_len); return r; } } public final static Object[] copy( Object[] a, Class c){ if (null == a) return a; else { int a_len = a.length; Object[] r; if (null != c) r = (Object[])java.lang.reflect.Array.newInstance(c,a_len); else r = new Object[a_len]; System.arraycopy(a,0,r,0,a_len); return r; } } public final static int[] copy( int[] a){ if (null == a) return a; else { int a_len = a.length; int[] r = new int[a_len]; System.arraycopy(a,0,r,0,a_len); return r; } } public final static long[] copy( long[] a){ if (null == a) return a; else { int a_len = a.length; long[] r = new long[a_len]; System.arraycopy(a,0,r,0,a_len); return r; } } public final static Object[] grow( Object[] a, int n_len, Class c){ if (1 > n_len) return a; else if (null == a){ Object[] r; if (null != c) r = (Object[])java.lang.reflect.Array.newInstance(c,n_len); else r = new Object[n_len]; return r; } else { int a_len = a.length; if (n_len > a_len){ Object[] r; if (null != c) r = (Object[])java.lang.reflect.Array.newInstance(c,n_len); else r = new Object[n_len]; System.arraycopy(a,0,r,0,a_len); return r; } else return a; } } public final static int[] grow( int[] a, int n_len){ if (1 > n_len) return a; else if (null == a){ int[] r = new int[n_len]; return r; } else { int a_len = a.length; if (n_len > a_len){ int[] r = new int[n_len]; System.arraycopy(a,0,r,0,a.length); return r; } else return a; } } public final static long[] grow( long[] a, int n_len){ if (1 > n_len) return a; else if (null == a){ long[] r = new long[n_len]; return r; } else { int a_len = a.length; if (n_len > a_len){ long[] r = new long[n_len]; System.arraycopy(a,0,r,0,a.length); return r; } else return a; } } protected static void usage( java.io.PrintStream out){ out.println(" usage: hasharray type N"); out.println(); out.println(" Test a hasharray for N elements."); out.println(); } /** * <p> Timed put and get test, with confirmation by enumeration of * keys over get. </p> */ public static void main (String[] argv){ if (null == argv || 2 > argv.length){ usage(System.err); System.exit(1); } else { try { String type = argv[0]; int N = Integer.parseInt(argv[1]); if (0 < N){ hasharray tm = null;//new hasharray(); Object tt; Object[] testvector = new Object[N]; long start; double duration; java.util.Random prng = new java.util.Random(); // start = System.currentTimeMillis(); for (int cc = 0; cc < N; cc++) testvector[cc] = new Integer(prng.nextInt()); duration = (double)(System.currentTimeMillis()-start); System.out.println("constructed test vector for "+N+" cycles in "+(duration/1000.0)+" seconds."); // int dupe = 0; start = System.currentTimeMillis(); for (int cc = 0; cc < N; cc++){ while (true){ tt = testvector[cc]; if (null != tm.get(tt)){ dupe += 1; System.out.println("input-store-test dup "+tt); } // tm.put(tt,tt); // if (tt != tm.get(tt)) throw new IllegalStateException("input-store-test failed at "+cc); else break; } } duration = (double)(System.currentTimeMillis()-start); // if ((N-dupe) != (tm.size())) throw new IllegalStateException("input-store-test failed for size "+tm.size()+" != N "+N); else { System.out.println("input-store-test completed for "+N+" cycles in "+(duration/1000.0)+" seconds."); java.util.Enumeration keys = tm.keys(); int count = 0; Object tk, tv; // start = System.currentTimeMillis(); while (keys.hasMoreElements()){ tk = keys.nextElement(); tv = tm.get(tk); count += 1; if (tv == tk) continue; else throw new IllegalStateException("keys-lookup-test miss "+tv+" != "+tk); } duration = (double)(System.currentTimeMillis()-start); // System.out.println("keys-lookup-test completed for "+count+" cycles in "+(duration/1000.0)+" seconds."); // start = System.currentTimeMillis(); for (int cc = 0; cc < N; cc++){ tt = testvector[cc]; tv = tm.get(tt); if (tt != tv) throw new IllegalStateException("vector-lookup-test miss "+tt+" != "+tv); } duration = (double)(System.currentTimeMillis()-start); // System.out.println("vector-lookup-test completed for "+count+" cycles in "+(duration/1000.0)+" seconds."); return;//(for test2)//System.exit(0); } } else throw new IllegalArgumentException(); } catch (NumberFormatException input){ usage(System.err); System.exit(1); } catch (IllegalArgumentException input){ usage(System.err); System.exit(1); } catch (Exception exc){ exc.printStackTrace(); System.exit(1); } } } /** * <p> Iterate over objects.</p> * * @author jdp */ public final static class Enumerator1 implements java.util.Enumeration { Object[] target = null; int tc = 0, len = 0; public Enumerator1 ( Object[] target){ this( target, ((null == target)?(0):(target.length))); } public Enumerator1 ( Object[] target, int size){ super(); this.target = target; this.len = size; } public boolean hasMoreElements(){ if (tc >= len) return false; else { int tt = tc; Object ret = target[tt]; while ( null == ret && tt < len && null == (ret = target[++tt])); if ( null == ret) return false; else return true; } } public Object nextElement(){ if ( tc >= len) throw new java.util.NoSuchElementException(); else { Object ret = null; while ( tc < len && null == (ret = target[tc++])); if ( null == ret) throw new java.util.NoSuchElementException(); else return ret; } } } /** * <p> Iterate over integers. Returns {@link java.lang.Integer} * objects.</p> * * @author jdp */ public final static class Enumerator2 implements java.util.Enumeration { int[] target = null; int tc = 0, len = 0; public Enumerator2 ( int[] target){ this( target, ((null == target)?(0):(target.length))); } public Enumerator2 ( int[] target, int size){ super(); this.target = target; this.len = size; } public boolean hasMoreElements(){ if (tc >= len) return false; else return true; } public Object nextElement(){ if ( tc >= len) throw new java.util.NoSuchElementException(); else { int ret = target[tc++]; return new java.lang.Integer(ret); } } } /** * <p> Iterate over long integer keys as 31 bit integers using * Hash Mask. Returns {@link java.lang.Integer} objects.</p> * * @author jdp */ public final static class Enumerator3 implements java.util.Enumeration { long[] target = null; int tc = 0, len = 0; public Enumerator3 ( long[] target){ this( target, ((null == target)?(0):(target.length))); } public Enumerator3 ( long[] target, int size){ super(); this.target = target; this.len = size; } public boolean hasMoreElements(){ if (tc >= len) return false; else return true; } public Object nextElement(){ if ( tc >= len) throw new java.util.NoSuchElementException(); else { int ret = (int)(target[tc++] & HASH_MASK); return new java.lang.Integer(ret); } } } }