com.ibm.wala.util.intset.BitVectorIntSet Maven / Gradle / Ivy
Show all versions of com.ibm.wala.util Show documentation
/*
* 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.debug.Assertions;
import com.ibm.wala.util.debug.UnimplementedError;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
/**
* A {@link BitVector} implementation of {@link MutableIntSet}.
*
* Note that this is NOT a value with regard to hashCode and equals.
*/
public final class BitVectorIntSet implements MutableIntSet {
private static final long serialVersionUID = 7477243071826223843L;
// population count of -1 means needs to be computed again.
private int populationCount = 0;
private static final int UNDEFINED = -1;
private BitVector bitVector = new BitVector(0);
public BitVectorIntSet() {}
public BitVectorIntSet(BitVector v) {
if (v == null) {
throw new IllegalArgumentException("null v");
}
bitVector.or(v);
populationCount = UNDEFINED;
}
public BitVectorIntSet(@Nullable IntSet S) throws IllegalArgumentException {
if (S == null) {
throw new IllegalArgumentException("S == null");
}
copySet(S);
}
@Override
public void clear() {
bitVector.clearAll();
populationCount = 0;
}
@Override
public void copySet(IntSet set) throws IllegalArgumentException {
if (set == null) {
throw new IllegalArgumentException("set == null");
}
if (set instanceof BitVectorIntSet) {
BitVectorIntSet S = (BitVectorIntSet) set;
bitVector = new BitVector(S.bitVector);
populationCount = S.populationCount;
} else if (set instanceof MutableSharedBitVectorIntSet) {
BitVectorIntSet S = ((MutableSharedBitVectorIntSet) set).makeDenseCopy();
bitVector = new BitVector(S.bitVector);
populationCount = S.populationCount;
} else if (set instanceof SparseIntSet) {
SparseIntSet s = (SparseIntSet) set;
if (s.size == 0) {
populationCount = 0;
bitVector = new BitVector(0);
} else {
bitVector = new BitVector(s.max());
populationCount = s.size;
for (int i = 0; i < s.size; i++) {
bitVector.set(s.elements[i]);
}
}
} else if (set instanceof BimodalMutableIntSet) {
IntSet backing = ((BimodalMutableIntSet) set).getBackingStore();
copySet(backing);
} else {
bitVector.clearAll();
populationCount = set.size();
for (IntIterator it = set.intIterator(); it.hasNext(); ) {
bitVector.set(it.next());
}
}
}
@Override
public boolean addAll(@Nullable IntSet set) {
if (set instanceof BitVectorIntSet) {
BitVector B = ((BitVectorIntSet) set).bitVector;
int delta = bitVector.orWithDelta(B);
populationCount += delta;
populationCount = (populationCount == (delta + UNDEFINED)) ? UNDEFINED : populationCount;
return (delta != 0);
} else {
BitVectorIntSet other = new BitVectorIntSet(set);
return addAll(other);
}
}
/**
* this version of add all will likely be faster if the client doesn't care about the change or
* the population count.
*
* @throws IllegalArgumentException if set == null
*/
public void addAllOblivious(IntSet set) throws IllegalArgumentException {
if (set == null) {
throw new IllegalArgumentException("set == null");
}
if (set instanceof BitVectorIntSet) {
BitVector B = ((BitVectorIntSet) set).bitVector;
bitVector.or(B);
populationCount = UNDEFINED;
} else {
BitVectorIntSet other = new BitVectorIntSet(set);
addAllOblivious(other);
}
}
@Override
public boolean add(int i) {
if (bitVector.get(i)) {
return false;
} else {
bitVector.set(i);
populationCount++;
populationCount = (populationCount == (UNDEFINED + 1)) ? UNDEFINED : populationCount;
return true;
}
}
@Override
public boolean remove(int i) {
if (contains(i)) {
populationCount--;
populationCount = (populationCount == UNDEFINED - 1) ? UNDEFINED : populationCount;
bitVector.clear(i);
return true;
} else {
return false;
}
}
@Override
public void intersectWith(IntSet set) {
if (!(set instanceof BitVectorIntSet)) {
set = new BitVectorIntSet(set);
}
BitVector B = ((BitVectorIntSet) set).bitVector;
bitVector.and(B);
populationCount = UNDEFINED;
}
/**
* @see com.ibm.wala.util.intset.IntSet#intersection(com.ibm.wala.util.intset.IntSet)
*/
@Override
public BitVectorIntSet intersection(IntSet that) {
BitVectorIntSet newbie = new BitVectorIntSet();
newbie.copySet(this);
newbie.intersectWith(that);
return newbie;
}
/**
* @see com.ibm.wala.util.intset.IntSet#union(com.ibm.wala.util.intset.IntSet)
*/
@Override
public IntSet union(IntSet that) {
BitVectorIntSet temp = new BitVectorIntSet();
temp.addAll(this);
temp.addAll(that);
return temp;
}
/**
* @see com.ibm.wala.util.intset.IntSet#isEmpty()
*/
@Override
public boolean isEmpty() {
return size() == 0;
}
/**
* @see com.ibm.wala.util.intset.IntSet#size()
*/
@Override
public int size() {
populationCount =
(populationCount == UNDEFINED) ? bitVector.populationCount() : populationCount;
return populationCount;
}
/** Use with extreme care; doesn't detect ConcurrentModificationExceptions */
@Override
public IntIterator intIterator() {
populationCount =
(populationCount == UNDEFINED) ? bitVector.populationCount() : populationCount;
return new IntIterator() {
int count = 0;
int last = 0;
@Override
public boolean hasNext() {
assert populationCount == bitVector.populationCount();
return count < populationCount;
}
@Override
public int next() {
count++;
last = nextSetBit(last) + 1;
return last - 1;
}
};
}
/**
* @see com.ibm.wala.util.intset.IntSet#foreach(com.ibm.wala.util.intset.IntSetAction)
*/
@Override
public void foreach(IntSetAction action) {
if (action == null) {
throw new IllegalArgumentException("null action");
}
int nextBit = bitVector.nextSetBit(0);
populationCount =
(populationCount == UNDEFINED) ? bitVector.populationCount() : populationCount;
for (int i = 0; i < populationCount; i++) {
action.act(nextBit);
nextBit = bitVector.nextSetBit(nextBit + 1);
}
}
public SparseIntSet makeSparseCopy() {
populationCount =
(populationCount == UNDEFINED) ? bitVector.populationCount() : populationCount;
int[] elements = new int[populationCount];
int i = 0;
int nextBit = -1;
while (i < populationCount) elements[i++] = nextBit = bitVector.nextSetBit(nextBit + 1);
return new SparseIntSet(elements);
}
/**
* @see com.ibm.wala.util.intset.IntSet#foreach(com.ibm.wala.util.intset.IntSetAction)
*/
@Override
public void foreachExcluding(IntSet X, IntSetAction action) {
if (X instanceof BitVectorIntSet) {
fastForeachExcluding((BitVectorIntSet) X, action);
} else {
slowForeachExcluding(X, action);
}
}
private void slowForeachExcluding(IntSet X, IntSetAction action) {
populationCount =
(populationCount == UNDEFINED) ? bitVector.populationCount() : populationCount;
for (int i = 0, count = 0; count < populationCount; i++) {
if (contains(i)) {
if (!X.contains(i)) {
action.act(i);
}
count++;
}
}
}
/** internal optimized form */
private void fastForeachExcluding(BitVectorIntSet X, IntSetAction action) {
int[] bits = bitVector.bits;
int[] xbits = X.bitVector.bits;
int w = 0;
while (w < xbits.length && w < bits.length) {
int b = bits[w] & ~xbits[w];
actOnWord(action, w << 5, b);
w++;
}
while (w < bits.length) {
actOnWord(action, w << 5, bits[w]);
w++;
}
}
private static void actOnWord(IntSetAction action, int startingIndex, int word) {
if (word != 0) {
if ((word & 0x1) != 0) {
action.act(startingIndex);
}
for (int i = 0; i < 31; i++) {
startingIndex++;
word >>>= 1;
if ((word & 0x1) != 0) {
action.act(startingIndex);
}
}
}
}
@Override
public boolean contains(int i) {
if (i < 0) {
throw new IllegalArgumentException("invalid i: " + i);
}
return bitVector.get(i);
}
@Override
public int max() {
return bitVector.max();
}
@Override
public String toString() {
return bitVector.toString();
}
/**
* @return min j >= n s.t get(j)
*/
public int nextSetBit(int n) {
return bitVector.nextSetBit(n);
}
/**
* @see com.ibm.wala.util.intset.IntSet#sameValue(com.ibm.wala.util.intset.IntSet)
*/
@Override
public boolean sameValue(@Nullable IntSet that)
throws IllegalArgumentException, UnimplementedError {
if (that == null) {
throw new IllegalArgumentException("that == null");
}
if (that instanceof BitVectorIntSet) {
BitVectorIntSet b = (BitVectorIntSet) that;
return bitVector.sameBits(b.bitVector);
} else if (that instanceof BimodalMutableIntSet) {
return that.sameValue(this);
} else if (that instanceof SparseIntSet) {
return sameValueInternal((SparseIntSet) that);
} else if (that instanceof MutableSharedBitVectorIntSet) {
return sameValue(((MutableSharedBitVectorIntSet) that).makeDenseCopy());
} else {
Assertions.UNREACHABLE("unexpected argument type " + that.getClass());
return false;
}
}
/** */
private boolean sameValueInternal(SparseIntSet that) {
populationCount =
(populationCount == UNDEFINED) ? bitVector.populationCount() : populationCount;
if (populationCount != that.size()) {
return false;
}
for (int i = 0; i < that.size(); i++) {
int val = that.elementAt(i);
if (!bitVector.contains(val)) {
return false;
}
}
return true;
}
/**
* @see com.ibm.wala.util.intset.IntSet#isSubset(com.ibm.wala.util.intset.IntSet)
*/
@NullUnmarked
@Override
public boolean isSubset(@Nullable IntSet that) {
if (that instanceof BitVectorIntSet) {
return bitVector.isSubset(((BitVectorIntSet) that).bitVector);
} else if (that instanceof SparseIntSet) {
return isSubsetInternal((SparseIntSet) that);
} else {
// really slow. optimize as needed.
for (IntIterator it = intIterator(); it.hasNext(); ) {
int x = it.next();
if (!that.contains(x)) {
return false;
}
}
return true;
}
}
private boolean isSubsetInternal(SparseIntSet set) {
return toSparseIntSet().isSubset(set);
}
public BitVector getBitVector() {
return bitVector;
}
/** TODO: optimize */
public SparseIntSet toSparseIntSet() {
MutableSparseIntSet result = MutableSparseIntSet.makeEmpty();
for (IntIterator it = intIterator(); it.hasNext(); ) {
result.add(it.next());
}
return result;
}
/**
* @throws IllegalArgumentException if set is null
*/
public boolean removeAll(BitVectorIntSet set) {
if (set == null) {
throw new IllegalArgumentException("set is null");
}
int oldSize = size();
bitVector.andNot(set.bitVector);
populationCount = UNDEFINED;
return oldSize > size();
}
/**
* @see com.ibm.wala.util.intset.IntSet#containsAny(com.ibm.wala.util.intset.IntSet)
*/
@Override
public boolean containsAny(IntSet set) throws IllegalArgumentException {
if (set == null) {
throw new IllegalArgumentException("set == null");
}
if (set instanceof BitVectorIntSet) {
BitVectorIntSet b = (BitVectorIntSet) set;
return !bitVector.intersectionEmpty(b.bitVector);
} else {
// TODO: optimize
for (IntIterator it = set.intIterator(); it.hasNext(); ) {
if (contains(it.next())) {
return true;
}
}
return false;
}
}
@Override
public boolean addAllInIntersection(IntSet other, IntSet filter) throws IllegalArgumentException {
if (other == null) {
throw new IllegalArgumentException("other == null");
}
BitVectorIntSet o = new BitVectorIntSet(other);
o.intersectWith(filter);
return addAll(o);
}
public boolean containsAll(BitVectorIntSet other) {
if (other == null) {
throw new IllegalArgumentException("other is null");
}
return other.isSubset(this);
}
}