/*
* Kodkod -- Copyright (c) 2005-present, Emina Torlak
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package kodkod.util.ints;
import java.util.Arrays;
import java.util.NoSuchElementException;
/**
* An implementation of the IntSet interface based on a bit map.
* An IntBitSet can store only numbers in the half-open range
* [0..capacity) where capacity is a user-specified value.
* The implementation will allocated enough bits to explicitly represent all allowed
* integers; it performs better than a tree set when the stored integers
* are not clustered.
* @specfield capacity: [0..Integer.MAX_VALUE]
* @invariant all i: this.ints | 0 <= i < capacity
* @author Emina Torlak
*/
public final class IntBitSet extends AbstractIntSet implements Cloneable {
// implementation adapted from java.util.JumboEnumSet
private final int capacity;
/*
* Bit vector representation of this set. The ith bit of the jth
* element of this array represents the presence of universe[64*j +i]
* in this set.
*/
private long elements[];
// Redundant - maintained for performance
private int size;
/**
* Constructs an empty IntBitSet that can store up
* to capacity elements.
* @ensures no this.ints' && this.capacity' = capacity
* @throws IllegalArgumentException capacity < 0
*/
public IntBitSet(int capacity) {
if (capacity < 0) throw new IllegalArgumentException("capacity < 0");
this.capacity = capacity;
elements = new long[(capacity >>> 6) + 1];
size = 0;
}
/**
* Constructs an IntBitSet that can store up to capacity elements.
* The set is initialized to contain all integers i such that
* data[i>>>6] & (1L<<i) == 1. This IntBitSet is backed by the given
* data array. The array must not be modified while in use by this set.
* @requires 0 <= capacity < max({i: int | data[i>>>6] & (1L<<i) == 1}) or (capacity>>>6)+1 > data.length
* @throws IllegalArgumentException capacity is out of range
*/
public IntBitSet(int capacity, long[] data) {
if (capacity > (data.length<<6)) throw new IllegalArgumentException("capacity too large: " + capacity + ", max: " + (data.length<<6));
this.capacity = capacity;
this.elements = data;
recalculateSize();
// System.out.println("capacity: " + capacity + ", max: " + max() + ", data.length: " + data.length);
// System.out.println(Arrays.toString(data));
if (size > 0 && capacity <= max()) throw new IllegalArgumentException("capacity too small");
}
/**
* Returns the smallest element in this set.
* Throws a NoSuchElementException if this set is empty.
* @return min(this.ints)
* @throws java.util.NoSuchElementException no this.ints
* @see kodkod.util.ints.IntSet#min()
*/
@Override
public int min() {
checkNonEmpty();
int minWordIndex = 0;
while(elements[minWordIndex]==0) { minWordIndex++; }
return (minWordIndex << 6) + Long.numberOfTrailingZeros(elements[minWordIndex]);
}
/**
* Returns the largest element in this set.
* Throws a NoSuchElementException if this set is empty.
* @return max(this.ints)
* @throws java.util.NoSuchElementException no this.ints
* @see kodkod.util.ints.IntSet#max()
*/
@Override
public int max() {
checkNonEmpty();
int maxWordIndex = elements.length-1;
while(elements[maxWordIndex]==0) { maxWordIndex--; }
return (maxWordIndex << 6) + 63 - Long.numberOfLeadingZeros(elements[maxWordIndex]);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.IntSet#ceil(int)
*/
public int ceil(int i) {
if (i <= 0)
return min();
int wordIndex = wordIndex(i);
long word = 0;
if (wordIndex < elements.length) {
word = (extendedMask(i) & elements[wordIndex]);
}
while(word==0 && wordIndex < elements.length-1) {
word = elements[++wordIndex];
}
if (word==0)
throw new NoSuchElementException();
else
return (wordIndex << 6) + Long.numberOfTrailingZeros(word);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.IntSet#floor(int)
*/
public int floor(int i) {
if (i < 0)
throw new NoSuchElementException();
int wordIndex = wordIndex(i);
long word = 0;
if (wordIndex < elements.length) {
word = ((~extendedMask(i+1)) & elements[wordIndex]);
} else {
wordIndex = elements.length-1;
word = elements[wordIndex];
}
while(word==0 && wordIndex > 0) {
word = elements[--wordIndex];
}
if (word==0)
throw new NoSuchElementException();
else
return (wordIndex << 6) + 63 - Long.numberOfLeadingZeros(word);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.IntSet#iterator()
*/
@Override
public IntIterator iterator() {
return new AscendingIterator(0,capacity);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.IntSet#iterator(int, int)
*/
public IntIterator iterator(int from, int to) {
return from > to ? new DescendingIterator(from,to) : new AscendingIterator(from,to);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.IntSet#size()
*/
public int size() {
return size;
}
/**
* Returns the capacity of this int bit set
* @return this.capacity
*/
public int capacity() { return capacity; }
/**
* Returns the index of the word that contains
* the bit that represents the integer i.
* @requires 0 <= i < this.capacity
*/
private final int wordIndex(int i) {
return i >>> 6;
}
/**
* Returns a bit mask that has 1 in the position representing the
* given integer within its word (obtained by wordIndex(i))
* @requires 0 <= i < this.capacity
*/
private final long bitMask(int i) {
return 1L << i;
}
/**
* Returns a bit mask that has 1 at every index greater than
* or equal to the position representing the
* given integer within its word.
* @requires 0 <= i < this.capacity
*/
private final long extendedMask(int i) {
return -1L << i;
}
/**
* @return i in [0..this.capacity)
*/
private final boolean allows(int i) {
return 0 <= i && i < capacity;
}
/**
* Returns true if i is in this set.
* @return i in this.ints
* @see kodkod.util.ints.IntSet#contains(int)
*/
@Override
public boolean contains(int i) {
return allows(i) && (elements[wordIndex(i)] & (bitMask(i))) != 0;
}
/**
* Adds the given integer to this set if not already present
* and returns true. Otherwise does nothing and returns false.
* @ensures this.ints' = this.ints + i
* @return i in this.ints'
* @throws IllegalArgumentException i !in [0..this.capacity)
* @see kodkod.util.ints.IntSet#add(int)
*/
@Override
public boolean add(int i) {
if (!allows(i)) throw new IllegalArgumentException(i + " !in [0.." + capacity + ")");
final int wordIndex = wordIndex(i);
final long oldElements = elements[wordIndex];
elements[wordIndex] |= bitMask(i);
if (elements[wordIndex] != oldElements) {
size++;
return true;
}
return false;
}
/**
* Removes the given integer from this set if already present and
* returns true. Otherwise does nothing and returns false.
* @ensures this.ints' = this.ints - i
* @return i !in this.ints'
* @see kodkod.util.ints.IntSet#remove(int)
*/
@Override
public boolean remove(int i) {
if (allows(i)) {
final int wordIndex = wordIndex(i);
final long oldElements = elements[wordIndex];
elements[wordIndex] &= ~bitMask(i);
if (elements[wordIndex] != oldElements) {
size--;
return true;
}
}
return false;
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.IntCollection#isEmpty()
*/
@Override
public boolean isEmpty() {
return size==0;
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.IntSet#containsAll(kodkod.util.ints.IntCollection)
*/
@Override
public boolean containsAll(IntCollection other) {
if (other instanceof IntBitSet) {
final IntBitSet s = (IntBitSet) other;
if (isEmpty() || s.isEmpty()) return isEmpty() ? s.isEmpty():true;
if (size < s.size || max() < s.max()) return false;
final int minLength = StrictMath.min(elements.length, s.elements.length);
for(int wordIndex = 0; wordIndex < minLength; wordIndex++) {
if ((s.elements[wordIndex] & ~elements[wordIndex]) != 0)
return false;
}
return true;
}
return super.containsAll(other);
}
/**
* Recalculates the size and returns true if the size has changed.
*/
private boolean recalculateSize() {
final int oldSize = size;
size = 0;
for(long elt: elements) {
size += Long.bitCount(elt);
}
return size!=oldSize;
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.IntSet#addAll(kodkod.util.ints.IntCollection)
*/
@Override
public boolean addAll(IntCollection other) {
if (other instanceof IntBitSet) {
final IntBitSet s = (IntBitSet) other;
if (s.isEmpty()) return false;
if (s.max() >= capacity)
throw new IllegalArgumentException(s.max()+" !in [0.." + capacity + ")");
final int minLength = StrictMath.min(elements.length, s.elements.length);
for(int wordIndex = 0; wordIndex < minLength; wordIndex++) {
elements[wordIndex] |= s.elements[wordIndex];
}
return recalculateSize();
}
return super.addAll(other);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.IntSet#retainAll(kodkod.util.ints.IntCollection)
*/
@Override
public boolean retainAll(IntCollection other) {
if (other instanceof IntBitSet) {
final IntBitSet s = (IntBitSet) other;
final int minLength = StrictMath.min(elements.length, s.elements.length);
int wordIndex = 0;
for(; wordIndex < minLength; wordIndex++) {
elements[wordIndex] &= s.elements[wordIndex];
}
for(; wordIndex < elements.length; wordIndex++) {
elements[wordIndex] = 0;
}
return recalculateSize();
}
return super.retainAll(other);
}
/**
* {@inheritDoc}
* @see kodkod.util.ints.IntSet#removeAll(kodkod.util.ints.IntCollection)
*/
@Override
public boolean removeAll(IntCollection other) {
if (other instanceof IntBitSet) {
final IntBitSet s = (IntBitSet) other;
final int minLength = StrictMath.min(elements.length, s.elements.length);
for(int wordIndex = 0; wordIndex < minLength; wordIndex++) {
elements[wordIndex] &= ~s.elements[wordIndex];
}
return recalculateSize();
}
return super.removeAll(other);
}
/**
* Removes all elements from this set.
* @ensures no this.ints'
* @see kodkod.util.ints.IntCollection#clear()
*/
@Override
public void clear() {
Arrays.fill(elements, 0);
size = 0;
}
/**
* Returns a copy of this int bit set. The copy is independent of this
* IntSet.
* @return a copy of this IntSet.
* @see kodkod.util.ints.IntSet#clone()
*/
@Override
public IntBitSet clone() {
try {
final IntBitSet ret = (IntBitSet) super.clone();
ret.elements = (long[]) this.elements.clone();
return ret;
} catch (CloneNotSupportedException e) {
throw new InternalError(); // unreachable code
}
}
/**
* Stores common fields and methods for the ascending and descending iterators.
*/
private abstract class AbstractIterator implements IntIterator {
long unseen;
int unseenIndex, lastReturned;
public void remove() {
if (lastReturned < 0)
throw new IllegalStateException();
elements[wordIndex(lastReturned)] -= bitMask(lastReturned);
size--;
lastReturned = -1;
}
}
/**
* Implementation of an ascending iterator over (a subset of) this set.
*/
private final class AscendingIterator extends AbstractIterator {
private final long maxMask;
private final int maxIndex;
/**
* Constructs an ascending iterator that returns elements between
* from and to.
* @requires from <= to
*/
AscendingIterator(int from, int to) {
if (from >= capacity || to < 0) {
unseenIndex = maxIndex = elements.length;
unseen = maxMask = 0L;
} else {
if (to >= capacity) {
maxIndex = elements.length - 1;
maxMask = -1L;
} else {
maxIndex = wordIndex(to);
maxMask = (bitMask(to)==Long.MIN_VALUE ? -1L : ~extendedMask(to+1));
}
if (from < 0) {
unseenIndex = 0;
unseen = elements[0];
} else {
unseenIndex = wordIndex(from);
unseen = elements[unseenIndex] & extendedMask(from);
}
}
lastReturned = -1;
}
public boolean hasNext() {
while (unseen == 0 && unseenIndex < elements.length - 1)
unseen = elements[++unseenIndex];
return (unseenIndex < maxIndex && unseen != 0) ||
(unseenIndex == maxIndex && (unseen & maxMask) != 0);
}
public int next() {
if (!hasNext()) throw new NoSuchElementException();
final long lastReturnedMask = Long.lowestOneBit(unseen);
unseen -= lastReturnedMask;
lastReturned = (unseenIndex << 6) + Long.numberOfTrailingZeros(lastReturnedMask);
return lastReturned;
}
}
/**
* Implementation of a descending iterator over (a subset of) this set.
*/
private final class DescendingIterator extends AbstractIterator {
private final long minMask;
private final int minIndex;
/**
* Constructs a descending iterator that returns elements between
* from and to.
* @requires from >= to
*/
DescendingIterator(int from, int to) {
if (to >= capacity || from < 0) {
unseenIndex = minIndex = 0;
unseen = minMask = 0L;
} else {
if (from < capacity) {
unseenIndex = wordIndex(from);
unseen = elements[unseenIndex] &
(bitMask(from)==Long.MIN_VALUE ? -1L : ~extendedMask(from+1));
} else {
unseenIndex = elements.length-1;
unseen = elements[unseenIndex];
}
if (to < 0) {
minIndex = 0 ;
minMask = -1L;
} else {
minIndex = wordIndex(to);
minMask = extendedMask(to);
}
}
lastReturned = -1;
}
public boolean hasNext() {
while (unseen == 0 && unseenIndex > 0)
unseen = elements[--unseenIndex];
return (unseenIndex > minIndex && unseen != 0) ||
(unseenIndex == minIndex && (unseen & minMask) != 0);
}
public int next() {
if (!hasNext()) throw new NoSuchElementException();
final long lastReturnedMask = Long.highestOneBit(unseen);
unseen -= lastReturnedMask;
lastReturned = (unseenIndex << 6) + 63 - Long.numberOfLeadingZeros(lastReturnedMask);
return lastReturned;
}
}
}