// // 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); } } }