//
// 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 IntVector but uses a hash table
* instead of an array. Also, does not require allocation with each add.
* Configurable default value.
*/
public class SparseIntVector implements Cloneable {
static final double MAX_LOAD_WIPE = 0.6;
static final double MAX_LOAD_REHASH = 0.4;
static final int DEFAULT_POW = 10;
static final int DEFAULT_VAL = 0;
/**
* a simplistic snapshot implementation that stores set indices/values in order to save space
*/
public static class Snapshot {
private final int length;
private final int pow, mask, nextWipe, nextRehash;
private final int[] positions;
private final int[] indices;
private final int[] values;
Snapshot (SparseIntVector v){
int len = v.idxTable.length;
length = len;
pow = v.pow;
mask = v.mask;
nextWipe = v.nextWipe;
nextRehash = v.nextRehash;
int size = v.count;
positions = new int[size];
indices = new int[size];
values = new int[size];
int[] idxTable = v.idxTable;
int[] valTable = v.valTable;
int j=0;
for (int i=0; i<len; i++) {
if (idxTable[i] != MIN_VALUE) {
positions[j] = i;
indices[j] = idxTable[i];
values[j] = valTable[i];
j++;
}
}
}
void restore (SparseIntVector v) {
int size = indices.length;
v.count = size;
v.pow = pow;
v.mask = mask;
v.nextWipe = nextWipe;
v.nextRehash = nextRehash;
int len = length;
int[] idxTable = new int[len];
int[] valTable = new int[len];
Arrays.fill(idxTable, MIN_VALUE);
for (int i=0; i<size; i++) {
int j = positions[i];
idxTable[j] = indices[i];
valTable[j] = values[i];
}
v.idxTable = idxTable;
v.valTable = valTable;
}
}
int[] idxTable; // MIN_VALUE => unoccupied
int[] valTable; // can be bound to null
int count;
int pow;
int mask;
int nextWipe;
int nextRehash;
int defaultValue;
/**
* Creates a SimplePool that holds about 716 elements before first
* rehash.
*/
public SparseIntVector() {
this(DEFAULT_POW,DEFAULT_VAL);
}
/**
* Creates a SimplePool that holds about 0.7 * 2**pow elements before
* first rehash.
*/
public SparseIntVector(int pow, int defValue) {
this.pow = pow;
newTable();
count = 0;
mask = valTable.length - 1;
nextWipe = (int)(MAX_LOAD_WIPE * mask);
nextRehash = (int)(MAX_LOAD_REHASH * mask);
defaultValue = defValue;
}
// INTERNAL //
protected void newTable() {
valTable = new int[1 << pow];
idxTable = new int[1 << pow];
if (defaultValue != 0) {
Arrays.fill(valTable, defaultValue);
}
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 ******************** //
public Snapshot getSnapshot() {
return new Snapshot(this);
}
public void restore (Snapshot snap) {
snap.restore(this);
}
public SparseIntVector clone() {
try {
SparseIntVector o = (SparseIntVector) super.clone();
o.idxTable = idxTable.clone();
o.valTable = valTable.clone();
return o;
} catch (CloneNotSupportedException cnsx) {
// can't happen
return null;
}
}
public int size() {
return count;
}
public void clear() {
Arrays.fill(valTable, defaultValue);
Arrays.fill(idxTable, MIN_VALUE);
count = 0;
}
public void clear(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; // nothing to clear
}
if (tidx == idx) {
count--;
idxTable[pos] = MIN_VALUE;
valTable[pos] = defaultValue;
return;
}
pos = (pos + delta) & mask;
assert (pos != oidx); // should never wrap around
}
}
public int 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 defaultValue;
}
if (tidx == idx) {
return valTable[pos];
}
pos = (pos + delta) & mask;
assert (pos != oidx); // should never wrap around
}
}
// for debug only
int count() {
int count = 0;
for (int i = 0; i < idxTable.length; i++) {
if (idxTable[i] != MIN_VALUE /*&& valTable[i] != defaultValue*/) {
count++;
}
}
return count;
}
public void set(int idx, int val) {
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] = val; // 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] != defaultValue) {
if (idxTable[i] != MIN_VALUE) {
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));
}
}
**/
int[] 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;
int o = oldValTable[i];
//if (o == defaultValue) 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] = val;
}
public void setRange (int fromIndex, int toIndex, int val) {
for (int i=fromIndex; i<toIndex; i++) {
set(i, val);
}
}
// ************************** Test main ************************ //
public static void main(String[] args) {
SparseIntVector vect = new SparseIntVector(3, MIN_VALUE);
// add some
for (int i = -4200; i < 4200; i += 10) {
vect.set(i, i);
}
// check for added & non-added
for (int i = -4200; i < 4200; i += 10) {
int v = vect.get(i);
if (v != i) {
throw new IllegalStateException();
}
}
for (int i = -4205; i < 4200; i += 10) {
int v = vect.get(i);
if (v != MIN_VALUE) {
throw new IllegalStateException();
}
}
// add some more
for (int i = -4201; i < 4200; i += 10) {
vect.set(i, i);
}
// check all added
for (int i = -4200; i < 4200; i += 10) {
int v = vect.get(i);
if (v != i) {
throw new IllegalStateException();
}
}
for (int i = -4201; i < 4200; i += 10) {
int v = vect.get(i);
if (v != i) {
throw new IllegalStateException();
}
}
// "remove" some
for (int i = -4200; i < 4200; i += 10) {
vect.set(i,MIN_VALUE);
}
// check for added & non-added
for (int i = -4201; i < 4200; i += 10) {
int v = vect.get(i);
if (v != i) {
throw new IllegalStateException();
}
}
for (int i = -4200; i < 4200; i += 10) {
int v = vect.get(i);
if (v != MIN_VALUE) {
throw new IllegalStateException();
}
}
// add even more
for (int i = -4203; i < 4200; i += 10) {
vect.set(i, i);
}
for (int i = -4204; i < 4200; i += 10) {
vect.set(i, i);
}
}
}