WALA/com.ibm.wala.util/src/com/ibm/wala/util/intset/BasicNaturalRelation.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 &gt;=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();
}
}