WALA/com.ibm.wala.core/src/com/ibm/wala/util/intset/MutableSparseLongSet.java

369 lines
8.9 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 longs.
* 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.
*
* @author Alan Donovan
* @author Stephen Fink
*/
public final class MutableSparseLongSet extends SparseLongSet implements MutableLongSet {
/**
* 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;
/**
* @param set
*/
public MutableSparseLongSet(LongSet set) {
super();
copySet(set);
}
public MutableSparseLongSet(long[] backingStore) {
super(backingStore);
}
/**
* Create an empty set with a non-zero capacity
*
* @param initialCapacity
*/
public MutableSparseLongSet(int initialCapacity) {
super(new long[initialCapacity]);
size = 0;
if (Assertions.verifyAssertions) {
Assertions._assert(initialCapacity > 0);
}
}
public MutableSparseLongSet() {
super();
}
/**
*/
public void remove(long value) {
if (elements != null) {
int remove;
for (remove = 0; remove < size; remove++) {
if (elements[remove] >= value) {
break;
}
}
if (remove == size) {
return;
}
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--;
}
}
}
}
/**
* @param value
* @return true iff this value changes
*/
public boolean add(long value) {
if (Assertions.verifyAssertions) {
Assertions._assert(value >= 0);
}
if (elements == null) {
elements = new long[INITIAL_NONEMPTY_SIZE];
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;
return true;
} else {
// no space left. expand the backing array.
float newExtent = elements.length * EXPANSION_FACTOR + 1;
long[] tmp = new long[(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;
return true;
}
}
}
/**
*/
public void copySet(LongSet that) {
if (that instanceof SparseLongSet) {
SparseLongSet set = (SparseLongSet) that;
if (set.elements != null) {
elements = (long[]) set.elements.clone();
size = set.size;
} else {
elements = null;
size = 0;
}
} else {
Assertions.UNREACHABLE();
}
}
public void intersectWith(LongSet set) {
if (set instanceof SparseLongSet) {
intersectWith((SparseLongSet) 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(SparseLongSet set) {
SparseLongSet that = set;
if (Assertions.verifyAssertions) {
Assertions._assert(that != null);
}
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 > INITIAL_NONEMPTY_SIZE) {
elements = new long[INITIAL_NONEMPTY_SIZE];
}
size = 1;
elements[0] = that.elements[0];
return;
} else {
elements = null;
size = 0;
return;
}
}
long[] ar = this.elements;
int ai = 0;
int al = size;
long[] br = that.elements;
int bi = 0;
int bl = that.size;
long[] cr = null; // allocate on demand
int ci = 0;
while (ai < al && bi < bl) {
long 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 long[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
*/
public boolean addAll(LongSet set) {
if (set instanceof SparseLongSet) {
return addAll((SparseLongSet) set);
} else {
Assertions.UNREACHABLE();
return false;
}
}
/**
* Add all elements from another int set.
*
* @param that
* @return true iff this set changes
*/
public boolean addAll(SparseLongSet 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;
}
long[] br = that.elements;
int bl = that.size();
return addAll(br, bl);
}
private boolean addAll(long[] that, int thatSize) {
long[] 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.
long[] cr = null;
int ci = 0;
while (ai < al && bi < thatSize) {
long cmp = (ar[ai] - that[bi]);
// (always accept element)
if (cmp > 0) { // a greater
if (cr == null) {
cr = new long[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 long[al + thatSize];
System.arraycopy(ar, 0, cr, 0, ci);
}
System.arraycopy(that, bi, cr, ci, tail);
ci += tail;
}
if (Assertions.verifyAssertions) {
Assertions._assert(ci > 0);
}
elements = (cr == null) ? ar : cr;
size = ci;
return (al != size);
}
/**
* TODO optimize
*
* @param set
*/
public void removeAll(MutableSparseLongSet set) {
Assertions.UNREACHABLE();
}
}