/*
* Redberry: symbolic tensor computations.
*
* Copyright (c) 2010-2015:
* Stanislav Poslavsky
* Bolotin Dmitriy
*
* This file is part of Redberry.
*
* Redberry is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Redberry is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Redberry. If not, see .
*/
package cc.redberry.core.utils;
import java.util.Arrays;
import static java.lang.Math.min;
/**
* This class represents an "array of booleans" with many fast and useful methods. Consumes ~ 8 times less memory than
* array of booleans for big sizes. Has slightly different semantics than java's built in {@link java.util.BitSet} and
* also provides additional functionality like {@link #loadValueFrom(BitArray, int, int, int)} and {@link
* #copyOfRange(int, int)}.
*/
public class BitArray {
public static final BitArray EMPTY = new BitArray(0);
final int[] data;
final int size;
private BitArray(int[] data, int size) {
this.data = data;
this.size = size;
}
/**
* Creates an array with specified size. Initial state of all bits is 0 (cleared).
*
* @param size
*/
public BitArray(int size) {
this.size = size;
this.data = new int[(size + 31) >>> 5];
}
/**
* Creates a bit array from array of booleans
*
* @param array boolean array
*/
public BitArray(boolean[] array) {
this(array.length);
for (int i = 0; i < array.length; ++i)
if (array[i])
set(i);
}
/**
* Compound assignment and.
*
* @param bitArray rhs of and
*/
public void and(BitArray bitArray) {
if (bitArray.size != size)
throw new IllegalArgumentException();
for (int i = data.length - 1; i >= 0; --i)
data[i] &= bitArray.data[i];
}
/**
* Compound assignment or.
*
* @param bitArray rhs of or
*/
public void or(BitArray bitArray) {
if (bitArray.size != size)
throw new IllegalArgumentException();
for (int i = data.length - 1; i >= 0; --i)
data[i] |= bitArray.data[i];
}
/**
* Compound assignment xor.
*
* @param bitArray rhs of xor
*/
public void xor(BitArray bitArray) {
if (bitArray.size != size)
throw new IllegalArgumentException();
for (int i = data.length - 1; i >= 0; --i)
data[i] ^= bitArray.data[i];
}
/**
* Inverts all bits in this bit array
*/
public void not() {
for (int i = data.length - 1; i >= 0; --i)
data[i] = ~data[i];
if (size > 0)
data[data.length - 1] &= lastElementMask();
}
int lastElementMask() {
if ((size & 0x1F) == 0)
return 0xFFFFFFFF;
else
return 0xFFFFFFFF >>> ((data.length << 5) - size);
//return ~(0xFFFFFFFF << (32 - ((data.length << 5) - size)));
}
/**
* Returns the number of 1 bits.
*
* @return number of 1 bits
*/
public int bitCount() {
int bits = 0;
for (int i : data)
bits += Integer.bitCount(i);
return bits;
}
/**
* Clears all bits of this bit array
*/
public void clearAll() {
Arrays.fill(data, 0);
}
/**
* Returns a clone of this bit array.
*
* @return clone of this bit array
*/
public BitArray clone() {
return new BitArray(data.clone(), size);
}
//public int[] getBits() {
// /*IntArrayList ial = new IntArrayList();
// for (int i = 0; i < data.length; ++i)
// nextTrailingBit() */
// return new int[0];
//}
/**
* Returns {@code true} if there are 1 bits in the same positions. Equivalent to {@code !this.and(other).isEmpty()
* }
*
* @param bitArray other bit array
* @return {@code true} if there are 1 bits in the same positions
*/
public boolean intersects(BitArray bitArray) {
if (bitArray.size != size)
throw new IllegalArgumentException();
for (int i = data.length - 1; i >= 0; --i)
if ((bitArray.data[i] & data[i]) != 0)
return true;
return false;
}
/**
* Copy values from the array of the same size
*
* @param bitArray bit array to copy values from
*/
public void loadValueFrom(BitArray bitArray) {
System.arraycopy(bitArray.data, 0, data, 0, data.length);
}
/**
* Returns the state of specified bit
*
* @param i index
* @return {@code true} if bit is set, {@code false} if bit is cleared
*/
public boolean get(int i) {
return (data[i >> 5] & (1 << (i & 0x1F))) != 0;
}
/**
* Sets the specified bit (sets to 1)
*
* @param i index of bit
*/
public void set(int i) {
data[i >> 5] |= (1 << (i & 0x1F));
}
/**
* Clears the specified bit (sets to 0)
*
* @param i index of bit
*/
public void clear(int i) {
data[i >> 5] &= ~(1 << (i & 0x1F));
}
/**
* Sets the value of specified bit to specified value
*
* @param i index
* @param value value
*/
public void set(int i, boolean value) {
if (value)
set(i);
else
clear(i);
}
/**
* Sets values at specified positions to specified value
*
* @param positions positions
* @param value value
*/
public void setAll(int[] positions, boolean value) {
for (int i : positions)
set(i, value);
}
/**
* Sets values at specified positions to specified value
*
* @param positions positions
* @param value value
*/
public void setAll(IntArrayList positions, boolean value) {
for (int i = positions.size() - 1; i >= 0; --i)
set(positions.get(i), value);
}
/**
* Sets all bits of this bit array
*/
public void setAll() {
for (int i = data.length - 2; i >= 0; --i)
data[i] = 0xFFFFFFFF;
if (size > 0)
data[data.length - 1] = lastElementMask();
}
/**
* Returns the length of this bit array
*
* @return length of this bit array
*/
public int size() {
return size;
}
/**
* Returns true if all bits in this array are in the 1 state.
*
* @return true if all bits in this array are in the 1 state
*/
public boolean isFull() {
if (size == 0)
return true;
for (int i = data.length - 2; i >= 0; --i)
if (data[i] != 0xFFFFFFFF)
return false;
return data[data.length - 1] == lastElementMask();
}
/**
* Returns true if all bits in this array are in the 0 state.
*
* @return true if all bits in this array are in the 0 state
*/
public boolean isEmpty() {
for (int i = data.length - 1; i >= 0; --i)
if (data[i] != 0)
return false;
return true;
}
/**
* Returns an array with positions of all "1" bits.
*
* @return array with positions of all "1" bits
*/
public int[] getBits() {
final int[] bits = new int[bitCount()];
int i, j = 0;
if (size < 40 || size >> 2 < bits.length) { // norm ??
for (i = 0; i < size; ++i)
if (get(i))
bits[j++] = i;
} else {
i = -1;
while ((i = nextBit(i + 1)) != -1) {
if (!get(i))
if (j > 0)
nextBit(bits[j - 1] + 1);
bits[j++] = i;
}
}
assert j == bits.length;
return bits;
}
/**
* Returns the next "1" bit from the specified position.
*
* @param position initial position
* @return position of the next "1" bit of -1 if all bits after position are 0
*/
public int nextBit(int position) {
int ret = position & 0x1F;
if (ret != 0)
if ((ret = Integer.numberOfTrailingZeros(data[position >>> 5] >>> ret)) != 32)
return position + ret;
else
position += 32;
ret = 32;
position = position >>> 5;
while (position < data.length &&
(ret = Integer.numberOfTrailingZeros(data[position++])) == 32) ;
if (position >= data.length && ret == 32)
return -1;
else
return ((position - 1) << 5) + ret;
}
/**
* Returns the next "0" bit from the specified position (inclusively).
*
* @param position initial position
* @return position of the next "0" bit of -1 if all bits after position are 0
*/
public int nextZeroBit(int position) {
//todo review
int ret = position & 0x1F;
if (ret != 0)
if ((ret = Integer.numberOfTrailingZeros((~data[position >>> 5]) >>> ret)) != 32) {
int r = position + ret;
return r >= size() ? -1 : r;
} else
position += 32;
ret = 32;
position = position >>> 5;
while (position < data.length &&
(ret = Integer.numberOfTrailingZeros(~data[position++])) == 32) ;
if (position >= data.length && ret == 32)
return -1;
else {
int r = ((position - 1) << 5) + ret;
return r >= size() ? -1 : r;
}
}
/**
* Returns a new bit array, containing values from the specified range
*
* @param from lower bound of range
* @param to upper bound of range
* @return new bit array, containing values from the specified range
*/
public BitArray copyOfRange(int from, int to) {
BitArray ba = new BitArray(to - from);
ba.loadValueFrom(this, from, 0, to - from);
return ba;
}
public BitArray copyOfRange(int from) {
return copyOfRange(from, size);
}
/**
* Analog of {@link System#arraycopy(Object, int, Object, int, int)}, where src is {@code bitArray}.
*
* @param bitArray source
* @param sourceOffset source offset
* @param thisOffset destination offset
* @param length number of bits to copy
*/
public void loadValueFrom(BitArray bitArray, int sourceOffset, int thisOffset, int length) {
if (bitArray == this)
throw new IllegalArgumentException("Can't copy from itself.");
//Forcing alignment on other (bitArray)
int alignmentOffset = (sourceOffset & 0x1F);
//Now in words (not in bits)
sourceOffset = sourceOffset >>> 5;
if (alignmentOffset != 0) {
int l = min(32 - alignmentOffset, length);
loadValueFrom(
bitArray.data[sourceOffset] >>> alignmentOffset,
thisOffset,
l);
thisOffset += l;
++sourceOffset;
length -= l;
}
//Bulk copy
while (length > 0) {
loadValueFrom(bitArray.data[sourceOffset], thisOffset, min(32, length));
length -= 32;
thisOffset += 32;
++sourceOffset;
}
}
/**
* Load 32 bits or less from single integer
*
* @param d integer with bits
* @param position offset
* @param length length
*/
void loadValueFrom(int d, int position, int length) {
if (length == 0)
return;
int res = position & 0x1F;
position = position >>> 5;
//mask for d
int mask = 0xFFFFFFFF >>> (32 - length);
if (res == 0) {
if (length == 32)
data[position] = d;
else {
data[position] &= ~mask;
data[position] |= d & mask;
}
return;
}
data[position] &= ~(mask << res);
data[position] |= (d & mask) << res;
length -= (32 - res);
if (length > 0)
loadValueFrom(d >>> (32 - res), (position + 1) << 5, length);
}
public BitArray append(BitArray other) {
BitArray ba = new BitArray(size + other.size);
System.arraycopy(data, 0, ba.data, 0, data.length);
ba.loadValueFrom(other, 0, size, other.size);
return ba;
}
public BitArray times(int times) {
BitArray ba = new BitArray(times * size);
if (times > 0)
System.arraycopy(data, 0, ba.data, 0, data.length);
for (int i = 1; i < times; ++i)
ba.loadValueFrom(this, 0, i * size, size);
return ba;
}
@Override
public String toString() {
char[] c = new char[size];
for (int i = 0; i < size; ++i)
if (get(i))
c[i] = '1';
else
c[i] = '0';
return new String(c);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
BitArray that = (BitArray) o;
if (size != that.size) return false;
return Arrays.equals(data, that.data);
}
@Override
public int hashCode() {
int result = Arrays.hashCode(data);
result = 31 * result + size;
return result;
}
}
Ads help maintain this website.