454 lines
12 KiB
Java
454 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 java.io.Serializable;
|
|
import java.util.Iterator;
|
|
|
|
import com.ibm.wala.util.collections.IVector;
|
|
import com.ibm.wala.util.collections.SimpleVector;
|
|
import com.ibm.wala.util.collections.TwoLevelVector;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
|
|
/**
|
|
* A relation between non-negative integers
|
|
*
|
|
* This implementation uses n IntVectors, to hold the first n y's associated with each x, and then 1 extra vector of SparseIntSet to
|
|
* hold the remaining ys.
|
|
*/
|
|
public final class BasicNaturalRelation implements IBinaryNaturalRelation, Serializable {
|
|
|
|
private static final long serialVersionUID = 4483720230344867621L;
|
|
|
|
private final static boolean VERBOSE = false;
|
|
|
|
private final static boolean DEBUG = false;
|
|
|
|
/**
|
|
* Tokens used as enumerated types to control the representation
|
|
*/
|
|
public final static byte SIMPLE = 0;
|
|
|
|
public final static byte TWO_LEVEL = 1;
|
|
|
|
public final static byte SIMPLE_SPACE_STINGY = 2;
|
|
|
|
private final static int EMPTY_CODE = -1;
|
|
|
|
private final static int DELEGATE_CODE = -2;
|
|
|
|
/**
|
|
* smallStore[i][x] holds
|
|
* <ul>
|
|
* <li>if >=0, the ith integer associated with x
|
|
* <li>if -2, then use the delegateStore instead of the small store
|
|
* <li>if -1, then R(x) is empty
|
|
* </ul>
|
|
* represented.
|
|
*/
|
|
final IntVector[] smallStore;
|
|
|
|
/**
|
|
* delegateStore[x] holds an int set of the y's s.t. R(x,y)
|
|
*/
|
|
final IVector<IntSet> delegateStore;
|
|
|
|
/**
|
|
* @param implementation a set of codes that represent how the first n IntVectors should be implemented.
|
|
* @param vectorImpl a code that indicates how to represent the delegateStore.
|
|
*
|
|
* For example implementation = {SIMPLE_INT_VECTOR,TWO_LEVEL_INT_VECTOR,TWO_LEVEL_INT_VECTOR} will result in an
|
|
* implementation where the first 3 y's associated with each x are represented in IntVectors. The IntVector for the first
|
|
* y will be implemented with a SimpleIntVector, and the 2nd and 3rd are implemented with TwoLevelIntVector
|
|
*
|
|
* @throws IllegalArgumentException if implementation is null
|
|
* @throws IllegalArgumentException if implementation.length == 0
|
|
*/
|
|
public BasicNaturalRelation(byte[] implementation, byte vectorImpl) throws IllegalArgumentException {
|
|
|
|
if (implementation == null) {
|
|
throw new IllegalArgumentException("implementation is null");
|
|
}
|
|
if (implementation.length == 0) {
|
|
throw new IllegalArgumentException("implementation.length == 0");
|
|
}
|
|
smallStore = new IntVector[implementation.length];
|
|
for (int i = 0; i < implementation.length; i++) {
|
|
switch (implementation[i]) {
|
|
case SIMPLE:
|
|
smallStore[i] = new SimpleIntVector(EMPTY_CODE);
|
|
break;
|
|
case TWO_LEVEL:
|
|
smallStore[i] = new TwoLevelIntVector(EMPTY_CODE);
|
|
break;
|
|
case SIMPLE_SPACE_STINGY:
|
|
smallStore[i] = new TunedSimpleIntVector(EMPTY_CODE, 1, 1.1f);
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("unsupported implementation " + implementation[i]);
|
|
}
|
|
}
|
|
switch (vectorImpl) {
|
|
case SIMPLE:
|
|
delegateStore = new SimpleVector<>();
|
|
break;
|
|
case TWO_LEVEL:
|
|
delegateStore = new TwoLevelVector<>();
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException("unsupported implementation " + vectorImpl);
|
|
}
|
|
}
|
|
|
|
public BasicNaturalRelation() {
|
|
this(new byte[] { SIMPLE }, TWO_LEVEL);
|
|
}
|
|
|
|
/**
|
|
* maximum x for any pair in this relation.
|
|
*/
|
|
private int maxX = -1;
|
|
|
|
/**
|
|
* Add (x,y) to the relation.
|
|
*
|
|
* This is performance-critical, so the implementation looks a little ugly in order to help out the compiler with redundancy
|
|
* elimination.
|
|
*
|
|
* @return true iff the relation changes as a result of this call.
|
|
*/
|
|
@Override
|
|
public boolean add(int x, int y) throws IllegalArgumentException {
|
|
if (x < 0) {
|
|
throw new IllegalArgumentException("illegal x: " + x);
|
|
}
|
|
if (y < 0) {
|
|
throw new IllegalArgumentException("illegal y: " + y);
|
|
}
|
|
maxX = Math.max(maxX, x);
|
|
MutableIntSet delegated = (MutableIntSet) delegateStore.get(x);
|
|
if (delegated != null) {
|
|
return delegated.add(y);
|
|
} else {
|
|
IntVector smallStore0 = smallStore[0];
|
|
if (smallStore0.get(x) != EMPTY_CODE) {
|
|
int i = 0;
|
|
IntVector v = null;
|
|
int ssLength = smallStore.length;
|
|
for (; i < ssLength; i++) {
|
|
v = smallStore[i];
|
|
int val = v.get(x);
|
|
if (val == y) {
|
|
return false;
|
|
} else if (val == EMPTY_CODE) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == ssLength) {
|
|
MutableIntSet s = new BimodalMutableIntSet(ssLength + 1, 1.1f);
|
|
delegateStore.set(x, s);
|
|
for (int j = 0; j < smallStore.length; j++) {
|
|
IntVector vv = smallStore[j];
|
|
s.add(vv.get(x));
|
|
vv.set(x, DELEGATE_CODE);
|
|
}
|
|
s.add(y);
|
|
} else {
|
|
v.set(x, y);
|
|
}
|
|
return true;
|
|
} else {
|
|
// smallStore[0].get(x) == EMPTY_CODE : just add
|
|
smallStore0.set(x, y);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean usingDelegate(int x) {
|
|
return smallStore[0].get(x) == DELEGATE_CODE;
|
|
}
|
|
|
|
@Override
|
|
public Iterator<IntPair> iterator() {
|
|
return new TotalIterator();
|
|
}
|
|
|
|
private class TotalIterator implements Iterator<IntPair> {
|
|
|
|
/**
|
|
* the next x that will be returned in a pair (x,y), or -1 if not hasNext()
|
|
*/
|
|
private int nextX = -1;
|
|
|
|
/**
|
|
* the source of the next y ... an integer between 0 and smallStore.length .. nextIndex == smallStore.length means use the
|
|
* delegateIterator
|
|
*/
|
|
private int nextIndex = -1;
|
|
|
|
private IntIterator delegateIterator = null;
|
|
|
|
TotalIterator() {
|
|
advanceX();
|
|
}
|
|
|
|
private void advanceX() {
|
|
delegateIterator = null;
|
|
for (int i = nextX + 1; i <= maxX; i++) {
|
|
if (anyRelated(i)) {
|
|
nextX = i;
|
|
nextIndex = getFirstIndex(i);
|
|
if (nextIndex == smallStore.length) {
|
|
IntSet s = delegateStore.get(i);
|
|
assert s.size() > 0;
|
|
delegateIterator = s.intIterator();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
nextX = -1;
|
|
}
|
|
|
|
private int getFirstIndex(int x) {
|
|
if (smallStore[0].get(x) >= 0) {
|
|
// will get first y for x from smallStore[0][x]
|
|
return 0;
|
|
} else {
|
|
// will get first y for x from delegateStore[x]
|
|
return smallStore.length;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean hasNext() {
|
|
return nextX != -1;
|
|
}
|
|
|
|
@Override
|
|
public IntPair next() {
|
|
IntPair result = null;
|
|
if (nextIndex == smallStore.length) {
|
|
int y = delegateIterator.next();
|
|
result = new IntPair(nextX, y);
|
|
if (!delegateIterator.hasNext()) {
|
|
advanceX();
|
|
}
|
|
} else {
|
|
result = new IntPair(nextX, smallStore[nextIndex].get(nextX));
|
|
if (nextIndex == (smallStore.length - 1) || smallStore[nextIndex + 1].get(nextX) == EMPTY_CODE) {
|
|
advanceX();
|
|
} else {
|
|
nextIndex++;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public void remove() {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
|
|
}
|
|
|
|
private IntSet getDelegate(int x) {
|
|
return delegateStore.get(x);
|
|
}
|
|
|
|
/**
|
|
* @param x
|
|
* @return true iff there exists pair (x,y) for some y
|
|
*/
|
|
@Override
|
|
public boolean anyRelated(int x) {
|
|
return smallStore[0].get(x) != EMPTY_CODE;
|
|
}
|
|
|
|
/*
|
|
* @see com.ibm.wala.util.intset.IBinaryNonNegativeIntRelation#getRelated(int)
|
|
*/
|
|
@Override
|
|
public IntSet getRelated(int x) {
|
|
if (DEBUG) {
|
|
assert x >= 0;
|
|
}
|
|
int ss0 = smallStore[0].get(x);
|
|
if (ss0 == EMPTY_CODE) {
|
|
return null;
|
|
} else {
|
|
if (ss0 == DELEGATE_CODE) {
|
|
return getDelegate(x);
|
|
} else {
|
|
int ssLength = smallStore.length;
|
|
if (ssLength == 2) {
|
|
int ss1 = smallStore[1].get(x);
|
|
if (ss1 == EMPTY_CODE) {
|
|
return SparseIntSet.singleton(ss0);
|
|
} else {
|
|
return SparseIntSet.pair(ss0, ss1);
|
|
}
|
|
} else if (ssLength == 1) {
|
|
return SparseIntSet.singleton(ss0);
|
|
} else {
|
|
int ss1 = smallStore[1].get(x);
|
|
if (ss1 == EMPTY_CODE) {
|
|
return SparseIntSet.singleton(ss0);
|
|
} else {
|
|
MutableSparseIntSet result = MutableSparseIntSet.createMutableSparseIntSet(ssLength);
|
|
for (int i = 0; i < smallStore.length; i++) {
|
|
if (smallStore[i].get(x) == EMPTY_CODE) {
|
|
break;
|
|
}
|
|
result.add(smallStore[i].get(x));
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @see com.ibm.wala.util.intset.IBinaryNonNegativeIntRelation#getRelatedCount(int)
|
|
*/
|
|
@Override
|
|
public int getRelatedCount(int x) throws IllegalArgumentException {
|
|
if (x < 0) {
|
|
throw new IllegalArgumentException("x must be greater than zero");
|
|
}
|
|
if (!anyRelated(x)) {
|
|
return 0;
|
|
} else {
|
|
if (usingDelegate(x)) {
|
|
return getDelegate(x).size();
|
|
} else {
|
|
int result = 0;
|
|
for (int i = 0; i < smallStore.length; i++) {
|
|
if (smallStore[i].get(x) == EMPTY_CODE) {
|
|
break;
|
|
}
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void remove(int x, int y) {
|
|
if (x < 0) {
|
|
throw new IllegalArgumentException("illegal x: " + x);
|
|
}
|
|
if (y < 0) {
|
|
throw new IllegalArgumentException("illegal y: " + y);
|
|
}
|
|
if (usingDelegate(x)) {
|
|
// TODO: switch representation back to small store?
|
|
MutableIntSet s = (MutableIntSet) delegateStore.get(x);
|
|
s.remove(y);
|
|
if (s.size() == 0) {
|
|
delegateStore.set(x, null);
|
|
for (int i = 0; i < smallStore.length; i++) {
|
|
smallStore[i].set(x, EMPTY_CODE);
|
|
}
|
|
}
|
|
} else {
|
|
for (int i = 0; i < smallStore.length; i++) {
|
|
if (smallStore[i].get(x) == y) {
|
|
for (int j = i; j < smallStore.length; j++) {
|
|
if (j < (smallStore.length - 1)) {
|
|
smallStore[j].set(x, smallStore[j + 1].get(x));
|
|
} else {
|
|
smallStore[j].set(x, EMPTY_CODE);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void removeAll(int x) {
|
|
for (int i = 0; i < smallStore.length; i++) {
|
|
smallStore[i].set(x, EMPTY_CODE);
|
|
}
|
|
delegateStore.set(x, null);
|
|
}
|
|
|
|
/*
|
|
* @see com.ibm.wala.util.debug.VerboseAction#performVerboseAction()
|
|
*/
|
|
@Override
|
|
public void performVerboseAction() {
|
|
if (VERBOSE) {
|
|
System.err.println((getClass() + " stats:"));
|
|
System.err.println(("count: " + countPairs()));
|
|
System.err.println(("delegate: " + delegateStore.getClass()));
|
|
delegateStore.performVerboseAction();
|
|
for (int i = 0; i < smallStore.length; i++) {
|
|
System.err.println(("smallStore[" + i + "]: " + smallStore[i].getClass()));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is slow.
|
|
*
|
|
* @return the size of this relation, in the number of pairs
|
|
*/
|
|
private int countPairs() {
|
|
int result = 0;
|
|
for (Iterator<?> it = iterator(); it.hasNext();) {
|
|
it.next();
|
|
result++;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean contains(int x, int y) {
|
|
if (x < 0) {
|
|
throw new IllegalArgumentException("invalid x: " + x);
|
|
}
|
|
if (y < 0) {
|
|
throw new IllegalArgumentException("invalid y: " + y);
|
|
}
|
|
if (usingDelegate(x)) {
|
|
return getDelegate(x).contains(y);
|
|
} else {
|
|
for (int i = 0; i < smallStore.length; i++) {
|
|
if (smallStore[i].get(x) == y) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int maxKeyValue() {
|
|
return maxX;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
StringBuffer result = new StringBuffer();
|
|
for (int i = 0; i <= maxX; i++) {
|
|
result.append(i).append(":");
|
|
result.append(getRelated(i));
|
|
result.append("\n");
|
|
}
|
|
return result.toString();
|
|
}
|
|
}
|