/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed 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 com.badlogic.gdx.utils;
/**
* A bitset, without size limitation, allows comparison via bitwise operators to
* other bitfields.
*
* @author mzechner
* @author jshapcott
*/
public class Bits {
long[] bits = { 0 };
public Bits() {
}
/**
* Creates a bit set whose initial size is large enough to explicitly
* represent bits with indices in the range 0 through nbits-1.
*
* @param nbits
* the initial size of the bit set
*/
public Bits(int nbits) {
checkCapacity(nbits >>> 6);
}
/**
* @param index
* the index of the bit
* @return whether the bit is set
* @throws ArrayIndexOutOfBoundsException
* if index < 0
*/
public boolean get(int index) {
final int word = index >>> 6;
if (word >= bits.length)
return false;
return (bits[word] & (1L << (index & 0x3F))) != 0L;
}
/**
* Returns the bit at the given index and clears it in one go.
*
* @param index
* the index of the bit
* @return whether the bit was set before invocation
* @throws ArrayIndexOutOfBoundsException
* if index < 0
*/
public boolean getAndClear(int index) {
final int word = index >>> 6;
if (word >= bits.length)
return false;
long oldBits = bits[word];
bits[word] &= ~(1L << (index & 0x3F));
return bits[word] != oldBits;
}
/**
* Returns the bit at the given index and sets it in one go.
*
* @param index
* the index of the bit
* @return whether the bit was set before invocation
* @throws ArrayIndexOutOfBoundsException
* if index < 0
*/
public boolean getAndSet(int index) {
final int word = index >>> 6;
checkCapacity(word);
long oldBits = bits[word];
bits[word] |= 1L << (index & 0x3F);
return bits[word] == oldBits;
}
/**
* @param index
* the index of the bit to set
* @throws ArrayIndexOutOfBoundsException
* if index < 0
*/
public void set(int index) {
final int word = index >>> 6;
checkCapacity(word);
bits[word] |= 1L << (index & 0x3F);
}
/**
* @param index
* the index of the bit to flip
*/
public void flip(int index) {
final int word = index >>> 6;
checkCapacity(word);
bits[word] ^= 1L << (index & 0x3F);
}
private void checkCapacity(int len) {
if (len >= bits.length) {
long[] newBits = new long[len + 1];
System.arraycopy(bits, 0, newBits, 0, bits.length);
bits = newBits;
}
}
/**
* @param index
* the index of the bit to clear
* @throws ArrayIndexOutOfBoundsException
* if index < 0
*/
public void clear(int index) {
final int word = index >>> 6;
if (word >= bits.length)
return;
bits[word] &= ~(1L << (index & 0x3F));
}
/** Clears the entire bitset */
public void clear() {
long[] bits = this.bits;
int length = bits.length;
for (int i = 0; i < length; i++) {
bits[i] = 0L;
}
}
/**
* @return the number of bits currently stored, <b>not</b> the highset set
* bit!
*/
public int numBits() {
return bits.length << 6;
}
/**
* Returns the "logical size" of this bitset: the index of the highest set
* bit in the bitset plus one. Returns zero if the bitset contains no set
* bits.
*
* @return the logical size of this bitset
*/
public int length() {
long[] bits = this.bits;
for (int word = bits.length - 1; word >= 0; --word) {
long bitsAtWord = bits[word];
if (bitsAtWord != 0) {
for (int bit = 63; bit >= 0; --bit) {
if ((bitsAtWord & (1L << (bit & 0x3F))) != 0L) {
return (word << 6) + bit;
}
}
}
}
return 0;
}
/**
* @return true if this bitset contains no bits that are set to true
*/
public boolean isEmpty() {
long[] bits = this.bits;
int length = bits.length;
for (int i = 0; i < length; i++) {
if (bits[i] != 0L) {
return false;
}
}
return true;
}
/**
* Returns the index of the first bit that is set to true that occurs on or
* after the specified starting index. If no such bit exists then -1 is
* returned.
*/
public int nextSetBit(int fromIndex) {
long[] bits = this.bits;
int word = fromIndex >>> 6;
int bitsLength = bits.length;
if (word >= bitsLength)
return -1;
long bitsAtWord = bits[word];
if (bitsAtWord != 0) {
for (int i = fromIndex & 0x3f; i < 64; i++) {
if ((bitsAtWord & (1L << (i & 0x3F))) != 0L) {
return (word << 6) + i;
}
}
}
for (word++; word < bitsLength; word++) {
if (word != 0) {
bitsAtWord = bits[word];
if (bitsAtWord != 0) {
for (int i = 0; i < 64; i++) {
if ((bitsAtWord & (1L << (i & 0x3F))) != 0L) {
return (word << 6) + i;
}
}
}
}
}
return -1;
}
/**
* Returns the index of the first bit that is set to false that occurs on or
* after the specified starting index. If no such bit exists then -1 is
* returned.
*/
public int nextClearBit(int fromIndex) {
long[] bits = this.bits;
int word = fromIndex >>> 6;
int bitsLength = bits.length;
if (word >= bitsLength)
return -1;
long bitsAtWord = bits[word];
for (int i = fromIndex & 0x3f; i < 64; i++) {
if ((bitsAtWord & (1L << (i & 0x3F))) == 0L) {
return (word << 6) + i;
}
}
for (word++; word < bitsLength; word++) {
if (word == 0) {
return word << 6;
}
bitsAtWord = bits[word];
for (int i = 0; i < 64; i++) {
if ((bitsAtWord & (1L << (i & 0x3F))) == 0L) {
return (word << 6) + i;
}
}
}
return -1;
}
/**
* Performs a logical <b>AND</b> of this target bit set with the argument
* bit set. This bit set is modified so that each bit in it has the value
* true if and only if it both initially had the value true and the
* corresponding bit in the bit set argument also had the value true.
*
* @param other
* a bit set
*/
public void and(Bits other) {
int commonWords = Math.min(bits.length, other.bits.length);
for (int i = 0; commonWords > i; i++) {
bits[i] &= other.bits[i];
}
if (bits.length > commonWords) {
for (int i = commonWords, s = bits.length; s > i; i++) {
bits[i] = 0L;
}
}
}
/**
* Clears all of the bits in this bit set whose corresponding bit is set in
* the specified bit set.
*
* @param other
* a bit set
*/
public void andNot(Bits other) {
for (int i = 0, j = bits.length, k = other.bits.length; i < j && i < k; i++) {
bits[i] &= ~other.bits[i];
}
}
/**
* Performs a logical <b>OR</b> of this bit set with the bit set argument.
* This bit set is modified so that a bit in it has the value true if and
* only if it either already had the value true or the corresponding bit in
* the bit set argument has the value true.
*
* @param other
* a bit set
*/
public void or(Bits other) {
int commonWords = Math.min(bits.length, other.bits.length);
for (int i = 0; commonWords > i; i++) {
bits[i] |= other.bits[i];
}
if (commonWords < other.bits.length) {
checkCapacity(other.bits.length);
for (int i = commonWords, s = other.bits.length; s > i; i++) {
bits[i] = other.bits[i];
}
}
}
/**
* Performs a logical <b>XOR</b> of this bit set with the bit set argument.
* This bit set is modified so that a bit in it has the value true if and
* only if one of the following statements holds:
* <ul>
* <li>The bit initially has the value true, and the corresponding bit in
* the argument has the value false.</li>
* <li>The bit initially has the value false, and the corresponding bit in
* the argument has the value true.</li>
* </ul>
*
* @param other
*/
public void xor(Bits other) {
int commonWords = Math.min(bits.length, other.bits.length);
for (int i = 0; commonWords > i; i++) {
bits[i] ^= other.bits[i];
}
if (bits.length > commonWords) {
for (int i = other.bits.length, s = bits.length; s > i; i++) {
bits[i] = 0L;
}
} else if (commonWords < other.bits.length) {
checkCapacity(other.bits.length);
for (int i = commonWords, s = other.bits.length; s > i; i++) {
bits[i] = other.bits[i];
}
}
}
/**
* Returns true if the specified BitSet has any bits set to true that are
* also set to true in this BitSet.
*
* @param other
* a bit set
* @return boolean indicating whether this bit set intersects the specified
* bit set
*/
public boolean intersects(Bits other) {
long[] bits = this.bits;
long[] otherBits = other.bits;
for (int i = Math.min(bits.length, otherBits.length) - 1; i >= 0; i--) {
if ((bits[i] & otherBits[i]) != 0) {
return true;
}
}
return false;
}
/**
* Returns true if this bit set is a super set of the specified set, i.e. it
* has all bits set to true that are also set to true in the specified
* BitSet.
*
* @param other
* a bit set
* @return boolean indicating whether this bit set is a super set of the
* specified set
*/
public boolean containsAll(Bits other) {
long[] bits = this.bits;
long[] otherBits = other.bits;
int otherBitsLength = otherBits.length;
int bitsLength = bits.length;
for (int i = bitsLength; i < otherBitsLength; i++) {
if (otherBits[i] != 0) {
return false;
}
}
for (int i = Math.min(bitsLength, otherBitsLength) - 1; i >= 0; i--) {
if ((bits[i] & otherBits[i]) != otherBits[i]) {
return false;
}
}
return true;
}
@Override
public int hashCode() {
final int word = length() >>> 6;
int hash = 0;
for (int i = 0; word >= i; i++) {
hash = 127 * hash + (int) (bits[i] ^ (bits[i] >>> 32));
}
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Bits other = (Bits) obj;
long[] otherBits = other.bits;
int commonWords = Math.min(bits.length, otherBits.length);
for (int i = 0; commonWords > i; i++) {
if (bits[i] != otherBits[i])
return false;
}
if (bits.length == otherBits.length)
return true;
return length() == other.length();
}
}