//
// Copyright (C) 2006 United States Government as represented by the
// Administrator of the National Aeronautics and Space Administration
// (NASA). All Rights Reserved.
//
// This software is distributed under the NASA Open Source Agreement
// (NOSA), version 1.3. The NOSA has been approved by the Open Source
// Initiative. See the file NOSA-1.3-JPF at the top of the distribution
// directory tree for the complete NOSA document.
//
// THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY
// KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT
// LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO
// SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR
// A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT
// THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT
// DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE.
//
package gov.nasa.jpf.util;
import static java.lang.Integer.MIN_VALUE;
import java.util.Arrays;
/**
* This has approximately the interface of ObjVector but uses a hash table
* instead of an array. Also, does not require allocation with each add.
*/
public class SparseObjVector<E> {
@SuppressWarnings("unused")
private static final boolean DEBUG = false;
static final double MAX_LOAD_WIPE = 0.6;
static final double MAX_LOAD_REHASH = 0.4;
static final int DEFAULT_POW = 10;
int[] idxTable; // MIN_VALUE => unoccupied
Object[] valTable; // can be bound to null
int count;
int pow;
int mask;
int nextWipe;
int nextRehash;
/**
* Creates a SimplePool that holds about 716 elements before first
* rehash.
*/
public SparseObjVector() {
this(DEFAULT_POW);
}
/**
* Creates a SimplePool that holds about 0.7 * 2**pow elements before
* first rehash.
*/
public SparseObjVector(int pow) {
this.pow = pow;
newTable();
count = 0;
mask = valTable.length - 1;
nextWipe = (int)(MAX_LOAD_WIPE * mask);
nextRehash = (int)(MAX_LOAD_REHASH * mask);
}
public void clear() {
pow = DEFAULT_POW;
newTable();
count = 0;
mask = valTable.length - 1;
nextWipe = (int) (MAX_LOAD_WIPE * mask);
nextRehash = (int) (MAX_LOAD_REHASH * mask);
}
// INTERNAL //
protected void newTable() {
valTable = new Object[1 << pow];
idxTable = new int[1 << pow];
Arrays.fill(idxTable, MIN_VALUE);
}
protected int mix(int x) {
int y = 0x9e3779b9;
x ^= 0x510fb60d;
y += (x >> 8) + (x << 3);
x ^= (y >> 5) + (y << 2);
return y - x;
}
// ********************* Public API ******************** //
@SuppressWarnings("unchecked")
public E get(int idx) {
int code = mix(idx);
int pos = code & mask;
int delta = (code >> (pow - 1)) | 1; // must be odd!
int oidx = pos;
for(;;) {
int tidx = idxTable[pos];
if (tidx == MIN_VALUE) {
return null;
}
if (tidx == idx) {
return (E) valTable[pos];
}
pos = (pos + delta) & mask;
assert (pos != oidx); // should never wrap around
}
}
public void set(int idx, E e) {
int code = mix(idx);
int pos = code & mask;
int delta = (code >> (pow - 1)) | 1; // must be odd!
int oidx = pos;
for(;;) {
int tidx = idxTable[pos];
if (tidx == MIN_VALUE) {
break;
}
if (tidx == idx) {
valTable[pos] = e; // update
return; // and we're done
}
pos = (pos + delta) & mask;
assert (pos != oidx); // should never wrap around
}
// idx not in table; add it
if ((count+1) >= nextWipe) { // too full
if (count >= nextRehash) {
pow++;
}
/**
// determine if size needs to be increased or just wipe null blocks
int oldCount = count;
count = 0;
for (int i = 0; i < idxTable.length; i++) {
if (idxTable[i] != MIN_VALUE && valTable[i] != null) {
count++;
}
}
if (count >= nextRehash) {
pow++; // needs to be increased in size
if (DEBUG) {
System.out.println("Rehash to capacity: 2**" + pow);
}
} else {
if (DEBUG) {
System.out.println("Rehash reclaiming this many nulls: " + (oldCount - count));
}
}
**/
Object[] oldValTable = valTable;
int[] oldIdxTable = idxTable;
newTable();
mask = idxTable.length - 1;
nextWipe = (int)(MAX_LOAD_WIPE * mask);
nextRehash = (int)(MAX_LOAD_REHASH * mask);
int oldLen = oldIdxTable.length;
for (int i = 0; i < oldLen; i++) {
int tidx = oldIdxTable[i];
if (tidx == MIN_VALUE) continue;
Object o = oldValTable[i];
//if (o == null) continue;
// otherwise:
code = mix(tidx);
pos = code & mask;
delta = (code >> (pow - 1)) | 1; // must be odd!
while (idxTable[pos] != MIN_VALUE) { // we know enough slots exist
pos = (pos + delta) & mask;
}
idxTable[pos] = tidx;
valTable[pos] = o;
}
// done with rehash; now get idx to empty slot
code = mix(idx);
pos = code & mask;
delta = (code >> (pow - 1)) | 1; // must be odd!
while (idxTable[pos] != MIN_VALUE) { // we know enough slots exist
pos = (pos + delta) & mask;
}
} else {
// pos already pointing to empty slot
}
count++;
idxTable[pos] = idx;
valTable[pos] = e;
}
public void remove(int idx) {
set(idx, null);
}
// ************************** Test main ************************ //
public static void main(String[] args) {
SparseObjVector<Integer> vect = new SparseObjVector<Integer>(3);
// add some
for (int i = -4200; i < 4200; i += 10) {
vect.set(i, new Integer(i));
}
// check for added & non-added
for (int i = -4200; i < 4200; i += 10) {
Integer v = vect.get(i);
if (v.intValue() != i) {
throw new IllegalStateException();
}
}
for (int i = -4205; i < 4200; i += 10) {
Integer v = vect.get(i);
if (v != null) {
throw new IllegalStateException();
}
}
// add some more
for (int i = -4201; i < 4200; i += 10) {
vect.set(i, new Integer(i));
}
// check all added
for (int i = -4200; i < 4200; i += 10) {
Integer v = vect.get(i);
if (v.intValue() != i) {
throw new IllegalStateException();
}
}
for (int i = -4201; i < 4200; i += 10) {
Integer v = vect.get(i);
if (v.intValue() != i) {
throw new IllegalStateException();
}
}
// "remove" some
for (int i = -4200; i < 4200; i += 10) {
vect.remove(i);
}
// check for added & non-added
for (int i = -4201; i < 4200; i += 10) {
Integer v = vect.get(i);
if (v.intValue() != i) {
throw new IllegalStateException();
}
}
for (int i = -4200; i < 4200; i += 10) {
Integer v = vect.get(i);
if (v != null) {
throw new IllegalStateException();
}
}
// add even more
for (int i = -4203; i < 4200; i += 10) {
vect.set(i, new Integer(i));
}
for (int i = -4204; i < 4200; i += 10) {
vect.set(i, new Integer(i));
}
}
}