WALA/com.ibm.wala.util/src/com/ibm/wala/util/intset/SemiSparseMutableIntSet.java

761 lines
21 KiB
Java

/*******************************************************************************
* Copyright (c) 2002 - 2006 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.util.intset;
import com.ibm.wala.util.collections.CompoundIntIterator;
import com.ibm.wala.util.collections.EmptyIntIterator;
public class SemiSparseMutableIntSet implements MutableIntSet {
private static final boolean DEBUG = true;
private static final double FIX_SPARSE_MOD = 12;
private static final double FIX_SPARSE_RATIO = .05;
private MutableSparseIntSet sparsePart;
private OffsetBitVector densePart = null;
public SemiSparseMutableIntSet() {
this(MutableSparseIntSet.makeEmpty());
}
private SemiSparseMutableIntSet(MutableSparseIntSet sparsePart) {
this.sparsePart = sparsePart;
}
private SemiSparseMutableIntSet(MutableSparseIntSet sparsePart, OffsetBitVector densePart) {
this.sparsePart = sparsePart;
this.densePart = densePart;
}
public SemiSparseMutableIntSet(SemiSparseMutableIntSet set) throws IllegalArgumentException {
if (set == null) {
throw new IllegalArgumentException("set == null");
}
copySet(set);
}
private final boolean assertDisjoint() {
if (DEBUG) {
if (densePart != null) {
for (IntIterator sparseBits = sparsePart.intIterator(); sparseBits.hasNext();) {
int bit = sparseBits.next();
if (densePart.contains(bit)) {
return false;
}
if (inDenseRange(bit)) {
return false;
}
}
}
}
return true;
}
private void fixAfterSparseInsert() {
if (sparsePart.size() % FIX_SPARSE_MOD == FIX_SPARSE_MOD - 1
&& (densePart == null || (densePart != null && sparsePart.size() > FIX_SPARSE_RATIO * densePart.getSize()))) {
assert assertDisjoint() : this.toString();
if (densePart == null) {
IntIterator sparseBits = sparsePart.intIterator();
int maxOffset = -1;
int maxCount = -1;
int maxMax = -1;
int offset = 0;
int bits = 0;
int count = 0;
int oldBit = 0;
while (sparseBits.hasNext()) {
int nextBit = sparseBits.next();
int newBits = bits + (nextBit - oldBit);
int newCount = count + 1;
if (newBits < (32 * newCount)) {
count = newCount;
bits = newBits;
if (count > maxCount) {
maxOffset = offset;
maxMax = nextBit;
maxCount = count;
}
} else {
offset = nextBit;
count = 1;
bits = 32;
}
oldBit = nextBit;
}
if (maxOffset != -1) {
densePart = new OffsetBitVector(maxOffset, maxMax - maxOffset);
sparseBits = sparsePart.intIterator();
int bit;
while ((bit = sparseBits.next()) < maxOffset) {
// do nothing
}
densePart.set(bit);
for (int i = 1; i < maxCount; i++) {
densePart.set(sparseBits.next());
}
sparsePart.removeAll(densePart);
}
assert assertDisjoint() : this.toString() + ", maxOffset=" + maxOffset + ", maxMax=" + maxMax + ", maxCount=" + maxCount;
} else {
IntIterator sparseBits = sparsePart.intIterator();
int thisBit = sparseBits.next();
int moveCount = 0;
int newOffset = -1;
int newCount = -1;
int newLength = -1;
// push stuff just below dense part into it, if it saves space
if (thisBit < densePart.getOffset()) {
newOffset = thisBit;
int bits = 32;
int count = 1;
while (sparseBits.hasNext()) {
int nextBit = sparseBits.next();
if (nextBit >= densePart.getOffset() || !sparseBits.hasNext()) {
if (nextBit < densePart.getOffset() && !sparseBits.hasNext()) {
count++;
}
if (densePart.getOffset() - newOffset < (32 * count)) {
moveCount += count;
} else {
newOffset = -1;
}
thisBit = nextBit;
break;
} else {
bits += (nextBit - thisBit);
count++;
if (bits > (32 * count)) {
newOffset = nextBit;
count = 1;
bits = 32;
}
thisBit = nextBit;
}
}
}
while (thisBit < densePart.length() && sparseBits.hasNext()) {
thisBit = sparseBits.next();
}
// push stuff just above dense part into it, if it saves space
if (thisBit >= densePart.length()) {
int count = 1;
int bits = (thisBit + 1 - densePart.length());
if (32 * count > bits) {
newLength = thisBit;
newCount = 1;
}
while (sparseBits.hasNext()) {
thisBit = sparseBits.next();
count++;
bits = (thisBit + 1 - densePart.length());
if ((32 * count) > bits) {
newLength = thisBit;
newCount = count;
}
}
if (newLength > -1) {
moveCount += newCount;
}
}
// actually move bits from sparse to dense
if (moveCount > 0) {
int index = 0;
int[] bits = new int[moveCount];
for (sparseBits = sparsePart.intIterator(); sparseBits.hasNext();) {
int bit = sparseBits.next();
if (newOffset != -1 && bit >= newOffset && bit < densePart.getOffset()) {
bits[index++] = bit;
}
if (newLength != -1 && bit >= densePart.length() && bit <= newLength) {
bits[index++] = bit;
}
}
if (index != moveCount) {
assert index == moveCount : "index is " + index + ", but moveCount is " + moveCount + " for " + this;
}
if (newLength != -1 && bits[index - 1] == sparsePart.max()) {
int base = densePart.getOffset();
int currentSize = densePart.length() - base;
float newSize = 1.1f * (bits[index - 1] - base);
float fraction = newSize / currentSize;
assert fraction > 1;
densePart.growCapacity(fraction);
}
for (int i = index - 1; i >= 0; i--) {
sparsePart.remove(bits[i]);
densePart.set(bits[i]);
}
}
assert assertDisjoint() : this.toString() + ", densePart.length()=" + densePart.length() + ", newOffset=" + newOffset
+ ", newLength=" + newLength + ", newCount=" + newCount + ", moveCount=" + moveCount;
}
}
}
/*
* @see com.ibm.wala.util.intset.MutableIntSet#clear()
*/
@Override
public void clear() {
sparsePart.clear();
densePart = null;
}
/**
* @param i
* @return true iff this set contains integer i
*/
@Override
public boolean contains(int i) {
if (densePart != null && inDenseRange(i)) {
return densePart.contains(i);
} else {
return sparsePart.contains(i);
}
}
/**
* @return true iff this set contains integer i
*/
@Override
public boolean containsAny(IntSet set) {
if (set == null) {
throw new IllegalArgumentException("null set");
}
if (!sparsePart.isEmpty() && sparsePart.containsAny(set)) {
return true;
} else if (densePart != null) {
int lower = densePart.getOffset();
for (IntIterator is = set.intIterator(); is.hasNext();) {
int i = is.next();
if (i < lower)
continue;
if (densePart.get(i)) {
return true;
}
}
}
return false;
}
/**
* This implementation must not despoil the original value of "this"
*
* @return a new IntSet which is the intersection of this and that
*/
@Override
public IntSet intersection(IntSet that) {
if (that == null) {
throw new IllegalArgumentException("null that");
}
SemiSparseMutableIntSet newThis = new SemiSparseMutableIntSet();
for (IntIterator bits = intIterator(); bits.hasNext();) {
int bit = bits.next();
if (that.contains(bit)) {
newThis.add(bit);
}
}
return newThis;
}
/*
* @see com.ibm.wala.util.intset.IntSet#union(com.ibm.wala.util.intset.IntSet)
*/
@Override
public IntSet union(IntSet that) {
SemiSparseMutableIntSet temp = new SemiSparseMutableIntSet();
temp.addAll(this);
temp.addAll(that);
return temp;
}
/**
* @return true iff this set is empty
*/
@Override
public boolean isEmpty() {
return sparsePart.isEmpty() && (densePart == null || densePart.isZero());
}
/**
* @return the number of elements in this set
*/
@Override
public int size() {
return sparsePart.size() + (densePart == null ? 0 : densePart.populationCount());
}
/**
* @return a perhaps more efficient iterator
*/
@Override
public IntIterator intIterator() {
class DensePartIterator implements IntIterator {
private int i = -1;
@Override
public boolean hasNext() {
return densePart.nextSetBit(i + 1) != -1;
}
@Override
public int next() {
int next = densePart.nextSetBit(i + 1);
i = next;
return next;
}
}
if (sparsePart.isEmpty()) {
if (densePart == null || densePart.isZero()) {
return EmptyIntIterator.instance();
} else {
return new DensePartIterator();
}
} else {
if (densePart == null || densePart.isZero()) {
return sparsePart.intIterator();
} else {
return new CompoundIntIterator(sparsePart.intIterator(), new DensePartIterator());
}
}
}
/**
* Invoke an action on each element of the Set
*/
@Override
public void foreach(IntSetAction action) {
if (action == null) {
throw new IllegalArgumentException("null action");
}
sparsePart.foreach(action);
if (densePart != null) {
for (int b = densePart.nextSetBit(0); b != -1; b = densePart.nextSetBit(b + 1)) {
action.act(b);
}
}
}
/**
* Invoke an action on each element of the Set, excluding elements of Set X
*
* @param action
*/
@Override
public void foreachExcluding(IntSet X, IntSetAction action) {
sparsePart.foreachExcluding(X, action);
if (densePart != null) {
for (int b = densePart.nextSetBit(0); b != -1; b = densePart.nextSetBit(b + 1)) {
if (!X.contains(b)) {
action.act(b);
}
}
}
}
/**
* @return maximum integer in this set.
*/
@Override
public int max() throws IllegalStateException {
if (densePart == null) {
return sparsePart.max();
} else {
return Math.max(sparsePart.max(), densePart.max());
}
}
/**
* @return true iff <code>this</code> has the same value as <code>that</code>.
* @throws IllegalArgumentException if that is null
*/
@Override
public boolean sameValue(IntSet that) {
if (that == null) {
throw new IllegalArgumentException("that is null");
}
if (size() != that.size()) {
return false;
}
if (densePart != null) {
for (int bit = densePart.nextSetBit(0); bit != -1; bit = densePart.nextSetBit(bit + 1)) {
if (!that.contains(bit)) {
return false;
}
}
}
for (IntIterator bits = sparsePart.intIterator(); bits.hasNext();) {
if (!that.contains(bits.next())) {
return false;
}
}
return true;
}
/**
* @return true iff <code>this</code> is a subset of <code>that</code>.
* @throws IllegalArgumentException if that is null
*/
@Override
public boolean isSubset(IntSet that) {
if (that == null) {
throw new IllegalArgumentException("that is null");
}
if (size() > that.size()) {
return false;
}
for (IntIterator bits = sparsePart.intIterator(); bits.hasNext();) {
if (!that.contains(bits.next())) {
return false;
}
}
if (densePart != null) {
for (int b = densePart.nextSetBit(0); b != -1; b = densePart.nextSetBit(b + 1)) {
if (!that.contains(b)) {
return false;
}
}
}
return true;
}
/**
* Set the value of this to be the same as the value of set
*
* @throws IllegalArgumentException if set == null
*/
@Override
public void copySet(IntSet set) throws IllegalArgumentException {
if (set == null) {
throw new IllegalArgumentException("set == null");
}
if (set instanceof SemiSparseMutableIntSet) {
SemiSparseMutableIntSet that = (SemiSparseMutableIntSet) set;
sparsePart = MutableSparseIntSet.make(that.sparsePart);
if (that.densePart == null) {
densePart = null;
} else {
densePart = new OffsetBitVector(that.densePart);
}
} else {
densePart = null;
sparsePart = MutableSparseIntSet.makeEmpty();
for (IntIterator bits = set.intIterator(); bits.hasNext();) {
add(bits.next());
}
}
}
private boolean inDenseRange(int i) {
return densePart.getOffset() <= i && densePart.length() > i;
}
/**
* Add all members of set to this.
*
* @return true iff the value of this changes.
* @throws IllegalArgumentException if set == null
*/
@Override
public boolean addAll(IntSet set) throws IllegalArgumentException {
if (set == null) {
throw new IllegalArgumentException("set == null");
}
boolean change = false;
if (set instanceof SemiSparseMutableIntSet) {
SemiSparseMutableIntSet that = (SemiSparseMutableIntSet) set;
if (densePart == null) {
// that dense part only
if (that.densePart != null) {
int oldSize = size();
densePart = new OffsetBitVector(that.densePart);
for (IntIterator bits = sparsePart.intIterator(); bits.hasNext();) {
int bit = bits.next();
if (inDenseRange(bit)) {
densePart.set(bit);
}
}
sparsePart.removeAll(densePart);
sparsePart.addAll(that.sparsePart);
change = size() != oldSize;
// no dense part
} else {
change = sparsePart.addAll(that.sparsePart);
fixAfterSparseInsert();
}
} else {
// both dense parts
if (that.densePart != null) {
int oldSize = size();
densePart.or(that.densePart);
sparsePart.addAll(that.sparsePart);
for (IntIterator bits = sparsePart.intIterator(); bits.hasNext();) {
int bit = bits.next();
if (inDenseRange(bit)) {
densePart.set(bit);
}
}
sparsePart.removeAll(densePart);
change = size() != oldSize;
// this dense part only
} else {
for (IntIterator bs = that.sparsePart.intIterator(); bs.hasNext();) {
change |= add(bs.next());
}
}
}
} else {
for (IntIterator bs = set.intIterator(); bs.hasNext();) {
change |= add(bs.next());
}
}
assert assertDisjoint() : this.toString();
return change;
}
/**
* Add an integer value to this set.
*
* @param i integer to add
* @return true iff the value of this changes.
*/
@Override
public boolean add(int i) {
if (densePart != null && inDenseRange(i)) {
if (!densePart.get(i)) {
densePart.set(i);
assert assertDisjoint() : this.toString();
return true;
}
} else if (!sparsePart.contains(i)) {
sparsePart.add(i);
assert assertDisjoint() : this.toString();
fixAfterSparseInsert();
return true;
}
return false;
}
/**
* Remove an integer from this set.
*
* @param i integer to remove
* @return true iff the value of this changes.
*/
@Override
public boolean remove(int i) {
if (densePart != null && densePart.get(i)) {
densePart.clear(i);
if (densePart.nextSetBit(0) == -1) {
densePart = null;
}
return true;
} else if (sparsePart.contains(i)) {
sparsePart.remove(i);
return true;
} else {
return false;
}
}
/**
* Interset this with another set.
*
* @param set
*/
@Override
public void intersectWith(IntSet set) {
sparsePart.intersectWith(set);
if (densePart != null) {
for (int b = densePart.nextSetBit(0); b != -1; b = densePart.nextSetBit(b + 1)) {
if (!set.contains(b)) {
densePart.clear(b);
}
}
}
}
/**
* @throws IllegalArgumentException if other is null
*/
@Override
public boolean addAllInIntersection(IntSet other, IntSet filter) {
if (other == null) {
throw new IllegalArgumentException("other is null");
}
if (filter == null) {
throw new IllegalArgumentException("null filter");
}
boolean change = false;
for (IntIterator bits = other.intIterator(); bits.hasNext();) {
int bit = bits.next();
if (filter.contains(bit)) {
change |= add(bit);
}
}
return change;
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("[");
if (densePart != null) {
sb.append("densePart: ").append(densePart.toString()).append(" ");
}
sb.append("sparsePart: ").append(sparsePart.toString()).append("]");
return sb.toString();
}
public SemiSparseMutableIntSet removeAll(SemiSparseMutableIntSet B) {
if (B == null) {
throw new IllegalArgumentException("B null");
}
if (densePart == null) {
if (B.densePart == null) {
sparsePart = MutableSparseIntSet.diff(sparsePart, B.sparsePart);
} else {
MutableSparseIntSet C = MutableSparseIntSet.diff(sparsePart, B.sparsePart);
for (IntIterator bits = sparsePart.intIterator(); bits.hasNext();) {
int bit = bits.next();
if (B.densePart.get(bit)) {
C.remove(bit);
}
}
sparsePart = C;
}
} else {
if (B.densePart == null) {
for (IntIterator bits = B.sparsePart.intIterator(); bits.hasNext();) {
densePart.clear(bits.next());
}
sparsePart = MutableSparseIntSet.diff(sparsePart, B.sparsePart);
} else {
densePart.andNot(B.densePart);
for (IntIterator bits = B.sparsePart.intIterator(); bits.hasNext();) {
densePart.clear(bits.next());
}
MutableSparseIntSet C = MutableSparseIntSet.diff(sparsePart, B.sparsePart);
for (IntIterator bits = sparsePart.intIterator(); bits.hasNext();) {
int bit = bits.next();
if (B.densePart.get(bit)) {
C.remove(bit);
}
}
sparsePart = C;
}
}
return this;
}
public static SemiSparseMutableIntSet diff(SemiSparseMutableIntSet A, SemiSparseMutableIntSet B) {
if (A == null) {
throw new IllegalArgumentException("A is null");
}
if (B == null) {
throw new IllegalArgumentException("B is null");
}
if (A.densePart == null) {
if (B.densePart == null) {
return new SemiSparseMutableIntSet(MutableSparseIntSet.diff(A.sparsePart, B.sparsePart));
} else {
MutableSparseIntSet C = MutableSparseIntSet.diff(A.sparsePart, B.sparsePart);
for (IntIterator bits = A.sparsePart.intIterator(); bits.hasNext();) {
int bit = bits.next();
if (B.densePart.get(bit)) {
C.remove(bit);
}
}
return new SemiSparseMutableIntSet(C);
}
} else {
if (B.densePart == null) {
OffsetBitVector newDensePart = new OffsetBitVector(A.densePart);
for (IntIterator bits = B.sparsePart.intIterator(); bits.hasNext();) {
newDensePart.clear(bits.next());
}
return new SemiSparseMutableIntSet(MutableSparseIntSet.diff(A.sparsePart, B.sparsePart), newDensePart);
} else {
OffsetBitVector newDensePart = new OffsetBitVector(A.densePart);
newDensePart.andNot(B.densePart);
for (IntIterator bits = B.sparsePart.intIterator(); bits.hasNext();) {
newDensePart.clear(bits.next());
}
MutableSparseIntSet C = MutableSparseIntSet.diff(A.sparsePart, B.sparsePart);
for (IntIterator bits = A.sparsePart.intIterator(); bits.hasNext();) {
int bit = bits.next();
if (B.densePart.get(bit)) {
C.remove(bit);
}
}
return new SemiSparseMutableIntSet(C, newDensePart);
}
}
}
}