blob: 9dfe35ed9684804a534d73c96770fbe10b785342 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.util;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.LongBuffer;
import libcore.io.SizeOf;
/**
* The {@code BitSet} class implements a
* <a href="http://en.wikipedia.org/wiki/Bit_array">bit array</a>.
* Each element is either true or false. A {@code BitSet} is created with a given size and grows
* automatically if this size is exceeded.
*/
public class BitSet implements Serializable, Cloneable {
private static final long serialVersionUID = 7997698588986878753L;
private static final long ALL_ONES = ~0L;
/**
* The bits. Access bit n thus:
*
* boolean bit = (bits[n / 64] | (1 << n)) != 0;
*
* Note that Java's shift operators truncate their rhs to the log2 size of the lhs.
* That is, there's no "% 64" needed because it's implicit in the shift.
*
* TODO: would int[] be significantly more efficient for Android at the moment?
*/
private long[] bits;
/**
* The number of elements of 'bits' that are actually in use (non-zero). Amongst other
* things, this guarantees that isEmpty is cheap, because we never have to examine the array.
*/
private transient int longCount;
/**
* Updates 'longCount' by inspecting 'bits'. Assumes that the new longCount is <= the current
* longCount, to avoid scanning large tracts of empty array. This means it's safe to call
* directly after a clear operation that may have cleared the highest set bit, but
* not safe after an xor operation that may have cleared the highest set bit or
* made a new highest set bit. In that case, you'd need to set 'longCount' to a conservative
* estimate before calling this method.
*/
private void shrinkSize() {
int i = longCount - 1;
while (i >= 0 && bits[i] == 0) {
--i;
}
this.longCount = i + 1;
}
/**
* Creates a new {@code BitSet} with size equal to 64 bits.
*/
public BitSet() {
this(new long[1]);
}
/**
* Creates a new {@code BitSet} with size equal to {@code bitCount}, rounded up to
* a multiple of 64.
*
* @throws NegativeArraySizeException if {@code bitCount < 0}.
*/
public BitSet(int bitCount) {
if (bitCount < 0) {
throw new NegativeArraySizeException(Integer.toString(bitCount));
}
this.bits = arrayForBits(bitCount);
this.longCount = 0;
}
private BitSet(long[] bits) {
this.bits = bits;
this.longCount = bits.length;
shrinkSize();
}
private static long[] arrayForBits(int bitCount) {
return new long[(bitCount + 63)/ 64];
}
@Override public Object clone() {
try {
BitSet clone = (BitSet) super.clone();
clone.bits = bits.clone();
clone.shrinkSize();
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError(e);
}
}
@Override public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BitSet)) {
return false;
}
BitSet lhs = (BitSet) o;
if (this.longCount != lhs.longCount) {
return false;
}
for (int i = 0; i < longCount; ++i) {
if (bits[i] != lhs.bits[i]) {
return false;
}
}
return true;
}
/**
* Ensures that our long[] can hold at least 64 * desiredLongCount bits.
*/
private void ensureCapacity(int desiredLongCount) {
if (desiredLongCount <= bits.length) {
return;
}
int newLength = Math.max(desiredLongCount, bits.length * 2);
long[] newBits = new long[newLength];
System.arraycopy(bits, 0, newBits, 0, longCount);
this.bits = newBits;
// 'longCount' is unchanged by this operation: the long[] is larger,
// but you're not yet using any more of it.
}
@Override public int hashCode() {
// The RI doesn't use Arrays.hashCode, and explicitly specifies this algorithm.
long x = 1234;
for (int i = 0; i < longCount; ++i) {
x ^= bits[i] * (i + 1);
}
return (int) ((x >> 32) ^ x);
}
/**
* Returns the bit at index {@code index}. Indexes greater than the current length return false.
*
* @throws IndexOutOfBoundsException if {@code index < 0}.
*/
public boolean get(int index) {
if (index < 0) { // TODO: until we have an inlining JIT.
checkIndex(index);
}
int arrayIndex = index / 64;
if (arrayIndex >= longCount) {
return false;
}
return (bits[arrayIndex] & (1L << index)) != 0;
}
/**
* Sets the bit at index {@code index} to true.
*
* @throws IndexOutOfBoundsException if {@code index < 0}.
*/
public void set(int index) {
if (index < 0) { // TODO: until we have an inlining JIT.
checkIndex(index);
}
int arrayIndex = index / 64;
if (arrayIndex >= bits.length) {
ensureCapacity(arrayIndex + 1);
}
bits[arrayIndex] |= (1L << index);
longCount = Math.max(longCount, arrayIndex + 1);
}
/**
* Clears the bit at index {@code index}.
*
* @throws IndexOutOfBoundsException if {@code index < 0}.
*/
public void clear(int index) {
if (index < 0) { // TODO: until we have an inlining JIT.
checkIndex(index);
}
int arrayIndex = index / 64;
if (arrayIndex >= longCount) {
return;
}
bits[arrayIndex] &= ~(1L << index);
shrinkSize();
}
/**
* Flips the bit at index {@code index}.
*
* @throws IndexOutOfBoundsException if {@code index < 0}.
*/
public void flip(int index) {
if (index < 0) { // TODO: until we have an inlining JIT.
checkIndex(index);
}
int arrayIndex = index / 64;
if (arrayIndex >= bits.length) {
ensureCapacity(arrayIndex + 1);
}
bits[arrayIndex] ^= (1L << index);
longCount = Math.max(longCount, arrayIndex + 1);
shrinkSize();
}
private void checkIndex(int index) {
if (index < 0) {
throw new IndexOutOfBoundsException("index < 0: " + index);
}
}
private void checkRange(int fromIndex, int toIndex) {
if ((fromIndex | toIndex) < 0 || toIndex < fromIndex) {
throw new IndexOutOfBoundsException("fromIndex=" + fromIndex + " toIndex=" + toIndex);
}
}
/**
* Returns a new {@code BitSet} containing the
* range of bits {@code [fromIndex, toIndex)}, shifted down so that the bit
* at {@code fromIndex} is at bit 0 in the new {@code BitSet}.
*
* @throws IndexOutOfBoundsException
* if {@code fromIndex} or {@code toIndex} is negative, or if
* {@code toIndex} is smaller than {@code fromIndex}.
*/
public BitSet get(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
int last = 64 * longCount;
if (fromIndex >= last || fromIndex == toIndex) {
return new BitSet(0);
}
if (toIndex > last) {
toIndex = last;
}
int firstArrayIndex = fromIndex / 64;
int lastArrayIndex = (toIndex - 1) / 64;
long lowMask = ALL_ONES << fromIndex;
long highMask = ALL_ONES >>> -toIndex;
if (firstArrayIndex == lastArrayIndex) {
long result = (bits[firstArrayIndex] & (lowMask & highMask)) >>> fromIndex;
if (result == 0) {
return new BitSet(0);
}
return new BitSet(new long[] { result });
}
long[] newBits = new long[lastArrayIndex - firstArrayIndex + 1];
// first fill in the first and last indexes in the new BitSet
newBits[0] = bits[firstArrayIndex] & lowMask;
newBits[newBits.length - 1] = bits[lastArrayIndex] & highMask;
// fill in the in between elements of the new BitSet
for (int i = 1; i < lastArrayIndex - firstArrayIndex; i++) {
newBits[i] = bits[firstArrayIndex + i];
}
// shift all the elements in the new BitSet to the right
int numBitsToShift = fromIndex % 64;
int actualLen = newBits.length;
if (numBitsToShift != 0) {
for (int i = 0; i < newBits.length; i++) {
// shift the current element to the right regardless of
// sign
newBits[i] = newBits[i] >>> (numBitsToShift);
// apply the last x bits of newBits[i+1] to the current
// element
if (i != newBits.length - 1) {
newBits[i] |= newBits[i + 1] << -numBitsToShift;
}
if (newBits[i] != 0) {
actualLen = i + 1;
}
}
}
return new BitSet(newBits);
}
/**
* Sets the bit at index {@code index} to {@code state}.
*
* @throws IndexOutOfBoundsException if {@code index < 0}.
*/
public void set(int index, boolean state) {
if (state) {
set(index);
} else {
clear(index);
}
}
/**
* Sets the range of bits {@code [fromIndex, toIndex)} to {@code state}.
*
* @throws IndexOutOfBoundsException
* if {@code fromIndex} or {@code toIndex} is negative, or if
* {@code toIndex} is smaller than {@code fromIndex}.
*/
public void set(int fromIndex, int toIndex, boolean state) {
if (state) {
set(fromIndex, toIndex);
} else {
clear(fromIndex, toIndex);
}
}
/**
* Clears all the bits in this {@code BitSet}. This method does not change the capacity.
* Use {@code clear} if you want to reuse this {@code BitSet} with the same capacity, but
* create a new {@code BitSet} if you're trying to potentially reclaim memory.
*/
public void clear() {
Arrays.fill(bits, 0, longCount, 0L);
longCount = 0;
}
/**
* Sets the range of bits {@code [fromIndex, toIndex)}.
*
* @throws IndexOutOfBoundsException
* if {@code fromIndex} or {@code toIndex} is negative, or if
* {@code toIndex} is smaller than {@code fromIndex}.
*/
public void set(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
if (fromIndex == toIndex) {
return;
}
int firstArrayIndex = fromIndex / 64;
int lastArrayIndex = (toIndex - 1) / 64;
if (lastArrayIndex >= bits.length) {
ensureCapacity(lastArrayIndex + 1);
}
long lowMask = ALL_ONES << fromIndex;
long highMask = ALL_ONES >>> -toIndex;
if (firstArrayIndex == lastArrayIndex) {
bits[firstArrayIndex] |= (lowMask & highMask);
} else {
int i = firstArrayIndex;
bits[i++] |= lowMask;
while (i < lastArrayIndex) {
bits[i++] |= ALL_ONES;
}
bits[i++] |= highMask;
}
longCount = Math.max(longCount, lastArrayIndex + 1);
}
/**
* Clears the range of bits {@code [fromIndex, toIndex)}.
*
* @throws IndexOutOfBoundsException
* if {@code fromIndex} or {@code toIndex} is negative, or if
* {@code toIndex} is smaller than {@code fromIndex}.
*/
public void clear(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
if (fromIndex == toIndex || longCount == 0) {
return;
}
int last = 64 * longCount;
if (fromIndex >= last) {
return;
}
if (toIndex > last) {
toIndex = last;
}
int firstArrayIndex = fromIndex / 64;
int lastArrayIndex = (toIndex - 1) / 64;
long lowMask = ALL_ONES << fromIndex;
long highMask = ALL_ONES >>> -toIndex;
if (firstArrayIndex == lastArrayIndex) {
bits[firstArrayIndex] &= ~(lowMask & highMask);
} else {
int i = firstArrayIndex;
bits[i++] &= ~lowMask;
while (i < lastArrayIndex) {
bits[i++] = 0L;
}
bits[i++] &= ~highMask;
}
shrinkSize();
}
/**
* Flips the range of bits {@code [fromIndex, toIndex)}.
*
* @throws IndexOutOfBoundsException
* if {@code fromIndex} or {@code toIndex} is negative, or if
* {@code toIndex} is smaller than {@code fromIndex}.
*/
public void flip(int fromIndex, int toIndex) {
checkRange(fromIndex, toIndex);
if (fromIndex == toIndex) {
return;
}
int firstArrayIndex = fromIndex / 64;
int lastArrayIndex = (toIndex - 1) / 64;
if (lastArrayIndex >= bits.length) {
ensureCapacity(lastArrayIndex + 1);
}
long lowMask = ALL_ONES << fromIndex;
long highMask = ALL_ONES >>> -toIndex;
if (firstArrayIndex == lastArrayIndex) {
bits[firstArrayIndex] ^= (lowMask & highMask);
} else {
int i = firstArrayIndex;
bits[i++] ^= lowMask;
while (i < lastArrayIndex) {
bits[i++] ^= ALL_ONES;
}
bits[i++] ^= highMask;
}
longCount = Math.max(longCount, lastArrayIndex + 1);
shrinkSize();
}
/**
* Returns true if {@code this.and(bs)} is non-empty, but may be faster than computing that.
*/
public boolean intersects(BitSet bs) {
long[] bsBits = bs.bits;
int length = Math.min(this.longCount, bs.longCount);
for (int i = 0; i < length; ++i) {
if ((bits[i] & bsBits[i]) != 0L) {
return true;
}
}
return false;
}
/**
* Logically ands the bits of this {@code BitSet} with {@code bs}.
*/
public void and(BitSet bs) {
int minSize = Math.min(this.longCount, bs.longCount);
for (int i = 0; i < minSize; ++i) {
bits[i] &= bs.bits[i];
}
Arrays.fill(bits, minSize, longCount, 0L);
shrinkSize();
}
/**
* Clears all bits in this {@code BitSet} which are also set in {@code bs}.
*/
public void andNot(BitSet bs) {
int minSize = Math.min(this.longCount, bs.longCount);
for (int i = 0; i < minSize; ++i) {
bits[i] &= ~bs.bits[i];
}
shrinkSize();
}
/**
* Logically ors the bits of this {@code BitSet} with {@code bs}.
*/
public void or(BitSet bs) {
int minSize = Math.min(this.longCount, bs.longCount);
int maxSize = Math.max(this.longCount, bs.longCount);
ensureCapacity(maxSize);
for (int i = 0; i < minSize; ++i) {
bits[i] |= bs.bits[i];
}
if (bs.longCount > minSize) {
System.arraycopy(bs.bits, minSize, bits, minSize, maxSize - minSize);
}
longCount = maxSize;
}
/**
* Logically xors the bits of this {@code BitSet} with {@code bs}.
*/
public void xor(BitSet bs) {
int minSize = Math.min(this.longCount, bs.longCount);
int maxSize = Math.max(this.longCount, bs.longCount);
ensureCapacity(maxSize);
for (int i = 0; i < minSize; ++i) {
bits[i] ^= bs.bits[i];
}
if (bs.longCount > minSize) {
System.arraycopy(bs.bits, minSize, bits, minSize, maxSize - minSize);
}
longCount = maxSize;
shrinkSize();
}
/**
* Returns the capacity in bits of the array implementing this {@code BitSet}. This is
* unrelated to the length of the {@code BitSet}, and not generally useful.
* Use {@link #nextSetBit} to iterate, or {@link #length} to find the highest set bit.
*/
public int size() {
return bits.length * 64;
}
/**
* Returns the number of bits up to and including the highest bit set. This is unrelated to
* the {@link #size} of the {@code BitSet}.
*/
public int length() {
if (longCount == 0) {
return 0;
}
return 64 * (longCount - 1) + (64 - Long.numberOfLeadingZeros(bits[longCount - 1]));
}
/**
* Returns a string containing a concise, human-readable description of the
* receiver: a comma-delimited list of the indexes of all set bits.
* For example: {@code "{0,1,8}"}.
*/
@Override public String toString() {
//System.err.println("BitSet[longCount=" + longCount + ",bits=" + Arrays.toString(bits) + "]");
StringBuilder sb = new StringBuilder(longCount / 2);
sb.append('{');
boolean comma = false;
for (int i = 0; i < longCount; ++i) {
if (bits[i] != 0) {
for (int j = 0; j < 64; ++j) {
if ((bits[i] & 1L << j) != 0) {
if (comma) {
sb.append(", ");
} else {
comma = true;
}
sb.append(64 * i + j);
}
}
}
}
sb.append('}');
return sb.toString();
}
/**
* Returns the index of the first bit that is set on or after {@code index}, or -1
* if no higher bits are set.
* @throws IndexOutOfBoundsException if {@code index < 0}.
*/
public int nextSetBit(int index) {
checkIndex(index);
int arrayIndex = index / 64;
if (arrayIndex >= longCount) {
return -1;
}
long mask = ALL_ONES << index;
if ((bits[arrayIndex] & mask) != 0) {
return 64 * arrayIndex + Long.numberOfTrailingZeros(bits[arrayIndex] & mask);
}
while (++arrayIndex < longCount && bits[arrayIndex] == 0) {
}
if (arrayIndex == longCount) {
return -1;
}
return 64 * arrayIndex + Long.numberOfTrailingZeros(bits[arrayIndex]);
}
/**
* Returns the index of the first bit that is clear on or after {@code index}.
* Since all bits past the end are implicitly clear, this never returns -1.
* @throws IndexOutOfBoundsException if {@code index < 0}.
*/
public int nextClearBit(int index) {
checkIndex(index);
int arrayIndex = index / 64;
if (arrayIndex >= longCount) {
return index;
}
long mask = ALL_ONES << index;
if ((~bits[arrayIndex] & mask) != 0) {
return 64 * arrayIndex + Long.numberOfTrailingZeros(~bits[arrayIndex] & mask);
}
while (++arrayIndex < longCount && bits[arrayIndex] == ALL_ONES) {
}
if (arrayIndex == longCount) {
return 64 * longCount;
}
return 64 * arrayIndex + Long.numberOfTrailingZeros(~bits[arrayIndex]);
}
/**
* Returns the index of the first bit that is set on or before {@code index}, or -1 if
* no lower bits are set or {@code index == -1}.
* @throws IndexOutOfBoundsException if {@code index < -1}.
* @hide 1.7
*/
public int previousSetBit(int index) {
if (index == -1) {
return -1;
}
checkIndex(index);
// TODO: optimize this.
for (int i = index; i >= 0; --i) {
if (get(i)) {
return i;
}
}
return -1;
}
/**
* Returns the index of the first bit that is clear on or before {@code index}, or -1 if
* no lower bits are clear or {@code index == -1}.
* @throws IndexOutOfBoundsException if {@code index < -1}.
* @hide 1.7
*/
public int previousClearBit(int index) {
if (index == -1) {
return -1;
}
checkIndex(index);
// TODO: optimize this.
for (int i = index; i >= 0; --i) {
if (!get(i)) {
return i;
}
}
return -1;
}
/**
* Returns true if all the bits in this {@code BitSet} are set to false, false otherwise.
*/
public boolean isEmpty() {
return (longCount == 0);
}
/**
* Returns the number of bits that are {@code true} in this {@code BitSet}.
*/
public int cardinality() {
int result = 0;
for (int i = 0; i < longCount; ++i) {
result += Long.bitCount(bits[i]);
}
return result;
}
/**
* Equivalent to {@code BitSet.valueOf(LongBuffer.wrap(longs))}, but likely to be faster.
* This is likely to be the fastest way to create a {@code BitSet} because it's closest
* to the internal representation.
* @hide 1.7
*/
public static BitSet valueOf(long[] longs) {
return new BitSet(longs.clone());
}
/**
* Returns a {@code BitSet} corresponding to {@code longBuffer}, interpreted as a little-endian
* sequence of bits. This method does not alter the {@code LongBuffer}.
* @hide 1.7
*/
public static BitSet valueOf(LongBuffer longBuffer) {
// The bulk get would mutate LongBuffer (even if we reset position later), and it's not
// clear that's allowed. My assumption is that it's the long[] variant that's the common
// case anyway, so copy the buffer into a long[].
long[] longs = new long[longBuffer.remaining()];
for (int i = 0; i < longs.length; ++i) {
longs[i] = longBuffer.get(longBuffer.position() + i);
}
return BitSet.valueOf(longs);
}
/**
* Equivalent to {@code BitSet.valueOf(ByteBuffer.wrap(bytes))}.
* @hide 1.7
*/
public static BitSet valueOf(byte[] bytes) {
return BitSet.valueOf(ByteBuffer.wrap(bytes));
}
/**
* Returns a {@code BitSet} corresponding to {@code byteBuffer}, interpreted as a little-endian
* sequence of bits. This method does not alter the {@code ByteBuffer}.
* @hide 1.7
*/
public static BitSet valueOf(ByteBuffer byteBuffer) {
byteBuffer = byteBuffer.slice().order(ByteOrder.LITTLE_ENDIAN);
long[] longs = arrayForBits(byteBuffer.remaining() * 8);
int i = 0;
while (byteBuffer.remaining() >= SizeOf.LONG) {
longs[i++] = byteBuffer.getLong();
}
for (int j = 0; byteBuffer.hasRemaining(); ++j) {
longs[i] |= ((((long) byteBuffer.get()) & 0xff) << (8*j));
}
return BitSet.valueOf(longs);
}
/**
* Returns a new {@code long[]} containing a little-endian representation of the bits of
* this {@code BitSet}, suitable for passing to {@code valueOf} to reconstruct
* this {@code BitSet}.
* @hide 1.7
*/
public long[] toLongArray() {
return Arrays.copyOf(bits, longCount);
}
/**
* Returns a new {@code byte[]} containing a little-endian representation the bits of
* this {@code BitSet}, suitable for passing to {@code valueOf} to reconstruct
* this {@code BitSet}.
* @hide 1.7
*/
public byte[] toByteArray() {
int bitCount = length();
byte[] result = new byte[(bitCount + 7)/ 8];
for (int i = 0; i < result.length; ++i) {
int lowBit = 8 * i;
int arrayIndex = lowBit / 64;
result[i] = (byte) (bits[arrayIndex] >>> lowBit);
}
return result;
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
// The serialized form doesn't include a 'longCount' field, so we'll have to scan the array.
this.longCount = this.bits.length;
shrinkSize();
}
}