com.ibm.wala.util.intset.SparseLongSet 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 java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.TreeSet;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
/** A sparse ordered, duplicate-free, fully-encapsulated set of longs; not necessary mutable */
public class SparseLongSet implements LongSet {
private static final int SINGLETON_CACHE_SIZE = 0;
private static final SparseLongSet[] singletonCache = new SparseLongSet[SINGLETON_CACHE_SIZE];
static {
for (int i = 0; i < SINGLETON_CACHE_SIZE; i++) {
singletonCache[i] = new SparseLongSet(new long[] {i});
}
}
// TODO: I'm not thrilled with exposing these to subclasses, but
// it seems expedient for now.
/** The backing store of int arrays */
@SuppressWarnings("NullAway.Init")
protected long[] elements;
/** The number of entries in the backing store that are valid. */
protected int size = 0;
/////////////////////////////////////////////////////////////////////////////
//
// Constructors & Factories
//
/////////////////////////////////////////////////////////////////////////////
protected SparseLongSet(int size) {
elements = new long[size];
this.size = size;
}
/**
* Subclasses should use this with extreme care. Do not allow the backing array to escape
* elsewhere.
*/
protected SparseLongSet(long[] backingArray) {
if (backingArray == null) {
throw new IllegalArgumentException("backingArray is null");
}
elements = backingArray;
this.size = backingArray.length;
}
/** Subclasses should use this with extreme care. */
@NullUnmarked
public SparseLongSet() {
elements = null;
this.size = 0;
}
protected SparseLongSet(SparseLongSet S) {
cloneState(S);
}
private void cloneState(SparseLongSet S) {
elements = S.elements.clone();
this.size = S.size;
}
public SparseLongSet(IntSet S) throws IllegalArgumentException {
if (S == null) {
throw new IllegalArgumentException("S == null");
}
if (S instanceof SparseLongSet) {
cloneState((SparseLongSet) S);
} else {
elements = new long[S.size()];
size = S.size();
S.foreach(
new IntSetAction() {
private int index = 0;
@Override
public void act(int i) {
elements[index++] = i;
}
});
}
}
/**
* Does this set contain value x?
*
* @see com.ibm.wala.util.intset.IntSet#contains(int)
*/
@Override
public final boolean contains(long x) {
if (elements == null) {
return false;
}
return LongSetUtil.binarySearch(elements, x, 0, size - 1) >= 0;
}
/**
* @return index i s.t. elements[i] == x, or -1 if not found.
*/
public final int getIndex(long x) {
if (elements == null) {
return -1;
}
return LongSetUtil.binarySearch(elements, x, 0, size - 1);
}
@Override
public final int size() {
return size;
}
@Override
public final boolean isEmpty() {
return size == 0;
}
public final long elementAt(int idx) throws NoSuchElementException {
if (idx < 0 || idx >= size) {
throw new NoSuchElementException();
}
return elements[idx];
}
private boolean sameValueInternal(SparseLongSet that) {
if (size != that.size) {
return false;
} else {
for (int i = 0; i < size; i++) {
if (elements[i] != that.elements[i]) {
return false;
}
}
return true;
}
}
@Override
public boolean sameValue(LongSet that) throws IllegalArgumentException, UnimplementedError {
if (that == null) {
throw new IllegalArgumentException("that == null");
}
if (that instanceof SparseLongSet) {
return sameValueInternal((SparseLongSet) that);
} else {
Assertions.UNREACHABLE(that.getClass().toString());
return false;
}
}
/**
* @return true iff {@code this} is a subset of {@code that}.
* Faster than: {@code this.diff(that) == EMPTY}.
*/
private boolean isSubsetInternal(SparseLongSet that) {
if (elements == null) return true;
if (that.elements == null) return false;
if (this.equals(that)) return true;
if (this.sameValue(that)) {
return true;
}
long[] ar = this.elements;
int ai = 0;
int al = size;
long[] br = that.elements;
int bi = 0;
int bl = that.size;
while (ai < al && bi < bl) {
long cmp = (ar[ai] - br[bi]);
// (fail when element only found in 'a')
if (cmp > 0) { // a greater
bi++;
} else if (cmp < 0) { // b greater
return false;
} else {
ai++;
bi++;
}
}
if (bi == bl && ai < al) {
// we ran off the end of b without finding an a
return false;
}
return true;
}
/**
* Compute the asymmetric difference of two sets, a \ b.
*
* @throws IllegalArgumentException if A is null
*/
public static SparseLongSet diff(SparseLongSet A, SparseLongSet B) {
if (A == null) {
throw new IllegalArgumentException("A is null");
}
if (B == null) {
throw new IllegalArgumentException("B is null");
}
if (A.isEmpty()) {
return new SparseLongSet(0);
} else if (B.isEmpty()) {
return new SparseLongSet(A);
} else if (A.equals(B)) {
return new SparseLongSet(0);
} else if (A.sameValue(B)) {
return new SparseLongSet(0);
}
long[] ar = A.elements;
int ai = 0;
int al = A.size;
long[] br = B.elements;
int bi = 0;
int bl = B.size;
long[] cr = new long[al];
// allocate enough (i.e. too much)
int ci = 0;
while (ai < al && bi < bl) {
long cmp = (ar[ai] - br[bi]);
// (accept element when only found in 'a')
if (cmp > 0) { // a greater
bi++;
} else if (cmp < 0) { // b greater
cr[ci++] = ar[ai];
ai++;
} else {
ai++;
bi++;
}
}
// append a's tail if any
if (ai < al) {
int tail = al - ai;
System.arraycopy(ar, ai, cr, ci, tail);
ci += tail;
}
// now compact cr to 'just enough'
ar = new long[ci];
System.arraycopy(cr, 0, ar, 0, ci); // ar := cr
return new SparseLongSet(ar);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(6 * size);
sb.append("{ ");
if (elements != null) {
for (int ii = 0; ii < size; ii++) {
sb.append(elements[ii]);
sb.append(' ');
}
}
sb.append('}');
return sb.toString();
}
/**
* Reverse of toString(): "{2,3}" -> [2,3]
*
* @throws IllegalArgumentException if str is null
*/
public static long[] parseLongArray(String str)
throws NumberFormatException, IllegalArgumentException {
if (str == null) {
throw new IllegalArgumentException("str is null");
}
int len = str.length();
if (len < 2) {
throw new IllegalArgumentException("malformed input: " + str);
}
if (str.charAt(0) != '{' || str.charAt(len - 1) != '}') {
throw new NumberFormatException(str);
}
str = str.substring(1, len - 1);
StringTokenizer tok = new StringTokenizer(str, " ,");
// XXX not very efficient:
TreeSet set = new TreeSet<>();
while (tok.hasMoreTokens()) {
set.add(Long.decode(tok.nextToken()));
}
long[] result = new long[set.size()];
int i = 0;
for (Long L : set) {
result[i++] = L;
}
return result;
}
public static SparseLongSet singleton(int i) {
if (i >= 0 && i < SINGLETON_CACHE_SIZE) {
return singletonCache[i];
} else {
return new SparseLongSet(new long[] {i});
}
}
public static SparseLongSet pair(long i, long j) {
if (j > i) {
return new SparseLongSet(new long[] {i, j});
} else {
return new SparseLongSet(new long[] {j, i});
}
}
/**
* @see com.ibm.wala.util.intset.IntSet#intersection(com.ibm.wala.util.intset.IntSet)
*/
@Override
public @Nullable LongSet intersection(LongSet that)
throws IllegalArgumentException, UnimplementedError {
if (that == null) {
throw new IllegalArgumentException("that == null");
}
if (that instanceof SparseLongSet) {
MutableSparseLongSet temp = MutableSparseLongSet.make(this);
temp.intersectWith((SparseLongSet) that);
return temp;
} else {
Assertions.UNREACHABLE("Unexpected: " + that.getClass());
return null;
}
}
/**
* @see com.ibm.wala.util.intset.IntSet#intIterator()
*/
@Override
public LongIterator longIterator() {
return new LongIterator() {
int i = 0;
@Override
public boolean hasNext() {
return (i < size);
}
@Override
public long next() throws NoSuchElementException {
if (elements == null) {
throw new NoSuchElementException();
}
return elements[i++];
}
};
}
@Override
public void foreach(LongSetAction action) {
if (action == null) {
throw new IllegalArgumentException("null action");
}
for (int i = 0; i < size; i++) action.act(elements[i]);
}
@Override
public void foreachExcluding(LongSet X, LongSetAction action) {
if (X == null) {
throw new IllegalArgumentException("null X");
}
if (action == null) {
throw new IllegalArgumentException("null action");
}
for (int i = 0; i < size; i++) {
if (!X.contains(elements[i])) {
action.act(elements[i]);
}
}
}
/**
* @return the largest element in the set
*/
@Override
public final long max() throws IllegalStateException {
if (elements == null) {
throw new IllegalStateException("Illegal to ask max() on an empty int set");
}
return (size > 0) ? elements[size - 1] : -1;
}
/**
* @return a new sparse int set which adds j to s
*/
public static SparseLongSet add(SparseLongSet s, int j) {
if (s == null || s.elements == null) {
return SparseLongSet.singleton(j);
}
SparseLongSet result = new SparseLongSet(s.size + 1);
int k = 0;
int m = 0;
while (k < s.elements.length && s.elements[k] < j) {
result.elements[k++] = s.elements[m++];
}
if (k == s.size) {
result.elements[k] = j;
} else {
if (s.elements[k] == j) {
result.size--;
} else {
result.elements[k++] = j;
}
while (k < result.size) {
result.elements[k++] = s.elements[m++];
}
}
return result;
}
/**
* @see com.ibm.wala.util.intset.IntSet#isSubset(com.ibm.wala.util.intset.IntSet)
*/
@Override
public boolean isSubset(LongSet that) throws IllegalArgumentException, UnimplementedError {
if (that == null) {
throw new IllegalArgumentException("that == null");
}
if (that instanceof SparseLongSet) {
return isSubsetInternal((SparseLongSet) that);
} else {
Assertions.UNREACHABLE("Unexpected type " + that.getClass());
return false;
}
}
/**
* @see com.ibm.wala.util.intset.IntSet#containsAny(com.ibm.wala.util.intset.IntSet)
*/
@Override
public boolean containsAny(LongSet set) {
if (set == null) {
throw new IllegalArgumentException("set == null");
}
if (set instanceof SparseLongSet) {
return containsAny((SparseLongSet) set);
} else {
for (int i = 0; i < size; i++) {
if (set.contains(elements[i])) {
return true;
}
}
return false;
}
}
public boolean containsAny(SparseLongSet set) throws IllegalArgumentException {
if (set == null) {
throw new IllegalArgumentException("set == null");
}
int i = 0;
for (int j = 0; j < set.size; j++) {
long x = set.elements[j];
while (i < size && elements[i] < x) {
i++;
}
if (i == size) {
return false;
} else if (elements[i] == x) {
return true;
}
}
return false;
}
}