544 lines
12 KiB
Java
544 lines
12 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.debug.Assertions;
|
|
|
|
/**
|
|
* A sparse ordered, mutable duplicate-free, fully-encapsulated set of integers.
|
|
* Instances are not canonical, except for EMPTY.
|
|
*
|
|
* This implementation will be inefficient if these sets get large.
|
|
*
|
|
* TODO: even for small sets, we probably want to work on this to reduce the
|
|
* allocation activity.
|
|
*/
|
|
public class MutableSparseIntSet extends SparseIntSet implements MutableIntSet {
|
|
|
|
private static final long serialVersionUID = 1479453398189400698L;
|
|
|
|
/**
|
|
* If forced to grow the backing array .. then by how much
|
|
*/
|
|
private final static float EXPANSION_FACTOR = 1.5f;
|
|
|
|
/**
|
|
* Default initial size for a backing array with one element
|
|
*/
|
|
private final static int INITIAL_NONEMPTY_SIZE = 2;
|
|
|
|
/**
|
|
* a debug flag, used to trap when a set gets large
|
|
*/
|
|
private final static boolean DEBUG_LARGE = false;
|
|
|
|
private final static int TRAP_SIZE = 1000;
|
|
|
|
protected MutableSparseIntSet(IntSet set) {
|
|
super();
|
|
copySet(set);
|
|
}
|
|
|
|
protected MutableSparseIntSet(int[] backingStore) {
|
|
super(backingStore);
|
|
}
|
|
|
|
/**
|
|
* Create an empty set with a non-zero capacity
|
|
*/
|
|
private MutableSparseIntSet(int initialCapacity)
|
|
throws IllegalArgumentException {
|
|
super(new int[initialCapacity]);
|
|
size = 0;
|
|
if (initialCapacity <= 0) {
|
|
throw new IllegalArgumentException(
|
|
"initialCapacity must be positive");
|
|
}
|
|
}
|
|
|
|
protected MutableSparseIntSet() {
|
|
super();
|
|
}
|
|
|
|
/*
|
|
* @see com.ibm.wala.util.intset.MutableIntSet#clear()
|
|
*/
|
|
@Override
|
|
public void clear() {
|
|
size = 0;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
@Override
|
|
public boolean remove(int value) {
|
|
if (elements != null) {
|
|
int remove;
|
|
for (remove = 0; remove < size; remove++) {
|
|
if (elements[remove] >= value) {
|
|
break;
|
|
}
|
|
}
|
|
if (remove == size) {
|
|
return false;
|
|
}
|
|
if (elements[remove] == value) {
|
|
if (size == 1) {
|
|
elements = null;
|
|
size = 0;
|
|
} else {
|
|
if (remove < size) {
|
|
System.arraycopy(elements, remove + 1, elements,
|
|
remove, size - remove - 1);
|
|
}
|
|
size--;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
public int getInitialNonEmptySize() {
|
|
return INITIAL_NONEMPTY_SIZE;
|
|
}
|
|
|
|
public float getExpansionFactor() {
|
|
return EXPANSION_FACTOR;
|
|
}
|
|
|
|
/**
|
|
* @param value
|
|
* @return true iff this value changes
|
|
*/
|
|
@Override
|
|
@SuppressWarnings("unused")
|
|
public boolean add(int value) {
|
|
if (elements == null) {
|
|
elements = new int[getInitialNonEmptySize()];
|
|
size = 1;
|
|
elements[0] = value;
|
|
return true;
|
|
} else {
|
|
int insert;
|
|
if (size == 0 || value > max()) {
|
|
insert = size;
|
|
} else if (value == max()) {
|
|
return false;
|
|
} else {
|
|
for (insert = 0; insert < size; insert++) {
|
|
if (elements[insert] >= value) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (insert < size && elements[insert] == value) {
|
|
return false;
|
|
}
|
|
if (size < elements.length - 1) {
|
|
// there's space in the backing elements array. Use it.
|
|
if (size != insert) {
|
|
System.arraycopy(elements, insert, elements, insert + 1,
|
|
size - insert);
|
|
}
|
|
size++;
|
|
elements[insert] = value;
|
|
if (DEBUG_LARGE && size() > TRAP_SIZE) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
return true;
|
|
} else {
|
|
// no space left. expand the backing array.
|
|
float newExtent = elements.length * getExpansionFactor() + 1;
|
|
int[] tmp = new int[(int) newExtent];
|
|
System.arraycopy(elements, 0, tmp, 0, insert);
|
|
if (size != insert) {
|
|
System.arraycopy(elements, insert, tmp, insert + 1, size
|
|
- insert);
|
|
}
|
|
tmp[insert] = value;
|
|
size++;
|
|
elements = tmp;
|
|
if (DEBUG_LARGE && size() > TRAP_SIZE) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @throws IllegalArgumentException
|
|
* if that == null
|
|
*/
|
|
@Override
|
|
@SuppressWarnings("unused")
|
|
public void copySet(IntSet that) throws IllegalArgumentException {
|
|
if (that == null) {
|
|
throw new IllegalArgumentException("that == null");
|
|
}
|
|
if (that instanceof SparseIntSet) {
|
|
SparseIntSet set = (SparseIntSet) that;
|
|
if (set.elements != null) {
|
|
// SJF: clone is performance problem. don't use it.
|
|
// elements = set.elements.clone();
|
|
elements = new int[set.elements.length];
|
|
for (int i = 0; i < set.size; i++) {
|
|
elements[i] = set.elements[i];
|
|
}
|
|
size = set.size;
|
|
} else {
|
|
elements = null;
|
|
size = 0;
|
|
}
|
|
} else {
|
|
elements = new int[that.size()];
|
|
size = that.size();
|
|
that.foreach(new IntSetAction() {
|
|
private int index = 0;
|
|
|
|
@Override
|
|
public void act(int i) {
|
|
elements[index++] = i;
|
|
}
|
|
});
|
|
}
|
|
if (DEBUG_LARGE && size() > TRAP_SIZE) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void intersectWith(IntSet set) {
|
|
if (set == null) {
|
|
throw new IllegalArgumentException("null set");
|
|
}
|
|
if (set instanceof SparseIntSet) {
|
|
intersectWith((SparseIntSet) set);
|
|
} else {
|
|
int j = 0;
|
|
for (int i = 0; i < size; i++)
|
|
if (set.contains(elements[i])) {
|
|
elements[j++] = elements[i];
|
|
}
|
|
size = j;
|
|
}
|
|
}
|
|
|
|
public void intersectWith(SparseIntSet set) {
|
|
if (set == null) {
|
|
throw new IllegalArgumentException("null set");
|
|
}
|
|
SparseIntSet that = set;
|
|
if (this.isEmpty()) {
|
|
return;
|
|
} else if (that.isEmpty()) {
|
|
elements = null;
|
|
size = 0;
|
|
return;
|
|
} else if (this.equals(that)) {
|
|
return;
|
|
}
|
|
|
|
// some simple optimizations
|
|
if (size == 1) {
|
|
if (that.contains(elements[0])) {
|
|
return;
|
|
} else {
|
|
elements = null;
|
|
size = 0;
|
|
return;
|
|
}
|
|
}
|
|
if (that.size == 1) {
|
|
if (contains(that.elements[0])) {
|
|
if (size > getInitialNonEmptySize()) {
|
|
elements = new int[getInitialNonEmptySize()];
|
|
}
|
|
size = 1;
|
|
elements[0] = that.elements[0];
|
|
return;
|
|
} else {
|
|
elements = null;
|
|
size = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
int[] ar = this.elements;
|
|
int ai = 0;
|
|
int al = size;
|
|
int[] br = that.elements;
|
|
int bi = 0;
|
|
int bl = that.size;
|
|
int[] cr = null; // allocate on demand
|
|
int ci = 0;
|
|
|
|
while (ai < al && bi < bl) {
|
|
int cmp = (ar[ai] - br[bi]);
|
|
|
|
// (accept element only on a match)
|
|
if (cmp > 0) { // a greater
|
|
bi++;
|
|
} else if (cmp < 0) { // b greater
|
|
ai++;
|
|
} else {
|
|
if (cr == null) {
|
|
cr = new int[al]; // allocate enough (i.e. too much)
|
|
}
|
|
cr[ci++] = ar[ai];
|
|
ai++;
|
|
bi++;
|
|
}
|
|
}
|
|
|
|
// now compact cr to 'just enough'
|
|
size = ci;
|
|
elements = cr;
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Add all elements from another int set.
|
|
*
|
|
* @return true iff this set changes
|
|
* @throws IllegalArgumentException
|
|
* if set == null
|
|
*/
|
|
@Override
|
|
@SuppressWarnings("unused")
|
|
public boolean addAll(IntSet set) throws IllegalArgumentException {
|
|
if (set == null) {
|
|
throw new IllegalArgumentException("set == null");
|
|
}
|
|
if (set instanceof SparseIntSet) {
|
|
return addAll((SparseIntSet) set);
|
|
} else {
|
|
int oldSize = size;
|
|
set.foreach(new IntSetAction() {
|
|
@Override
|
|
public void act(int i) {
|
|
if (!contains(i))
|
|
add(i);
|
|
}
|
|
});
|
|
|
|
if (DEBUG_LARGE && size() > TRAP_SIZE) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
return size != oldSize;
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add all elements from another int set.
|
|
*
|
|
* @param that
|
|
* @return true iff this set changes
|
|
*/
|
|
public boolean addAll(SparseIntSet that) {
|
|
if (that == null) {
|
|
throw new IllegalArgumentException("null that");
|
|
}
|
|
if (this.isEmpty()) {
|
|
copySet(that);
|
|
return !that.isEmpty();
|
|
} else if (that.isEmpty()) {
|
|
return false;
|
|
} else if (this.equals(that)) {
|
|
return false;
|
|
}
|
|
|
|
// common-case optimization
|
|
if (that.size == 1) {
|
|
boolean result = add(that.elements[0]);
|
|
return result;
|
|
}
|
|
|
|
int[] br = that.elements;
|
|
int bl = that.size();
|
|
|
|
return addAll(br, bl);
|
|
}
|
|
|
|
@SuppressWarnings("unused")
|
|
private boolean addAll(int[] that, int thatSize) {
|
|
int[] ar = this.elements;
|
|
int ai = 0;
|
|
final int al = size();
|
|
int bi = 0;
|
|
|
|
// invariant: assume cr has same value as ar until cr is allocated.
|
|
// we allocate cr lazily when we discover cr != ar.
|
|
int[] cr = null;
|
|
int ci = 0;
|
|
|
|
while (ai < al && bi < thatSize) {
|
|
int cmp = (ar[ai] - that[bi]);
|
|
|
|
// (always accept element)
|
|
if (cmp > 0) { // a greater
|
|
if (cr == null) {
|
|
cr = new int[al + thatSize];
|
|
System.arraycopy(ar, 0, cr, 0, ci);
|
|
}
|
|
cr[ci++] = that[bi++];
|
|
} else if (cmp < 0) { // b greater
|
|
if (cr != null) {
|
|
cr[ci] = ar[ai];
|
|
}
|
|
ci++;
|
|
ai++;
|
|
} else {
|
|
if (cr != null) {
|
|
cr[ci] = ar[ai]; // (same: use a)
|
|
}
|
|
ci++;
|
|
ai++;
|
|
bi++;
|
|
}
|
|
}
|
|
|
|
// append tail if any (at most one of a or b has tail)
|
|
if (ai < al) {
|
|
int tail = al - ai;
|
|
if (cr != null) {
|
|
System.arraycopy(ar, ai, cr, ci, tail);
|
|
}
|
|
ci += tail;
|
|
} else if (bi < thatSize) {
|
|
int tail = thatSize - bi;
|
|
if (cr == null) {
|
|
cr = new int[al + thatSize];
|
|
System.arraycopy(ar, 0, cr, 0, ci);
|
|
}
|
|
System.arraycopy(that, bi, cr, ci, tail);
|
|
ci += tail;
|
|
}
|
|
|
|
assert ci > 0;
|
|
|
|
elements = (cr == null) ? ar : cr;
|
|
size = ci;
|
|
if (DEBUG_LARGE && size() > TRAP_SIZE) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
return (al != size);
|
|
}
|
|
|
|
public void removeAll(BitVectorIntSet v) {
|
|
if (v == null) {
|
|
throw new IllegalArgumentException("null v");
|
|
}
|
|
int ai = 0;
|
|
for (int i = 0; i < size; i++) {
|
|
if (!v.contains(elements[i])) {
|
|
elements[ai++] = elements[i];
|
|
}
|
|
}
|
|
size = ai;
|
|
}
|
|
|
|
public <T extends BitVectorBase<T>> void removeAll(T v) {
|
|
if (v == null) {
|
|
throw new IllegalArgumentException("null v");
|
|
}
|
|
int ai = 0;
|
|
for (int i = 0; i < size; i++) {
|
|
if (!v.get(elements[i])) {
|
|
elements[ai++] = elements[i];
|
|
}
|
|
}
|
|
size = ai;
|
|
}
|
|
|
|
/**
|
|
* TODO optimize
|
|
*
|
|
* @param set
|
|
* @throws IllegalArgumentException
|
|
* if set is null
|
|
*/
|
|
public void removeAll(MutableSparseIntSet set) {
|
|
if (set == null) {
|
|
throw new IllegalArgumentException("set is null");
|
|
}
|
|
for (IntIterator it = set.intIterator(); it.hasNext();) {
|
|
remove(it.next());
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @see
|
|
* com.ibm.wala.util.intset.MutableIntSet#addAllInIntersection(com.ibm.wala
|
|
* .util.intset.IntSet, com.ibm.wala.util.intset.IntSet)
|
|
*/
|
|
@Override
|
|
public boolean addAllInIntersection(IntSet other, IntSet filter) {
|
|
if (other == null) {
|
|
throw new IllegalArgumentException("other is null");
|
|
}
|
|
if (filter == null) {
|
|
throw new IllegalArgumentException("invalid filter");
|
|
}
|
|
// a hack. TODO: better algorithm
|
|
if (other.size() < 5) {
|
|
boolean result = false;
|
|
for (IntIterator it = other.intIterator(); it.hasNext();) {
|
|
int i = it.next();
|
|
if (filter.contains(i)) {
|
|
result |= add(i);
|
|
}
|
|
}
|
|
return result;
|
|
} else if (filter.size() < 5) {
|
|
boolean result = false;
|
|
for (IntIterator it = filter.intIterator(); it.hasNext();) {
|
|
int i = it.next();
|
|
if (other.contains(i)) {
|
|
result |= add(i);
|
|
}
|
|
}
|
|
return result;
|
|
} else {
|
|
BitVectorIntSet o = new BitVectorIntSet(other);
|
|
o.intersectWith(filter);
|
|
return addAll(o);
|
|
}
|
|
}
|
|
|
|
public static MutableSparseIntSet diff(MutableSparseIntSet A,
|
|
MutableSparseIntSet B) {
|
|
return new MutableSparseIntSet(diffInternal(A, B));
|
|
}
|
|
|
|
public static MutableSparseIntSet make(IntSet set) {
|
|
return new MutableSparseIntSet(set);
|
|
}
|
|
|
|
public static MutableSparseIntSet makeEmpty() {
|
|
return new MutableSparseIntSet();
|
|
}
|
|
|
|
public static MutableSparseIntSet createMutableSparseIntSet(
|
|
int initialCapacity) throws IllegalArgumentException {
|
|
if (initialCapacity < 0) {
|
|
throw new IllegalArgumentException("illegal initialCapacity: "
|
|
+ initialCapacity);
|
|
}
|
|
return new MutableSparseIntSet(initialCapacity);
|
|
}
|
|
|
|
}
|