
com.tomgibara.bits.BitVector Maven / Gradle / Ivy
Show all versions of bits Show documentation
/*
* Copyright 2010 Tom Gibara
*
* 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.tomgibara.bits;
import static com.tomgibara.bits.Bits.checkBitsLength;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectStreamException;
import java.io.OutputStream;
import java.io.Serializable;
import java.math.BigInteger;
import java.security.PrivilegedAction;
import java.util.AbstractList;
import java.util.AbstractSet;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.SortedSet;
import com.tomgibara.bits.ImmutableBit.ImmutableOne;
import com.tomgibara.bits.ImmutableBit.ImmutableZero;
import com.tomgibara.fundament.Alignable;
import com.tomgibara.streams.ReadStream;
import com.tomgibara.streams.WriteStream;
/**
*
* Provides a 'cannonical' {@link BitStore} implementation for situations where
* bit operations must be performed with a trusted implementation. The class is
* marked as final to ensure that immutable instances can be safely used in
* security sensitive code (eg. within a {@link PrivilegedAction}).
*
*
* Instances of this class may be aligned (the class implements the
* Alignable
interface - see {@link #isAligned()} and
* {@link #aligned()}). Many operations will execute more efficiently on aligned
* instances. Instances may also be immutable (see {@link #isMutable()},
* {@link #mutable()} and {@link #immutable()}).
*
*
* The class is {@link Serializable} and {@link Cloneable} too (clones are
* shallow and essentially behave as a view of the original instance). For
* instances where more control over serialization is needed,
* {@link #readFrom(ReadStream)} and {@link #writeTo(WriteStream)} methods are
* available, though better performance may result from calling
* {@link #toByteArray()} and managing the writing outside this class.
*
*
* Performance should be adequate for most uses; none of the methods operate in
* anything more than linear time and inner loops are mostly 'tight'. Generally,
* method implementations aim to avoid unnecessary intermediate object creation
* and data copying.
*
* @author Tom Gibara
*/
public final class BitVector implements BitStore, Alignable, Cloneable, Serializable {
// statics
private static final int ADDRESS_BITS = 6;
private static final int ADDRESS_SIZE = 1 << ADDRESS_BITS;
private static final int ADDRESS_MASK = ADDRESS_SIZE - 1;
private static final int SET = 0;
private static final int AND = 1;
private static final int OR = 2;
private static final int XOR = 3;
private static final int EQUALS = 0;
private static final int EXCLUDES = 1;
private static final int CONTAINS = 2;
private static final int COMPLEMENTS = 3;
// static constructors
/**
* Creates a {@link BitVector} from a BigInteger
. The bits of
* the integer copied into the new bit vector. The size of the returned
* {@link BitVector} is bigInt.bitLength()
. Negative values are
* recorded using 2's complement encoding. No sign-bit is included in the
* returned {@link BitVector}.
*
* @param bigInt
* a big integer
* @return a {@link BitVector} initialized with the bits of the big integer
* @see #fromBigInteger(BigInteger, int)
* @see Bits#asStore(BigInteger)
*/
public static final BitVector fromBigInteger(BigInteger bigInt) {
if (bigInt == null) throw new IllegalArgumentException();
final int length = bigInt.bitLength();
return fromBigIntegerImpl(bigInt, length);
}
/**
* Creates a {@link BitVector} from a BigInteger
. The bits of
* the integer copied into the new bit vector. The size of the returned
* {@link BitVector} is the specified size
. If the size exceeds
* bigInt.bitLength()
then the most significant bits are padded
* with ones if the integer is negative and zeros otherwise. Negative values
* are recorded using 2's complement encoding.
*
* @param bigInt
* a big integer
* @param size
* the size of the {@link BitVector} to create
* @return a {@link BitVector} initialized with the bits of the big integer
* @see #fromBigInteger(BigInteger)
* @see Bits#asStore(BigInteger)
*/
public static BitVector fromBigInteger(BigInteger bigInt, int size) {
if (bigInt == null) throw new IllegalArgumentException();
if (size < 0) throw new IllegalArgumentException();
return fromBigIntegerImpl(bigInt, size);
}
/**
* Creates a {@link BitVector} from the bits of a byte array. The byte array
* is in big-endian order. If the specified size is less than the number of
* bits in the byte array, the most-significant bits are discarded. If the
* size exceeds the number of bits in the byte array, the most-significant
* bits of the {@link BitVector} are padded with zeros.
*
* @param bytes
* the bits in big-endian order
* @param size
* the size of the returned bit vector
* @return a bit vector containing the specified bits
* @see Bits#asStore(byte[])
*/
public static BitVector fromByteArray(byte[] bytes, int size) {
//TODO provide a more efficient implementation, perhaps based on Bits.asStore()
if (bytes == null) throw new IllegalArgumentException("null bytes");
if (size < 0) throw new IllegalArgumentException("negative size");
BigInteger bigInt = new BigInteger(1, bytes);
return fromBigIntegerImpl(bigInt, size);
}
private static BitVector fromBigIntegerImpl(BigInteger bigInt, int size) {
int length = bigInt.bitLength();
boolean neg = bigInt.signum() == -1;
if (neg) bigInt = bigInt.negate().subtract(BigInteger.ONE);
final BitVector vector = new BitVector(size);
final long[] bits = vector.bits;
long v = 0L;
int i = 0;
for (; i < size; i++) {
if (i < length ? bigInt.testBit(i) != neg : neg) {
v = (v >>> 1) | Long.MIN_VALUE;
} else {
v >>>= 1;
}
if ((i & ADDRESS_MASK) == ADDRESS_MASK) {
bits[i >> ADDRESS_BITS] = v;
v = 0;
}
}
if (v != 0L) bits[i >> ADDRESS_BITS] = v >>> (ADDRESS_SIZE - (i & ADDRESS_MASK));
return vector;
}
/**
* Creates a {@link BitVector} by copying the bits in a BitSet
.
* The size of the returned {@link BitVector} will equal the length of the
* supplied bitSet.
*
* @param bitSet
* a BitSet
* @return a bit vector containing the bits of the bit set.
* @see #fromBitSet(BitSet, int)
*/
public static BitVector fromBitSet(BitSet bitSet) {
if (bitSet == null) throw new IllegalArgumentException();
final int length = bitSet.length();
return fromBitSetImpl(bitSet, length, length);
}
/**
* Creates a {@link BitVector} by copying the bits in a BitSet
.
*
* @param bitSet
* a BitSet
* @param size
* the size of {@link BitVector} to create, in bits
* @return a bit vector containing the bits of the bit set.
* @see Bits#asStore(BitSet, int)
*/
public static BitVector fromBitSet(BitSet bitSet, int size) {
if (bitSet == null) throw new IllegalArgumentException();
if (size < 0) throw new IllegalArgumentException();
final int length = bitSet.length();
return fromBitSetImpl(bitSet, size, length);
}
private static BitVector fromBitSetImpl(BitSet bitSet, int size, int length) {
final BitVector vector = new BitVector(size);
final long[] bits = vector.bits;
long v = 0L;
int i = 0;
int limit = Math.min(size, length);
for (; i < limit; i++) {
if (bitSet.get(i)) {
v = (v >>> 1) | Long.MIN_VALUE;
} else {
v >>>= 1;
}
if ((i & ADDRESS_MASK) == ADDRESS_MASK) {
bits[i >> ADDRESS_BITS] = v;
v = 0;
}
}
if (v != 0L) bits[i >> ADDRESS_BITS] = v >>> (ADDRESS_SIZE - (i & ADDRESS_MASK));
return vector;
}
/**
* Creates a {@link BitVector} by copying another {@link BitStore}.
*
* @param store
* the bit store to be copied
* @return a new bit vector that equals the supplied store
*/
public static BitVector fromStore(BitStore store) {
if (store instanceof BitVector) return ((BitVector)store).mutableCopy();
if (store == null) throw new IllegalArgumentException("null store");
return new BitVector(store);
}
// static utility methods
//a, b not null a size not greater than b size
private static int compareNumeric(BitVector a, BitVector b) {
final int aSize = a.size();
final int bSize = b.size();
if (aSize != bSize && !b.isAllZerosAdj(b.finish - bSize + aSize, b.finish)) return -1;
// more optimizations are possible but probably not worthwhile
if (a.isAligned() && b.isAligned()) {
int pos = aSize & ~ADDRESS_MASK;
if (pos != aSize) {
int bits = aSize - pos;
long aBits = a.getBitsAdj(pos, bits);
long bBits = b.getBitsAdj(pos, bits);
if (aBits != bBits) {
return aBits < bBits ? -1 : 1;
}
}
final long[] aArr = a.bits;
final long[] bArr = b.bits;
for (int i = (pos >> ADDRESS_BITS) - 1; i >= 0; i--) {
long aBits = aArr[i];
long bBits = bArr[i];
if (aBits == bBits) continue;
boolean aNeg = aBits < 0L;
boolean bNeg = bBits < 0L;
if (bNeg && !aNeg) return -1;
if (aNeg && !bNeg) return 1;
return aBits < bBits ? -1 : 1;
}
return 0;
} else {
final int aStart = a.start;
final int bStart = b.start;
int offset;
for (offset = aSize - ADDRESS_SIZE; offset >= 0; offset -= ADDRESS_SIZE) {
long aBits = a.getBitsAdj(offset + aStart, ADDRESS_SIZE);
long bBits = b.getBitsAdj(offset + bStart, ADDRESS_SIZE);
if (aBits == bBits) continue;
boolean aNeg = aBits < 0L;
boolean bNeg = bBits < 0L;
if (bNeg && !aNeg) return -1;
if (aNeg && !bNeg) return 1;
return aBits < bBits ? -1 : 1;
}
if (offset != 0) {
long aBits = a.getBitsAdj(aStart, ADDRESS_SIZE + offset);
long bBits = b.getBitsAdj(bStart, ADDRESS_SIZE + offset);
if (aBits == bBits) return 0;
return aBits < bBits ? -1 : 1;
}
return 0;
}
}
//TODO should optimize
//a, b not null a size not greater than b size
private static int compareLexical(BitVector a, BitVector b) {
final int aSize = a.size();
final int bSize = b.size();
if (aSize == bSize) return compareNumeric(a, b); // more efficient
final int size = Math.min(aSize, bSize);
final int aStart = a.finish - size;
final int bStart = b.finish - size;
for (int i = size - 1; i >= 0; i--) {
boolean aBit = a.getBitAdj(aStart + i);
boolean bBit = b.getBitAdj(bStart + i);
if (aBit != bBit) return bBit ? -1 : 1;
}
return aSize < bSize ? -1 : 1;
}
private static boolean overlapping(int thisFrom, int thisTo, int thatFrom, int thatTo) {
return thisTo > thatFrom && thisFrom < thatTo;
}
//necessary for throwing an IAE
private static int stringLength(String str) {
if (str == null) throw new IllegalArgumentException();
return str.length();
}
// fields
private final int start;
private final int finish;
private final long[] bits;
private final boolean mutable;
// public constructors
//creates a new bit vector of the specified size
//naturally aligned
public BitVector(int size) {
if (size < 0) throw new IllegalArgumentException();
if (size > (Integer.MAX_VALUE / 8)) throw new IllegalArgumentException();
final int length = (size + ADDRESS_MASK) >> ADDRESS_BITS;
this.bits = new long[length];
this.start = 0;
this.finish = size;
this.mutable = true;
}
//TODO consider changing String constructors to static methods
//creates a new bit vector from the supplied binary string
//naturally aligned
public BitVector(String str) {
this(stringLength(str));
readFrom(new CharBitReader(str));
}
public BitVector(String str, int radix) {
this(new BigInteger(str, radix));
}
//TODO unit test
public BitVector(Random random, float probability, int size) {
this(size);
if (random == null) throw new IllegalArgumentException("null random");
if (probability < 0f) throw new IllegalArgumentException("negative probability");
if (probability > 1f) throw new IllegalArgumentException("probability exceeds one");
if (probability == 0f) {
// nothing to do
} else if (probability == 1f) {
for (int i = 0; i < bits.length; i++) {
bits[i] = -1L;
}
} else if (probability == 0.5f) {
for (int i = 0; i < bits.length; i++) {
bits[i] = random.nextLong();
}
} else {
for (int i = 0; i < bits.length; i++) {
long b = 0L;
for (int j = 0; j < 64; j++) {
b <<= 1;
if (random.nextFloat() < probability) b |= 1L;
}
bits[i] = b;
}
}
}
public BitVector(Random random, int size) {
this(random, 0.5f, size);
}
// private constructors
private BitVector(int start, int finish, long[] bits, boolean mutable) {
this.start = start;
this.finish = finish;
this.bits = bits;
this.mutable = mutable;
}
private BitVector(Serial serial) {
this(serial.start, serial.finish, serial.bits, serial.mutable);
}
//only called to support parsing with a different radix
private BitVector(BigInteger bigInt) {
this(bigInt.bitLength());
//TODO ideally trap this earlier
if (bigInt.signum() < 0) throw new IllegalArgumentException();
for (int i = 0; i < finish; i++) {
performSetAdj(i, bigInt.testBit(i));
}
}
private BitVector(BitStore store) {
this(store.size());
store.writeTo(new VectorWriter());
}
// fundamental methods
@Override
public int size() {
return finish - start;
}
@Override
public boolean getBit(int index) {
index = adjIndex(index);
final int i = index >> ADDRESS_BITS;
final long m = 1L << (index & ADDRESS_MASK);
return (bits[i] & m) != 0;
}
@Override
public void setBit(int index, boolean value) {
index = adjIndex(index);
checkMutable();
final int i = index >> ADDRESS_BITS;
final long m = 1L << (index & ADDRESS_MASK);
if (value) {
bits[i] |= m;
} else {
bits[i] &= ~m;
}
}
// accelerating methods
@Override
public long getBits(int position, int length) {
checkBitsLength(length);
return getBitsAdj(adjPosition(position, length), length);
}
// equivalent to xor().with(index, true)
@Override
public void flipBit(int index) {
index = adjIndex(index);
checkMutable();
final int i = index >> ADDRESS_BITS;
final long m = 1L << (index & ADDRESS_MASK);
bits[i] ^= m;
}
@Override
public boolean getThenSetBit(int index, boolean value) {
index = adjIndex(index);
checkMutable();
int i = index >> ADDRESS_BITS;
long m = 1L << (index & ADDRESS_MASK);
boolean previous = (bits[i] & m) != 0;
if (previous != value) bits[i] ^= m;
return previous;
}
@Override
public void setBits(int position, long value, int length) {
checkBitsLength(length);
position = adjPosition(position);
checkMutable();
if (length == 0) return;
int i = position >> ADDRESS_BITS;
int s = position & ADDRESS_MASK;
long m = length == ADDRESS_SIZE ? -1L : (1L << length) - 1L;
long v = value & m;
performAdjSet(length, i, s, m, v);
}
@Override
public void setStore(int position, BitStore store) {
if (store instanceof BitVector) {
perform(SET, position, (BitVector) store);
} else {
// store.writeTo(openWriter(SET, size() - position - store.size()));
// store.writeTo(openWriter(SET, size() - position - store.size(), size() - position));
store.writeTo(openWriter(SET, position, position + store.size()));
}
}
@Override
public void fill() {
checkMutable();
performAdjSet(start, finish);
}
@Override
public void clear() {
checkMutable();
performAdjClear(start, finish);
}
//named flip for consistency with BigInteger and BitSet
@Override
public void flip() {
checkMutable();
performAdjXor(start, finish);
}
// operations
@Override
public Op set() {
return new SetOp();
}
@Override
public Op and() {
return new AndOp();
}
@Override
public Op or() {
return new OrOp();
}
@Override
public Op xor() {
return new XorOp();
}
// shifting
@Override
public void shift(int distance, boolean fill) {
shiftAdj(start, finish, distance, fill);
}
// matching
@Override
public BitMatches ones() {
return new MatchesOnes();
}
@Override
public BitMatches zeros() {
return new MatchesZeros();
}
@Override
public Tests equals() {
return new VectorTests(EQUALS);
}
@Override
public Tests contains() {
return new VectorTests(CONTAINS);
}
@Override
public Tests excludes() {
return new VectorTests(EXCLUDES);
}
@Override
public Tests complements() {
return new VectorTests(COMPLEMENTS);
}
// I/O
@Override
public BitWriter openWriter(int finalPos, int initialPos) {
return openWriter(SET, finalPos, initialPos);
}
@Override
public BitReader openReader(int finalPos, int initialPos) {
return new VectorReader( adjPosition(finalPos), adjPosition(initialPos) );
}
@Override
public int writeTo(BitWriter writer) {
if (writer == null) throw new IllegalArgumentException("null writer");
int size = finish - start;
int count = 0;
if (size <= 64) {
count += writer.write(getBitsAdj(start, size), size);
} else {
int head = finish & ADDRESS_MASK;
if (head != 0) count += writer.write(getBitsAdj(finish - head, head), head);
final int f = (finish ) >> ADDRESS_BITS;
final int t = (start + 63) >> ADDRESS_BITS;
for (int i = f - 1; i >= t; i--) count += writer.write(bits[i], ADDRESS_SIZE);
int tail = 64 - (start & ADDRESS_MASK);
if (tail != 64) count += writer.write(getBitsAdj(start, tail), tail);
}
return count;
}
@Override
public void readFrom(BitReader reader) {
if (reader == null) throw new IllegalArgumentException("null reader");
int size = finish - start;
if (size <= 64) {
performAdj(SET, start, reader.readLong(size), size) ;
} else {
int head = finish & ADDRESS_MASK;
if (head != 0) performAdj(SET, finish - head, reader.readLong(head), head);
final int f = (finish ) >> ADDRESS_BITS;
final int t = (start + 63) >> ADDRESS_BITS;
for (int i = f - 1; i >= t; i--) bits[i] = reader.readLong(ADDRESS_SIZE);
int tail = 64 - (start & ADDRESS_MASK);
if (tail != 64) performAdj(SET, start, reader.readLong(tail), tail);
}
}
@Override
public void writeTo(WriteStream writer) {
//TODO could actually optimize under weaker condition that start is byte aligned
if ((start & ADDRESS_MASK) == 0L) {
int head = finish & ADDRESS_MASK;
int to = finish >> ADDRESS_BITS;
if (head != 0L) {
long mask = -1L >>> (ADDRESS_SIZE - head);
LongBitStore.writeBits(writer, bits[to] & mask, head);
}
int from = start >> ADDRESS_BITS;
for (int i = to - 1; i >= from; i--) {
writer.writeLong(bits[i]);
}
} else {
//TODO this can be optimized by doing the byte breakdown internally
BitStore.super.writeTo(writer);
}
}
@Override
public void readFrom(ReadStream reader) {
if (reader == null) throw new IllegalArgumentException("null reader");
checkMutable();
//TODO could actually optimize under weaker condition that start is byte aligned
if ((start & ADDRESS_MASK) == 0L) {
int head = finish & ADDRESS_MASK;
int to = finish >> ADDRESS_BITS;
if (head != 0L) {
long mask = -1L << head;
bits[to] = (bits[to] & mask) | (LongBitStore.readBits(reader, head) & ~mask);
}
int from = start >> ADDRESS_BITS;
for (int i = to - 1; i >= from; i--) {
bits[i] = reader.readLong();
}
} else {
//TODO what other optimizations?
BitStore.super.readFrom(reader);
}
}
// views
// returns a new bitvector that is backed by the same data as this one
// equivalent to: duplicate(from, to, false, isMutable());
// bypasses duplicate for efficiency
@Override
public BitVector range(int from, int to) {
if (from < 0) throw new IllegalArgumentException();
if (to < from) throw new IllegalArgumentException();
from += start;
to += start;
if (to > finish) throw new IllegalArgumentException();
return new BitVector(from, to, bits, mutable);
}
@Override
public Permutes permute() {
checkMutable();
return new VectorPermutes();
}
@Override
public byte[] toByteArray() {
//TODO can optimize when byte aligned
final int size = finish - start;
final int length = (size + 7) >> 3;
final byte[] bytes = new byte[length];
if (length == 0) return bytes;
if ((start & ADDRESS_MASK) == 0) { //long aligned case
int i = start >> ADDRESS_BITS;
int j = length; //how many bytes we have left to process
for (; j > 8; i++) {
final long l = bits[i];
bytes[--j] = (byte) ( l & 0xff);
bytes[--j] = (byte) ( (l >> 8) & 0xff);
bytes[--j] = (byte) ( (l >> 16) & 0xff);
bytes[--j] = (byte) ( (l >> 24) & 0xff);
bytes[--j] = (byte) ( (l >> 32) & 0xff);
bytes[--j] = (byte) ( (l >> 40) & 0xff);
bytes[--j] = (byte) ( (l >> 48) & 0xff);
bytes[--j] = (byte) ( (l >> 56) & 0xff);
}
if (j > 0) {
final long m = -1L >>> (ADDRESS_SIZE - finish & ADDRESS_MASK);
final long l = bits[i] & m;
for (int k = 0; j > 0; k++) {
bytes[--j] = (byte) ( (l >> (k*8)) & 0xff);
}
}
} else { //general case
//TODO indexing could probably be tidied up
int i = 0;
for (; i < length - 1; i++) {
bytes[length - 1 - i] = (byte) getBitsAdj(start + (i << 3), 8);
}
bytes[0] = (byte) getBitsAdj(start + (i << 3), size - (i << 3));
}
return bytes;
}
@Override
public BitSet toBitSet() {
final int size = finish - start;
final BitSet bitSet = new BitSet(size);
for (int i = 0; i < size; i++) {
bitSet.set(i, getBitAdj(i + start));
}
return bitSet;
}
@Override
public Number asNumber() {
return new VectorNumber();
}
public List asList() {
return new VectorList();
}
// mutability methods
@Override
public boolean isMutable() {
return mutable;
}
@Override
public BitVector mutable() {
return mutable ? this : mutableCopy();
}
@Override
public BitVector immutable() {
return mutable ? immutableView() : this;
}
@Override
public BitVector immutableView() {
return duplicate(false, false);
}
@Override
public BitVector immutableCopy() {
return duplicate(true, false);
}
@Override
public BitVector mutableCopy() {
return duplicate(true, true);
}
// comparable methods
@Override
public int compareNumericallyTo(BitStore that) {
if (that instanceof BitVector) {
if (this == that) return 0; // cheap check
BitVector v = (BitVector) that;
return this.size() < that.size() ? compareNumeric(this, v) : -compareNumeric(v, this);
}
return BitStore.super.compareNumericallyTo(that);
}
@Override
public int compareLexicallyTo(BitStore that) {
if (that instanceof BitVector) {
if (this == that) return 0; // cheap check
return compareLexical(this, (BitVector) that);
}
return BitStore.super.compareLexicallyTo(that);
}
// convenience methods
@Override
public void setAll(boolean value) {
checkMutable();
if (value) {
performAdjSet(start, finish);
} else {
performAdjClear(start, finish);
}
}
@Override
public Tests test(Test test) {
if (test == null) throw new IllegalArgumentException("null test");
return new VectorTests(test.ordinal());
}
@Override
public BitWriter openWriter() {
return new VectorWriter();
}
@Override
public BitReader openReader() {
return new VectorReader();
}
@Override
public BitVector rangeFrom(int from) {
return new BitVector(adjPosition(from), finish, bits, mutable);
}
@Override
public BitVector rangeTo(int to) {
return new BitVector(start, adjPosition(to), bits, mutable);
}
// alignment methods
@Override
public boolean isAligned() {
return start == 0;
}
//TODO consider adding a trimmed copy, or guarantee this is trimmed?
//only creates a new bit vector if necessary
@Override
public BitVector aligned() {
return start == 0 ? this : getVectorAdj(start, finish - start, mutable);
}
@Override
public BitVector alignedCopy() {
return getVectorAdj(start, finish - start, mutable);
}
// bit vector specific methods
/**
* Duplicates the {@link BitVector}.
*
* @param copy
* true if the duplicate should be a detached copy, false if it
* should be a view, backed by the same bit data.
* @param mutable
* whether the duplicate should be mutable
* @return a duplicate of this BitVector
* @see #duplicateRange(int, int, boolean, boolean)
*/
public BitVector duplicate(boolean copy, boolean mutable) {
if (mutable && !copy && !this.mutable) throw new IllegalStateException("Cannot obtain mutable view of an immutable BitVector");
return duplicateAdj(start, finish, copy, mutable);
}
/**
* Duplicates a range of the {@link BitVector}.
*
* @param from
* the (inclusive) position at which the range begins
* @param to
* the (exclusive) position at which the range ends
* @param copy
* true if the duplicated range should be a detached copy, false
* if it should be a view, backed by the same bit data.
* @param mutable
* whether the duplicated range should be mutable
* @return a duplicate of this BitVector
* @see #duplicate(boolean, boolean)
*/
public BitVector duplicateRange(int from, int to, boolean copy, boolean mutable) {
if (mutable && !copy && !this.mutable) throw new IllegalStateException("Cannot obtain mutable view of an immutable BitVector");
if (from < 0) throw new IllegalArgumentException();
if (to < from) throw new IllegalArgumentException();
from += start;
to += start;
if (to > finish) throw new IllegalArgumentException();
return duplicateAdj(from, to, copy, mutable);
}
/**
* Creates a mutable copy of this {@link BitVector} that may have a
* different size.
*
* @param newSize
* the size of the returned {@link BitVector}
* @param anchorLeft
* true if the most-significant bit of this {@link BitVector}
* remains the most-significant bit of the returned
* {@link BitVector}, false if the least-significant bit of this
* {@link BitVector} remains the least-significant bit of the
* returned {@link BitVector}.
* @return a resized mutable copy of this {@link BitVector}
*/
public BitVector resizedCopy(int newSize, boolean anchorLeft) {
if (newSize < 0) throw new IllegalArgumentException();
final int size = finish - start;
if (newSize == size) return duplicate(true, true);
int from;
int to;
if (anchorLeft) {
from = size - newSize;
to = size;
} else {
from = 0;
to = newSize;
}
if (newSize < size) return new BitVector(start + from, start + to, bits, mutable).duplicate(true, true);
final BitVector copy = new BitVector(newSize);
copy.perform(SET, -from, this);
return copy;
}
// object methods
public boolean equals(Object obj) {
if (obj == this) return true;
if (obj instanceof BitVector) {
BitVector that = (BitVector) obj;
if (this.finish - this.start != that.finish - that.start) return false;
return test(EQUALS, that);
}
if (obj instanceof BitStore) {
BitStore store = (BitStore) obj;
if (this.size() != store.size()) return false;
return equals().store(store);
}
return false;
}
@Override
public int hashCode() {
return Bits.bitStoreHasher().hash(this).intValue();
}
@Override
public String toString() {
final int size = finish - start;
switch (size) {
case 0 : return "";
case 1 : return getBitAdj(start) ? "1" : "0";
default :
StringBuilder sb = new StringBuilder(size);
for (int i = finish - 1; i >= start; i--) {
sb.append(getBitAdj(i) ? '1' : '0');
}
return sb.toString();
}
}
//shallow, externally identical to calling view();
@Override
public BitVector clone() {
try {
return (BitVector) super.clone();
} catch (CloneNotSupportedException e) {
//should never occur
throw new RuntimeException("Clone failure!", e);
}
}
// serialization
private Object writeReplace() throws ObjectStreamException {
return new Serial(this);
}
// package preserved methods
//NOTE: preserved for performance testing
void clear(int from, int to, boolean value) {
if (from < 0) throw new IllegalArgumentException();
if (from > to) throw new IllegalArgumentException();
checkMutable();
from += start;
to += start;
if (to > finish) throw new IllegalArgumentException();
if (value) {
performAdjSet(from, to);
} else {
performAdjClear(from, to);
}
}
//NOTE: preserved for performance testing
int countOnes(int from, int to) {
if (from < 0) throw new IllegalArgumentException();
if (from > to) throw new IllegalArgumentException();
from += start;
to += start;
if (to > finish) throw new IllegalArgumentException();
return countOnesAdj(from, to);
}
//NOTE: preserved for performance testing
int countZeros(int from, int to) {
return to - from - countOnes(from, to);
}
//NOTE: preserved for performance testing
int firstOneInRange(int from, int to) {
if (from < 0) throw new IllegalArgumentException();
if (to < from) throw new IllegalArgumentException();
from += start;
to += start;
if (to > finish) throw new IllegalArgumentException();
return firstOneInRangeAdj(from, to) - start;
}
//NOTE: preserved for performance testing
int firstZeroInRange(int from, int to) {
if (from < 0) throw new IllegalArgumentException();
if (to < from) throw new IllegalArgumentException();
from += start;
to += start;
if (to > finish) throw new IllegalArgumentException();
return firstZeroInRangeAdj(from, to) - start;
}
//NOTE: preserved for performance testing
int lastOneInRange(int from, int to) {
if (from < 0) throw new IllegalArgumentException();
if (to < from) throw new IllegalArgumentException();
from += start;
to += start;
if (to > finish) throw new IllegalArgumentException();
return lastOneInRangeAdj(from, to) - start;
}
//NOTE: preserved for performance testing
int lastZeroInRange(int from, int to) {
if (from < 0) throw new IllegalArgumentException();
if (to < from) throw new IllegalArgumentException();
from += start;
to += start;
if (to > finish) throw new IllegalArgumentException();
return lastZeroInRangeAdj(from, to) - start;
}
//NOTE: uncertain future
int[] toIntArray() {
final int size = finish - start;
final int length = (size + 31) >> 5;
final int[] ints = new int[length];
if (length == 0) return ints;
if ((start & ADDRESS_MASK) == 0) {
int i = start >> ADDRESS_BITS;
int j = length; // how many ints we have to process
for (; j > 2; i++) {
final long l = bits[i];
ints[--j] = (int) (l );
ints[--j] = (int) (l >> 32);
}
if (j > 0) {
final long m = -1L >>> (ADDRESS_SIZE - finish & ADDRESS_MASK);
final long l = bits[i] & m;
for (int k = 0; j > 0; k++) {
ints[--j] = (int) (l >> (k*32));
}
}
} else { // general case
int i = 0;
for (; i < length - 1; i++) {
ints[length - 1 - i] = (int) getBitsAdj(start + (i << 5), 32);
}
ints[0] = (int) getBitsAdj(start + (i << 5), size - (i << 5));
}
return ints;
}
//NOTE: uncertain future
long[] toLongArray() {
// create array through an aligned copy
BitVector copy = alignedCopy();
long[] longs = copy.bits;
int length = longs.length;
if (length == 0) return longs;
// reverse the array
for (int i = 0, mid = length >> 1, j = length - 1; i < mid; i++, j--) {
long t = longs[i];
longs[i] = longs[j];
longs[j] = t;
}
// mask off top bits in case copy was produced via clone
final long mask = -1L >>> (ADDRESS_SIZE - copy.finish & ADDRESS_MASK);
longs[0] &= mask;
// return the result
return longs;
}
//NOTE: preserved for performance testing
void writeTo(OutputStream out) throws IOException {
//TODO could optimize for aligned instances
final int size = finish - start;
final int length = (size + 7) >> 3;
if (length == 0) return;
int p = size & 7;
final int q = finish - p;
if (p != 0) out.write((byte) getBitsAdj(q, p));
p = q;
while (p > start) {
p -= 8;
out.write((byte) getBitsAdj(p, 8));
}
}
//NOTE: preserved for performance testing
void readFrom(InputStream in) throws IOException {
//TODO could optimize for aligned instances
final int size = finish - start;
final int length = (size + 7) >> 3;
if (length == 0) return;
int p = size & 7;
final int q = finish - p;
if (p != 0) performAdj(SET, q, (long) in.read(), p);
p = q;
while (p > start) {
p -= 8;
performAdj(SET, p, (long) in.read(), 8);
}
}
// private utility methods
private void checkMutable() {
if (!mutable) throw new IllegalStateException("immutable");
}
private int adjIndex(int index) {
if (index < 0) throw new IllegalArgumentException("negative index");
index += start;
if (index >= finish) throw new IllegalArgumentException("index beyond highest bit");
return index;
}
private int adjPosition(int position) {
if (position < 0) throw new IllegalArgumentException("negative position");
position += start;
if (position > finish) throw new IllegalArgumentException("position exceeds size");
return position;
}
private int adjPosition(int position, int length) {
if (position < 0) throw new IllegalArgumentException();
position += start;
if (position + length > finish) throw new IllegalArgumentException();
return position;
}
private void perform(int operation, int index, boolean value) {
performAdj(operation, adjIndex(index), value);
}
private boolean getThenPerform(int operation, int index, boolean value) {
return getThenPerformAdj(operation, adjIndex(index), value);
}
private void performAdj(int operation, int from, int to, boolean value) {
if (from == to) return; // nothing to do for an empty vector
//rationalize possible operations into SETs or INVERTs
switch (operation) {
case AND : if (!value) performAdjClear(from, to); return;
case OR : if ( value) performAdjSet(from, to); return;
case XOR : if ( value) performAdjXor(from, to); return;
case SET : if ( value) performAdjSet(from, to); else performAdjClear(from, to);
}
}
private void performAdjSet(int from, int to) {
if (from == to) return; // nothing to do for an empty vector
final int f = from >> ADDRESS_BITS;
final int t = (to-1) >> ADDRESS_BITS;
final long fm = -1L << (from - f * ADDRESS_SIZE);
final long tm = -1L >>> (t * ADDRESS_SIZE - to);
if (f == t) { // change falls into one element
bits[f] |= fm & tm;
return;
}
//process intermediate elements
Arrays.fill(bits, f+1, t, -1L);
//process terminals
bits[f] |= fm;
bits[t] |= tm;
}
private void performAdjClear(int from, int to) {
if (from == to) return; // nothing to do for an empty vector
final int f = from >> ADDRESS_BITS;
final int t = (to-1) >> ADDRESS_BITS;
final long fm = -1L << (from - f * ADDRESS_SIZE);
final long tm = -1L >>> (t * ADDRESS_SIZE - to);
if (f == t) { // change falls into one element
bits[f] &= ~(fm & tm);
return;
}
//process intermediate elements
Arrays.fill(bits, f+1, t, 0L);
//process terminals
bits[f] &= ~fm;
bits[t] &= ~tm;
}
private void performAdjXor(int from, int to) {
if (from == to) return; // nothing to do for an empty vector
final int f = from >> ADDRESS_BITS;
final int t = (to-1) >> ADDRESS_BITS;
final long fm = -1L << (from - f * ADDRESS_SIZE);
final long tm = -1L >>> (t * ADDRESS_SIZE - to);
if (f == t) { // change falls into one element
bits[f] ^= fm & tm;
return;
}
//process intermediate elements
for (int i = f+1; i < t; i++) bits[i] = ~bits[i];
//process terminals
bits[f] ^= fm;
bits[t] ^= tm;
}
//assumes address size is size of long
private void perform(int operation, int position, long bs, int length) {
checkBitsLength(length);
position = adjPosition(position);
checkMutable();
performAdj(operation, position, bs, length);
}
private void performAdj(int operation, int position, long bs, int length) {
if (length == 0) return;
int i = position >> ADDRESS_BITS;
int s = position & ADDRESS_MASK;
long m = length == ADDRESS_SIZE ? -1L : (1L << length) - 1L;
long v = bs & m;
switch(operation) {
case SET : performAdjSet(length, i, s, m, v); return;
case AND : performAdjAnd(length, i, s, m, v); return;
case OR : performAdjOr (length, i, s, m, v); return;
case XOR : performAdjXor(length, i, s, m, v); return;
}
}
private void performAdjSet(int length, int i, int s, long m, long v) {
if (s == 0) { // fast case, long-aligned
bits[i] = bits[i] & ~m | v;
} else if (s + length <= ADDRESS_SIZE) { //single long case
bits[i] = bits[i] & Long.rotateLeft(~m, s) | (v << s);
} else {
bits[i ] = bits[i ] & (-1L >>> ( ADDRESS_SIZE - s)) | (v << s );
bits[i+1] = bits[i+1] & (-1L << (length - ADDRESS_SIZE + s)) | (v >>> (ADDRESS_SIZE - s));
}
}
private void performAdjAnd(int length, int i, int s, long m, long v) {
if (s == 0) { // fast case, long-aligned
bits[i] &= v | ~m;
} else if (s + length <= ADDRESS_SIZE) { //single long case
bits[i] &= (v << s) | Long.rotateLeft(~m, s);
} else {
bits[i ] &= (v << s ) | (-1L >>> ( ADDRESS_SIZE - s));
bits[i+1] &= (v >>> (ADDRESS_SIZE - s)) | (-1L << (length - ADDRESS_SIZE + s));
}
}
private void performAdjOr(int length, int i, int s, long m, long v) {
if (s == 0) { // fast case, long-aligned
bits[i] |= v;
} else if (s + length <= ADDRESS_SIZE) { //single long case
bits[i] |= v << s;
} else {
bits[i ] |= (v << s );
bits[i+1] |= (v >>> (ADDRESS_SIZE - s));
}
}
private void performAdjXor(int length, int i, int s, long m, long v) {
if (s == 0) { // fast case, long-aligned
bits[i] ^= v;
} else if (s + length <= ADDRESS_SIZE) { //single long case
bits[i] ^= v << s;
} else {
bits[i ] ^= (v << s );
bits[i+1] ^= (v >>> (ADDRESS_SIZE - s));
}
}
private void perform(int operation, int position, BitVector that) {
if (that == null) throw new IllegalArgumentException("null vector");
position = adjPosition(position);
checkMutable();
performAdj(operation, position, that);
}
private void perform(int operation, BitStore store) {
if (this.size() != store.size()) throw new IllegalArgumentException("mismatched store size");
perform(operation, 0, store);
}
private void perform(int operation, int position, BitStore store) {
if (store instanceof BitVector) {
perform(operation, position, (BitVector) store);
return;
}
if (store == null) throw new IllegalArgumentException("null store");
checkMutable();
performAdj(operation, adjPosition(position), store);
}
private void perform(int operation, int position, byte[] bytes, int offset, int length) {
if (bytes == null) throw new IllegalArgumentException("null bytes");
if (offset < 0) throw new IllegalArgumentException("negative offset");
if (length == 0) return;
if (offset + length > (bytes.length << 3)) throw new IllegalArgumentException("length greater than number of bits in byte array");
performAdj(operation, adjPosition(position), bytes, offset, length);
}
private BitVector duplicateAdj(int from, int to, boolean copy, boolean mutable) {
if (copy) {
int f = from >> ADDRESS_BITS;
int t = (to + ADDRESS_MASK) >> ADDRESS_BITS;
from &= ADDRESS_MASK;
to -= f << ADDRESS_BITS;
return new BitVector(from, to, Arrays.copyOfRange(bits, f, t), mutable);
} else {
return new BitVector(from, to, bits, mutable);
}
}
private boolean test(final int test, final BitVector that) {
//trivial case
if (this.start == this.finish) return true;
//TODO worth optimizing for case where this == that?
//fully optimal case - both start at 0
//TODO can weaken this constraint - can optimize if their start are equal
if (this.start == 0 && that.start == 0) {
final long[] thisBits = this.bits;
final long[] thatBits = that.bits;
final int t = (finish-1) >> ADDRESS_BITS;
switch (test) {
case EQUALS :
for (int i = t-1; i >= 0; i--) {
if (thisBits[i] != thatBits[i]) return false;
}
break;
case EXCLUDES :
for (int i = t-1; i >= 0; i--) {
if ((thisBits[i] & thatBits[i]) != 0) return false;
}
break;
case CONTAINS :
for (int i = t-1; i >= 0; i--) {
final long bits = thisBits[i];
if ((bits | thatBits[i]) != bits) return false;
}
break;
case COMPLEMENTS :
for (int i = t-1; i >= 0; i--) {
if (~thisBits[i] != thatBits[i]) return false;
}
break;
default : throw new IllegalArgumentException("Unexpected comparison constant: " + test);
}
{
// same length & same start so same finish mask
final long m = -1L >>> (ADDRESS_SIZE - finish & ADDRESS_MASK);
final long thisB = thisBits[t] & m;
final long thatB = thatBits[t] & m;
switch (test) {
case EQUALS : return thisB == thatB;
case EXCLUDES : return (thisB & thatB) == 0;
case CONTAINS : return (thisB | thatB) == thisB;
case COMPLEMENTS : return (~thisB & m) == thatB;
default : throw new IllegalArgumentException("Unexpected comparison constant: " + test);
}
}
}
//TODO an additional optimization is possible when their starts differ by 64
//partially optimal case - both are address aligned
if ((this.start & ADDRESS_MASK) == 0 && (that.start & ADDRESS_MASK) == 0 && (this.finish & ADDRESS_MASK) == 0) {
final long[] thisBits = this.bits;
final long[] thatBits = that.bits;
final int f = this.start >> ADDRESS_BITS;
final int t = this.finish >> ADDRESS_BITS;
final int d = (that.start - this.start) >> ADDRESS_BITS;
switch (test) {
case EQUALS :
for (int i = f; i < t; i++) {
if (thisBits[i] != thatBits[i+d]) return false;
}
return true;
case EXCLUDES :
for (int i = f; i < t; i++) {
if ((thisBits[i] & thatBits[i+d]) == 0) return true;
}
return true;
case CONTAINS :
for (int i = f; i < t; i++) {
final long bits = thisBits[i];
if ((bits | thatBits[i+d]) != bits) return false;
}
return true;
case COMPLEMENTS :
for (int i = f; i < t; i++) {
if (~thisBits[i] != thatBits[i+d]) return false;
}
return true;
default : throw new IllegalArgumentException("Unexpected comparison constant: " + test);
}
}
//non-optimized case
//TODO could be optimized with long comparisions
final int size = finish - start;
switch (test) {
case EQUALS :
for (int i = 0; i < size; i++) {
if (that.getBitAdj(that.start + i) != this.getBitAdj(this.start + i)) return false;
}
return true;
case EXCLUDES :
for (int i = 0; i < size; i++) {
if (that.getBitAdj(that.start + i) && this.getBitAdj(this.start + i)) return false;
}
return true;
case CONTAINS :
for (int i = 0; i < size; i++) {
if (that.getBitAdj(that.start + i) && !this.getBitAdj(this.start + i)) return false;
}
return true;
case COMPLEMENTS :
for (int i = 0; i < size; i++) {
if (that.getBitAdj(that.start + i) == this.getBitAdj(this.start + i)) return false;
}
return true;
default : throw new IllegalArgumentException("Unexpected comparison constant: " + test);
}
}
// size cannot exceed 64
private boolean test(int test, long bits, int size) {
switch (size) {
case 0 : return true;
case 1 : return test(test, getBitAdj(start), (bits & 1L) != 0L);
case 64: return test(test, getBitsAdj(0, 64), bits);
default :
long m = -1L << size;
long value = getBitsAdj(0, size);
if (test == COMPLEMENTS) {
bits |= m;
} else {
bits &= ~m;
}
return test(test, value, bits);
}
}
private boolean test(int test, boolean a, boolean b) {
switch (test) {
case EQUALS : return a == b ;
case EXCLUDES : return !(a && b);
case CONTAINS : return a || !b ;
case COMPLEMENTS : return a != b ;
default : throw new IllegalArgumentException("Unexpected comparison constant: " + test);
}
}
private boolean test(int test, long a, long b) {
switch (test) {
case EQUALS : return a == b;
case EXCLUDES : return (a & b) == 0L;
case CONTAINS : return (a | ~b) == -1L;
case COMPLEMENTS : return a != b;
default : throw new IllegalArgumentException("Unexpected comparison constant: " + test);
}
}
private boolean getBitAdj(int position) {
final int i = position >> ADDRESS_BITS;
final long m = 1L << (position & ADDRESS_MASK);
return (bits[i] & m) != 0;
}
private long getBitsAdj(int position, int length) {
final int i = position >> ADDRESS_BITS;
if (i >= bits.length) return 0L; // may happen if position == finish
final int s = position & ADDRESS_MASK;
final long b;
if (s == 0) { // fast case, long-aligned
b = bits[i];
} else if (s + length <= ADDRESS_SIZE) { //single long case
b = bits[i] >>> s;
} else {
b = (bits[i] >>> s) | (bits[i+1] << (ADDRESS_SIZE - s));
}
return length == ADDRESS_SIZE ? b : b & ((1L << length) - 1);
}
private BitVector getVectorAdj(int position, int length, boolean mutable) {
final long[] newBits;
if (length == 0) {
newBits = new long[0];
} else {
final int from = position >> ADDRESS_BITS;
final int to = (position + length + ADDRESS_MASK) >> ADDRESS_BITS;
if ((position & ADDRESS_MASK) == 0) {
newBits = Arrays.copyOfRange(bits, from, to);
} else {
final int s = position & ADDRESS_MASK;
final int plen = to - from; // number of longs which need processing
final int alen = (length + ADDRESS_MASK) >> ADDRESS_BITS; // number of longs to return
newBits = new long[alen];
//do all but last bit
int j = from;
int i = 0;
for (; i < alen - 1; i++, j++) {
newBits[i] = (bits[j] >>> s) | (bits[j+1] << (ADDRESS_SIZE - s));
}
//do last bits as a special case
if (plen == alen) {
newBits[i] = bits[j] >>> s;
} else {
newBits[i] = (bits[j] >>> s) | (bits[j+1] << (ADDRESS_SIZE - s));
}
}
}
return new BitVector(0, length, newBits, mutable);
}
private int countOnesAdj(int from, int to) {
if (from == to) return 0;
final int f = from >> ADDRESS_BITS;
final int t = (to-1) >> ADDRESS_BITS;
final int r = from & ADDRESS_MASK;
final int l = to & ADDRESS_MASK;
if (f == t) {
//alternatively: (0x8000000000000000L >> (l - r - 1)) >>> (ADDRESS_SIZE - l);
final long m = (-1L >>> (ADDRESS_SIZE - l + r)) << r;
return Long.bitCount(m & bits[f]);
}
int count = 0;
count += Long.bitCount( (-1L << r) & bits[f] );
for (int i = f+1; i < t; i++) {
count += Long.bitCount(bits[i]);
}
count += Long.bitCount( (-1L >>> (ADDRESS_SIZE - l)) & bits[t] );
return count;
}
private boolean isAllOnesAdj(int from, int to) {
if (from == to) return true;
final int f = from >> ADDRESS_BITS;
final int t = (to-1) >> ADDRESS_BITS;
final long fm;
final long tm;
{
final int fs = from & ADDRESS_MASK;
final int ts = to & ADDRESS_MASK;
fm = fs == 0 ? 0 : -1L >>> (ADDRESS_SIZE - fs);
tm = ts == 0 ? 0 : -1L << ts;
}
if (f == t) { // bits fit into a single element
return (bits[f] | fm | tm) == -1L;
}
//check intermediate elements
for (int i = f+1; i < t; i++) if (bits[i] != -1L) return false;
//check terminals
return (bits[f] | fm) == -1L && (bits[t] | tm) == -1L;
}
private boolean isAllZerosAdj(int from, int to) {
if (from == to) return true;
final int f = from >> ADDRESS_BITS;
final int t = (to-1) >> ADDRESS_BITS;
final long fm;
final long tm;
{
final int fs = from & ADDRESS_MASK;
final int ts = to & ADDRESS_MASK;
fm = -1L << fs;
tm = -1L >>> (ADDRESS_SIZE - ts);
}
if (f == t) { // bits fit into a single element
return (bits[f] & fm & tm) == 0L;
}
//check intermediate elements
for (int i = f+1; i < t; i++) if (bits[i] != 0L) return false;
//check terminals
return (bits[f] & fm) == 0L && (bits[t] & tm) == 0L;
}
private void performAdj(int operation, int index, boolean value) {
checkMutable();
final int i = index >> ADDRESS_BITS;
final long m = 1L << (index & ADDRESS_MASK);
switch(operation) {
case SET :
if (value) {
bits[i] |= m;
} else {
bits[i] &= ~m;
}
break;
case AND :
if (value) {
/* no-op */
} else {
bits[i] &= ~m;
}
break;
case OR :
if (value) {
bits[i] |= m;
} else {
/* no-op */
}
break;
case XOR :
if (value) {
bits[i] ^= m;
} else {
/* no-op */
}
break;
}
}
//TODO really needs a more efficient implementation (see below for a failure)
private void performAdj(int operation, int position, byte[] bytes, int offset, int length) {
checkMutable();
final int to = (bytes.length << 3) - offset;
final int from = to - length;
position += length;
for (int i = from; i < to; i++) {
boolean b = (bytes[i >> 3] & (128 >> (i & 7))) != 0;
performAdj(operation, --position, b);
}
}
private void performAdj(int operation, int position, BitVector that) {
final int thatSize = that.size();
if (thatSize == 0) return;
if (this.bits == that.bits && overlapping(position, position + thatSize, that.start, that.finish)) that = that.immutableCopy();
if (thatSize <= ADDRESS_SIZE) {
performAdj(operation, position, that.getBitsAdj(that.start, thatSize), thatSize);
} else {
// TODO would like not to depend on writer here
// a direct implementation would be faster
that.writeTo( new VectorWriter(operation, position, position + thatSize) );
}
}
private void performAdj(int operation, int position, BitStore store) {
final int storeSize = store.size();
// note - can't defend against possibility of overlapping data backing here
// note 8 here is a heuristic
if (storeSize < 8) {
for (int i = 0; i < storeSize; i++) {
performAdj(operation, position++, store.getBit(i));
}
} else {
store.writeTo( new VectorWriter(operation, position, position + storeSize) );
}
}
//specialized implementation for the common case of setting an individual bit
private void performSetAdj(int position, boolean value) {
checkMutable();
final int i = position >> ADDRESS_BITS;
final long m = 1L << (position & ADDRESS_MASK);
if (value) {
bits[i] |= m;
} else {
bits[i] &= ~m;
}
}
//separate implementation from performAdj is an optimization
private boolean getThenPerformAdj(int operation, int position, boolean value) {
checkMutable();
final int i = position >> ADDRESS_BITS;
final long m = 1L << (position & ADDRESS_MASK);
final long v = bits[i] & m;
switch(operation) {
case SET :
if (value) {
bits[i] |= m;
} else {
bits[i] &= ~m;
}
break;
case AND :
if (value) {
/* no-op */
} else {
bits[i] &= ~m;
}
break;
case OR :
if (value) {
bits[i] |= m;
} else {
/* no-op */
}
break;
case XOR :
if (value) {
bits[i] ^= m;
} else {
/* no-op */
}
break;
}
return v != 0;
}
private void rotateAdj(int from, int to, int distance) {
final int length = to - from;
if (length < 2) return;
distance = distance % length;
if (distance < 0) distance += length;
if (distance == 0) return;
//TODO is this capable of optimization in some cases?
final int cycles = Bits.gcd(distance, length);
for (int i = from + cycles - 1; i >= from; i--) {
boolean m = getBitAdj(i); // the previously overwritten value
int j = i; // the index that is to be overwritten next
do {
j += distance;
if (j >= to) j -= length;
m = getThenPerformAdj(SET, j, m);
} while (j != i);
}
}
private void shiftAdj(int from, int to, int distance, boolean fill) {
checkMutable();
if (from == to) return;
if (distance == 0) return;
//TODO have separate methods for true/false fill?
//TODO this capable of optimization in some cases
if (distance > 0) {
int j = to - 1;
for (int i = j - distance; i >= from; i--, j--) {
performSetAdj(j, getBitAdj(i));
}
if (fill) {
performAdjSet(from, j + 1);
} else {
performAdjClear(from, j + 1);
}
} else {
int j = from;
for (int i = j - distance; i < to; i++, j++) {
performSetAdj(j, getBitAdj(i));
}
if (fill) {
performAdjSet(j, to);
} else {
performAdjClear(j, to);
}
}
}
private void reverseAdj(int from, int to) {
to--;
while (from < to) {
performSetAdj(to, getThenPerformAdj(SET, from, getBitAdj(to)));
from++; to--;
}
}
private void shuffleAdj(int from, int to, Random random) {
checkMutable();
int size = to - from;
int ones = countOnesAdj(from, to);
// simple case - all bits identical, nothing to do
if (ones == 0 || ones == size) return;
// relocate one-bits
//TODO could set multiple bits at once for better performance
for (int i = from; ones < size && ones > 0; size--) {
boolean one = random.nextInt(size) < ones;
performSetAdj(i++, one);
if (one) ones--;
}
// fill remaining definites
if (size > 0) {
if (ones > 0) {
performAdjSet(to - size, to);
} else {
performAdjClear(to - size, to);
}
}
}
//TODO can eliminate calls to getBitsAdj from these methods
private int firstOneInRangeAdj(int from, int to) {
// trivial case
if (from == to) return to;
final int size = to - from;
//simple case
if (size <= ADDRESS_SIZE) {
final int j = Long.numberOfTrailingZeros( getBitsAdj(from, size) );
return j >= size ? to : from + j;
}
int i = from;
// check head
int a = i & ADDRESS_MASK;
if (a != 0) {
final int s = ADDRESS_SIZE - a;
final int j = Long.numberOfTrailingZeros( getBitsAdj(i, s) );
if (j < s) return from + j;
i += s;
}
// check body
final int b = to & ADDRESS_MASK;
final int t = to - b;
while (i < t) {
final int j = Long.numberOfTrailingZeros( bits[i >> ADDRESS_BITS] );
if (j < ADDRESS_SIZE) return i + j;
i += ADDRESS_SIZE;
}
// check tail
if (b != 0) {
final int j = Long.numberOfTrailingZeros( getBitsAdj(t, b) );
return j >= b ? to : i + j;
}
// give up
return to;
}
private int firstZeroInRangeAdj(int from, int to) {
// trivial case
if (from == to) return to;
final int size = to - from;
//simple case
if (size <= ADDRESS_SIZE) {
final int j = Long.numberOfTrailingZeros( ~getBitsAdj(from, size) );
return j >= size ? to : from + j;
}
int i = from;
// check head
int a = i & ADDRESS_MASK;
if (a != 0) {
final int s = ADDRESS_SIZE - a;
final int j = Long.numberOfTrailingZeros( ~getBitsAdj(i, s) );
if (j < s) return from + j;
i += s;
}
// check body
final int b = to & ADDRESS_MASK;
final int t = to - b;
while (i < t) {
final int j = Long.numberOfTrailingZeros( ~bits[i >> ADDRESS_BITS] );
if (j < ADDRESS_SIZE) return i + j;
i += ADDRESS_SIZE;
}
// check tail
if (b != 0) {
final int j = Long.numberOfTrailingZeros( ~getBitsAdj(t, b) );
return j >= b ? to : i + j;
}
// give up
return to;
}
private int lastOneInRangeAdj(int from, int to) {
// trivial case
if (from == to) return start - 1;
final int size = to - from;
//simple case
if (size <= ADDRESS_SIZE) {
final int j = Long.numberOfLeadingZeros( getBitsAdj(from, size) << (ADDRESS_SIZE - size) );
return j == ADDRESS_SIZE ? start - 1 : to - (j + 1);
}
// check tail
final int b = to & ADDRESS_MASK;
int i = to - b;
if (b != 0) {
final int j = Long.numberOfLeadingZeros( getBitsAdj(i, b) << (ADDRESS_SIZE - b) );
if (j != ADDRESS_SIZE) return to - (j + 1);
}
// check body
final int a = from & ADDRESS_MASK;
final int f = from + (ADDRESS_SIZE - a);
while (i >= f) {
i -= ADDRESS_SIZE;
final int j = Long.numberOfLeadingZeros( bits[i >> ADDRESS_BITS] );
if (j != ADDRESS_SIZE) return i + ADDRESS_SIZE - (j + 1);
}
// check head
if (a != 0) {
final int s = ADDRESS_SIZE - a;
final int j = Long.numberOfLeadingZeros( getBitsAdj(from, s) << a );
if (j != ADDRESS_SIZE) return i - (j + 1);
}
// give up
return start - 1;
}
private int lastZeroInRangeAdj(int from, int to) {
// trivial case
if (from == to) return start - 1;
final int size = to - from;
//simple case
if (size <= ADDRESS_SIZE) {
final int j = Long.numberOfLeadingZeros( ~getBitsAdj(from, size) << (ADDRESS_SIZE - size) );
return j == ADDRESS_SIZE ? start - 1 : to - (j + 1);
}
// check tail
final int b = to & ADDRESS_MASK;
int i = to - b;
if (b != 0) {
final int j = Long.numberOfLeadingZeros( ~getBitsAdj(i, b) << (ADDRESS_SIZE - b) );
if (j != ADDRESS_SIZE) return to - (j + 1);
}
// check body
final int a = from & ADDRESS_MASK;
final int f = from + (ADDRESS_SIZE - a);
while (i >= f) {
i -= ADDRESS_SIZE;
final int j = Long.numberOfLeadingZeros( ~bits[i >> ADDRESS_BITS] );
if (j != ADDRESS_SIZE) return i + ADDRESS_SIZE - (j + 1);
}
// check head
if (a != 0) {
final int s = ADDRESS_SIZE - a;
final int j = Long.numberOfLeadingZeros( ~getBitsAdj(from, s) << a );
if (j != ADDRESS_SIZE) return i - (j + 1);
}
// give up
return start - 1;
}
private BitWriter openWriter(int operation, int finalPos, int initialPos) {
if (finalPos < 0) throw new IllegalArgumentException("negative finalPos");
finalPos += start;
initialPos += start;
if (initialPos < finalPos) throw new IllegalArgumentException("finalPos exceeds initialPos");
if (initialPos > finish) throw new IllegalArgumentException("initialPos too large");
return new VectorWriter(operation, finalPos, initialPos);
}
// collection methods
private IntSet asSet(boolean bit, int offset) {
return new IntSet(bit, offset);
}
private ListIterator listIterator() {
return new VectorIterator();
}
private ListIterator listIterator(int position) {
return new VectorIterator(adjPosition(position));
}
// inner classes
private final class VectorNumber extends Number {
private static final long serialVersionUID = 2471332225370258558L;
@Override
public byte byteValue() {
return (byte) getBitsAdj(start, Math.min(8, finish-start));
}
@Override
public short shortValue() {
return (short) getBitsAdj(start, Math.min(16, finish-start));
}
@Override
public int intValue() {
return (int) getBitsAdj(start, Math.min(32, finish-start));
}
@Override
public long longValue() {
return (long) getBitsAdj(start, Math.min(64, finish-start));
}
@Override
public float floatValue() {
return toBigInteger().floatValue();
}
@Override
public double doubleValue() {
return toBigInteger().doubleValue();
}
}
private final class SetOp implements Op {
@Override
public Operation getOperation() {
return Operation.SET;
}
@Override
public void with(boolean value) {
checkMutable();
performAdj(SET, start, finish, value);
}
@Override
public void withBit(int position, boolean value) {
perform(SET, position, value);
}
@Override
public boolean getThenWithBit(int position, boolean value) {
return getThenPerform(SET, position, value);
}
@Override
public void withByte(int position, byte value) {
perform(SET, position, value, 8);
}
@Override
public void withShort(int position, short value) {
perform(SET, position, value, 16);
}
@Override
public void withInt(int position, int value) {
perform(SET, position, value, 32);
}
@Override
public void withLong(int position, long value) {
perform(SET, position, value, 64);
}
@Override
public void withBits(int position, long value, int length) {
perform(SET, position, value, length);
}
@Override
public void withStore(BitStore store) {
perform(SET, store);
}
@Override
public void withStore(int position, BitStore store) {
perform(SET, position, store);
}
@Override
public void withBytes(int position, byte[] bytes, int offset, int length) {
perform(SET, position, bytes, offset, length);
}
@Override
public BitWriter openWriter(int finalPos, int initialPos) {
return BitVector.this.openWriter(SET, finalPos, initialPos);
}
}
private final class AndOp implements Op {
@Override
public Operation getOperation() {
return Operation.AND;
}
@Override
public void with(boolean value) {
checkMutable();
performAdj(AND, start, finish, value);
}
@Override
public void withBit(int position, boolean value) {
perform(AND, position, value);
}
@Override
public boolean getThenWithBit(int position, boolean value) {
return getThenPerform(AND, position, value);
}
@Override
public void withByte(int position, byte value) {
perform(AND, position, value, 8);
}
@Override
public void withShort(int position, short value) {
perform(AND, position, value, 16);
}
@Override
public void withInt(int position, int value) {
perform(AND, position, value, 32);
}
@Override
public void withLong(int position, long value) {
perform(AND, position, value, 64);
}
@Override
public void withBits(int position, long value, int length) {
perform(AND, position, value, length);
}
@Override
public void withStore(BitStore store) {
perform(AND, store);
}
@Override
public void withStore(int position, BitStore store) {
perform(AND, position, store);
}
@Override
public void withBytes(int position, byte[] bytes, int offset, int length) {
perform(AND, position, bytes, offset, length);
}
@Override
public BitWriter openWriter(int finalPos, int initialPos) {
return BitVector.this.openWriter(AND, finalPos, initialPos);
}
}
private final class OrOp implements Op {
@Override
public Operation getOperation() {
return Operation.OR;
}
@Override
public void with(boolean value) {
checkMutable();
performAdj(OR, start, finish, value);
}
@Override
public void withBit(int position, boolean value) {
perform(OR, position, value);
}
@Override
public boolean getThenWithBit(int position, boolean value) {
return getThenPerform(OR, position, value);
}
@Override
public void withByte(int position, byte value) {
perform(OR, position, value, 8);
}
@Override
public void withShort(int position, short value) {
perform(OR, position, value, 16);
}
@Override
public void withInt(int position, int value) {
perform(OR, position, value, 32);
}
@Override
public void withLong(int position, long value) {
perform(OR, position, value, 64);
}
@Override
public void withBits(int position, long value, int length) {
perform(OR, position, value, length);
}
@Override
public void withStore(BitStore store) {
perform(OR, store);
}
@Override
public void withStore(int position, BitStore store) {
perform(OR, position, store);
}
@Override
public void withBytes(int position, byte[] bytes, int offset, int length) {
perform(OR, position, bytes, offset, length);
}
@Override
public BitWriter openWriter(int finalPos, int initialPos) {
return BitVector.this.openWriter(OR, finalPos, initialPos);
}
}
private final class XorOp implements Op {
@Override
public Operation getOperation() {
return Operation.XOR;
}
@Override
public void with(boolean value) {
checkMutable();
performAdj(XOR, start, finish, value);
}
@Override
public void withBit(int position, boolean value) {
perform(XOR, position, value);
}
@Override
public boolean getThenWithBit(int position, boolean value) {
return getThenPerform(XOR, position, value);
}
@Override
public void withByte(int position, byte value) {
perform(XOR, position, value, 8);
}
@Override
public void withShort(int position, short value) {
perform(XOR, position, value, 16);
}
@Override
public void withInt(int position, int value) {
perform(XOR, position, value, 32);
}
@Override
public void withLong(int position, long value) {
perform(XOR, position, value, 64);
}
@Override
public void withBits(int position, long value, int length) {
perform(XOR, position, value, length);
}
@Override
public void withStore(BitStore store) {
perform(XOR, store);
}
@Override
public void withStore(int position, BitStore store) {
perform(XOR, position, store);
}
@Override
public void withBytes(int position, byte[] bytes, int offset, int length) {
perform(XOR, position, bytes, offset, length);
}
@Override
public BitWriter openWriter(int finalPos, int initialPos) {
return BitVector.this.openWriter(XOR, finalPos, initialPos);
}
}
private final class MatchesOnes extends AbstractBitMatches {
@Override
public boolean bit() {
return true;
}
@Override
public ImmutableOne sequence() {
return ImmutableOne.INSTANCE;
}
@Override
public BitMatches range(int from, int to) {
return BitVector.this.range(from, to).ones();
}
@Override
public BitVector store() {
return BitVector.this;
}
@Override
public boolean isAll() {
return isAllOnesAdj(start, finish);
}
@Override
public boolean isNone() {
return isAllZerosAdj(start, finish);
}
@Override
public int count() {
return countOnesAdj(start, finish);
}
@Override
public int first() {
return firstOneInRangeAdj(start, finish) - start;
}
@Override
public int last() {
return lastOneInRangeAdj(start, finish) - start;
}
@Override
public int next(int position) {
if (position < 0) throw new IllegalArgumentException();
position += start;
if (position > finish) throw new IllegalArgumentException();
return firstOneInRangeAdj(position, finish) - start;
}
public int previous(int position) {
if (position < 0) throw new IllegalArgumentException();
position += start;
if (position - 1 > finish) throw new IllegalArgumentException();
return lastOneInRangeAdj(start, position) - start;
}
public Positions positions() {
return new PositionIterator(true);
}
public Positions positions(int position) {
if (position < 0) throw new IllegalArgumentException();
position += start;
if (position > finish) throw new IllegalArgumentException();
return new PositionIterator(true, position);
}
@Override
public SortedSet asSet() {
return new IntSet(true, start);
}
}
private final class MatchesZeros extends AbstractBitMatches {
@Override
public boolean bit() {
return false;
}
@Override
public ImmutableZero sequence() {
return ImmutableZero.INSTANCE;
}
@Override
public BitVector store() {
return BitVector.this;
}
@Override
public BitMatches range(int from, int to) {
return BitVector.this.range(from, to).zeros();
}
@Override
public boolean isAll() {
return isAllZerosAdj(start, finish);
}
@Override
public boolean isNone() {
return isAllOnesAdj(start, finish);
}
@Override
public int count() {
return finish - start - countOnesAdj(start, finish);
}
@Override
public int first() {
return firstZeroInRangeAdj(start, finish) - start;
}
@Override
public int last() {
return lastZeroInRangeAdj(start, finish) - start;
}
@Override
public int next(int position) {
if (position < 0) throw new IllegalArgumentException();
position += start;
if (position > finish) throw new IllegalArgumentException();
return firstZeroInRangeAdj(position, finish) - start;
}
@Override
public int previous(int position) {
if (position < 0) throw new IllegalArgumentException();
position += start;
if (position - 1 > finish) throw new IllegalArgumentException();
return lastZeroInRangeAdj(start, position) - start;
}
@Override
public Positions positions() {
return new PositionIterator(false);
}
@Override
public Positions positions(int position) {
if (position < 0) throw new IllegalArgumentException();
position += start;
if (position > finish) throw new IllegalArgumentException();
return new PositionIterator(false, position);
}
@Override
public SortedSet asSet() {
return new IntSet(false, start);
}
}
private final class VectorTests implements Tests {
private final int test;
VectorTests(int test) {
this.test = test;
}
public Test getTest() {
return Test.values[test];
}
public boolean store(BitStore store) {
if (store instanceof BitVector) {
BitVector vector = (BitVector) store;
if (finish - start != vector.finish - vector.start) throw new IllegalArgumentException();
return test(test, vector);
}
//TODO needs to be optimized
switch (test) {
case EQUALS : return new BitStoreTests.Equals(BitVector.this).store(store);
case EXCLUDES : return new BitStoreTests.Excludes(BitVector.this).store(store);
case CONTAINS : return new BitStoreTests.Contains(BitVector.this).store(store);
case COMPLEMENTS : new BitStoreTests.Complements(BitVector.this).store(store);
default : throw new IllegalStateException();
}
}
public boolean bits(long bits) {
int size = finish - start;
if (size > 64) throw new IllegalArgumentException("size exceeds 64 bits");
return test(test, bits, size);
}
}
//TODO make public and expose more efficient methods?
private final class VectorIterator implements ListIterator {
private final int from;
private final int to;
// points to the element that will be returned by next
private int index;
private int recent = -1;
VectorIterator(int from, int to, int index) {
this.from = from;
this.to = to;
this.index = index;
}
VectorIterator(int index) {
this(start, finish, index);
}
VectorIterator() {
this(start, finish, start);
}
@Override
public boolean hasNext() {
return index < to;
}
@Override
public Boolean next() {
if (!hasNext()) throw new NoSuchElementException();
recent = index;
return Boolean.valueOf( getBitAdj(index++) );
}
@Override
public int nextIndex() {
return hasNext() ? index - start : finish - start;
}
@Override
public boolean hasPrevious() {
return index > from;
}
@Override
public Boolean previous() {
if (!hasPrevious()) throw new NoSuchElementException();
recent = --index;
return Boolean.valueOf( getBitAdj(recent) );
}
@Override
public int previousIndex() {
return hasPrevious() ? index - start - 1 : -1;
}
@Override
public void set(Boolean bit) {
if (recent == -1) throw new IllegalStateException();
performAdj(SET, recent, bit);
}
@Override
public void add(Boolean bit) {
throw new UnsupportedOperationException();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
private final class VectorPermutes implements Permutes {
@Override
public void transpose(int i, int j) {
}
@Override
public void rotate(int distance) {
rotateAdj(start, finish, distance);
}
@Override
public void reverse() {
reverseAdj(start, finish);
}
@Override
public void shuffle(Random random) {
if (random == null) throw new IllegalArgumentException("null random");
shuffleAdj(start, finish, random);
}
}
private final class PositionIterator implements Positions {
private static final int NOT_SET = Integer.MIN_VALUE;
private final boolean bit;
private final int from;
private final int to;
private int previous;
private int next;
private int nextIndex;
private int recent = NOT_SET;
PositionIterator(boolean bit, int from, int to, int position) {
this.bit = bit;
this.from = from;
this.to = to;
previous = lastInRange(from, position);
next = firstInRange(position, to);
nextIndex = previous == -1 ? 0 : NOT_SET;
}
PositionIterator(boolean bit, int index) {
this(bit, start, finish, index);
}
PositionIterator(boolean bit) {
this(bit, start, finish, start);
}
@Override
public boolean isDisjoint() {
return true;
}
@Override
public boolean hasPrevious() {
return previous != start - 1;
}
@Override
public boolean hasNext() {
return next != to;
}
@Override
public int previousPosition() {
if (previous == start - 1) return -1;
recent = previous;
next = recent;
previous = lastInRange(from, recent);
if (nextIndex != NOT_SET) nextIndex--;
return next - start;
}
@Override
public Integer previous() {
int position = previousPosition();
if (position == -1) throw new NoSuchElementException();
return position;
}
@Override
public int nextPosition() {
if (next == to) return to - start;
recent = next;
previous = recent;
next = firstInRange(recent + 1, to);
if (nextIndex != NOT_SET) nextIndex++;
return previous - start;
}
@Override
public void replace(BitStore replacement) {
if (replacement == null) throw new IllegalArgumentException("null replacement");
if (replacement.size() != 1) throw new IllegalArgumentException("replacement size does not match sequence size");
if (recent == NOT_SET) throw new IllegalStateException("no position");
if (replacement.getBit(0) != bit) flipBit(recent);
}
@Override
public void replace(boolean bits) {
if (recent == NOT_SET) throw new IllegalStateException("no position");
if (bits != bit) flipBit(recent);
}
@Override
public Integer next() {
int position = nextPosition();
if (position == to - start) throw new NoSuchElementException();
return position;
}
@Override
public int previousIndex() {
return nextIndex() - 1;
}
@Override
public int nextIndex() {
return nextIndex == NOT_SET ? nextIndex = countInRange(from, next) : nextIndex;
}
@Override
public void add(Integer e) {
doAdd(e);
recent = NOT_SET;
}
@Override
public void remove() {
doRemove();
recent = NOT_SET;
}
@Override
public void set(Integer e) {
doRemove();
doAdd(e);
recent = NOT_SET;
}
private void doAdd(Integer e) {
if (e == null) throw new IllegalArgumentException("null e");
int i = start + e;
if (i < start || i >= finish) throw new IllegalArgumentException("e out of bounds: [" + 0 + "," + (finish - start) + "]");
if (i < previous) throw new IllegalArgumentException("e less than previous value: " + (previous - start));
if (i >= next) throw new IllegalArgumentException("e not less than next value: " + (next - start));
boolean changed = bit != getThenPerformAdj(SET, i, bit);
if (changed) {
if (nextIndex != NOT_SET) nextIndex ++;
previous = i;
}
}
private void doRemove() {
if (recent == previous) { // we went forward
previous = lastInRange(from, recent);
if (nextIndex != NOT_SET) nextIndex --;
} else if (recent == next) { // we went backwards
next = firstInRange(recent + 1, to);
} else { // no recent value
throw new IllegalStateException();
}
performAdj(SET, recent, !bit);
}
private int lastInRange(int from, int to) {
return bit ? lastOneInRangeAdj(from, to) : lastZeroInRangeAdj(from, to);
}
private int firstInRange(int from, int to) {
return bit ? firstOneInRangeAdj(from, to) : firstZeroInRangeAdj(from, to);
}
private int countInRange(int from, int to) {
int count = countOnesAdj(from, next);
return bit ? count : next - from - count;
}
}
private final class VectorList extends AbstractList {
@Override
public boolean isEmpty() {
return BitVector.this.size() == 0;
}
@Override
public int size() {
return BitVector.this.size();
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Boolean)) return false;
int count = countOnesAdj(start, finish);
return (Boolean)o ?
// check for ones
count > 0 :
// check for zeros
finish - start - count > 0;
}
@Override
public boolean containsAll(Collection> c) {
boolean ones = c.contains(Boolean.TRUE);
boolean zeros = c.contains(Boolean.FALSE);
int bools = 0;
if (ones) bools++;
if (zeros) bools++;
if (c.size() > bools) return false; // must contain a non-boolean
switch (bools) {
case 0: return true; // empty collection
case 1: return !match(zeros).isAll();
default:
int count = countOnesAdj(start, finish);
return count > 0 && (finish - start - count) > 0;
}
}
@Override
public Boolean get(int index) {
return getBit(index);
}
@Override
public Iterator iterator() {
return BitVector.this.listIterator();
}
@Override
public ListIterator listIterator() {
return BitVector.this.listIterator();
}
@Override
public ListIterator listIterator(int index) {
return BitVector.this.listIterator(index);
}
@Override
public int indexOf(Object object) {
if (!(object instanceof Boolean)) return -1;
boolean bit = (Boolean) object;
int position = bit ?
firstOneInRangeAdj(start, finish) :
firstZeroInRangeAdj(start, finish);
return position == finish ? -1 : position - start;
}
@Override
public int lastIndexOf(Object object) {
if (!(object instanceof Boolean)) return -1;
boolean bit = (Boolean) object;
int position = bit ?
lastOneInRangeAdj(start, finish) :
lastZeroInRangeAdj(start, finish);
return position - start;
}
@Override
public Boolean set(int index, Boolean element) {
boolean b = element;
return getThenSetBit(index, b) != b;
}
@Override
public List subList(int fromIndex, int toIndex) {
return range(fromIndex, toIndex).asList();
}
}
private final class IntSet extends AbstractSet implements SortedSet {
private final boolean bit;
private final int offset; // the value that must be added to received values to map them onto the bits
// constructors
private IntSet(boolean bit, int offset) {
this.bit = bit;
this.offset = offset;
}
// set methods
@Override
public int size() {
return countOnesAdj(start, finish);
}
@Override
public boolean isEmpty() {
return isAllZerosAdj(start, finish);
}
@Override
public boolean contains(Object o) {
if (!(o instanceof Integer)) return false;
int position = offset + (Integer) o;
return position >= start && position < finish && getBitAdj(position) == bit;
}
@Override
public boolean add(Integer e) {
return getThenPerformAdj(SET, position(e), bit) != bit;
}
@Override
public boolean remove(Object o) {
if (!(o instanceof Integer)) return false;
int i = offset + (Integer) o;
if (i < start || i >= finish) return false;
return getThenSetBit(i, bit) == bit;
}
@Override
public void clear() {
checkMutable();
if (bit) {
performAdjClear(start, finish);
} else {
performAdjSet(start, finish);
}
}
@Override
public Iterator iterator() {
return new Iterator() {
final Iterator it = match(bit).positions();
@Override
public boolean hasNext() {
return it.hasNext();
}
@Override
public Integer next() {
//TODO build offset into positionIterator?
return it.next() + start - offset;
}
@Override
public void remove() {
it.remove();
}
};
}
@Override
public boolean containsAll(Collection> c) {
//TODO optimize
// if (c instanceof IntSet) {
// }
return super.containsAll(c);
}
@Override
public boolean addAll(Collection extends Integer> c) {
//TODO optimize
// if (c instanceof IntSet) {
// }
for (Integer e : c) position(e);
Iterator extends Integer> it = c.iterator();
boolean changed = false;
while (!changed && it.hasNext()) {
changed = getThenPerformAdj(SET, it.next() + offset, bit) != bit;
}
while (it.hasNext()) {
performAdj(SET, it.next() + offset, bit);
}
return changed;
}
@Override
public boolean removeAll(Collection> c) {
//TODO optimize
// if (c instanceof IntSet) {
// }
return super.removeAll(c);
}
@Override
public boolean retainAll(Collection> c) {
//TODO optimize
// if (c instanceof IntSet) {
// }
return super.retainAll(c);
}
// sorted set methods
@Override
public Comparator super Integer> comparator() {
return null;
}
@Override
public Integer first() {
int i = firstOneInRangeAdj(start, finish);
if (i == finish) throw new NoSuchElementException();
return i - offset;
}
@Override
public Integer last() {
int i = lastOneInRangeAdj(start, finish);
if (i == start - 1) throw new NoSuchElementException();
return i - offset;
}
@Override
public SortedSet headSet(Integer toElement) {
if (toElement == null) throw new NullPointerException();
int to = Math.max(toElement + offset, start);
return duplicateAdj(start, to, false, mutable).asSet(bit, offset);
}
@Override
public SortedSet tailSet(Integer fromElement) {
if (fromElement == null) throw new NullPointerException();
int from = Math.min(fromElement + offset, finish);
return duplicateAdj(from, finish, false, mutable).asSet(bit, offset);
}
@Override
public SortedSet subSet(Integer fromElement, Integer toElement) {
if (fromElement == null) throw new NullPointerException();
if (toElement == null) throw new NullPointerException();
int fromInt = fromElement;
int toInt = toElement;
if (fromInt > toInt) throw new IllegalArgumentException("from exceeds to");
int from = Math.min(fromInt + offset, finish);
int to = Math.max(toInt + offset, start);
return duplicateAdj(from, to, false, mutable).asSet(bit, offset);
}
// object methods
@Override
public boolean equals(Object o) {
//TODO optimize
// if (c instanceof IntSet) {
// }
return super.equals(o);
}
// private methods
private int position(Integer e) {
if (e == null) throw new NullPointerException("null value");
int i = e + offset;
if (i < start) throw new IllegalArgumentException("value less than lower bound");
if (i >= finish) throw new IllegalArgumentException("value greater than upper bound");
return i;
}
}
private static class Serial implements Serializable {
private static final long serialVersionUID = -1476938830216828886L;
private int start;
private int finish;
private long[] bits;
private boolean mutable;
Serial(BitVector v) {
start = v.start;
finish = v.finish;
bits = v.bits;
mutable = v.mutable;
}
private Object readResolve() throws ObjectStreamException {
return new BitVector(this);
}
}
// classes for reading and writing bits
private abstract class VectorStream implements BitStream {
final long initialPos;
final long finalPos;
int position;
private VectorStream(int finalPos, int initialPos) {
this.finalPos = finalPos;
this.initialPos = initialPos;
position = initialPos;
}
@Override
public long getPosition() {
return initialPos - position;
}
@Override
public long setPosition(long position) {
position = Math.max(position, finalPos - start);
position = Math.min(position, initialPos - start);
this.position = (int) (initialPos - position);
return position;
}
@Override
public long skipBits(long count) {
long advance = count < 0 ?
Math.max(position - initialPos, count):
Math.min(position - finalPos, count);
position -= (int) advance;
return advance;
}
}
private final class VectorReader extends VectorStream implements BitReader {
private VectorReader(int finalPos, int initialPos) {
super(finalPos, initialPos);
}
private VectorReader() {
this(start, finish);
}
@Override
public int readBit() {
if (position == finalPos) throw new EndOfBitStreamException();
return getBitAdj(--position) ? 1 : 0;
}
@Override
public int read(int count) {
if (count < 0) throw new IllegalArgumentException("negative count");
if (count > 32) throw new IllegalArgumentException("count too great");
if (count == 0) return 0;
if (position - count < finalPos) throw new EndOfBitStreamException();
return (int) getBitsAdj(position -= count, count);
}
@Override
public long readLong(int count) {
if (count < 0) throw new IllegalArgumentException("negative count");
if (count > 64) throw new IllegalArgumentException("count too great");
if (count == 0) return 0L;
if (position - count < finalPos) throw new EndOfBitStreamException();
return getBitsAdj(position -= count, count);
}
@Override
public BigInteger readBigInt(int count) throws BitStreamException {
if (position - count < finalPos) throw new EndOfBitStreamException();
switch(count) {
case 0 : return BigInteger.ZERO;
case 1 : return getBitAdj(position--) ? BigInteger.ONE : BigInteger.ZERO;
default :
final int from = position - count;
final int to = position;
position = from;
return duplicateAdj(from, to, false, false).toBigInteger();
}
}
@Override
public boolean readBoolean() {
if (position == finalPos) throw new EndOfBitStreamException();
return getBitAdj(--position);
}
@Override
public int readUntil(boolean one) throws BitStreamException {
int index = one ? lastOneInRangeAdj(start, position) : lastZeroInRangeAdj(start, position);
if (index < finalPos) throw new EndOfBitStreamException();
int read = position - index - 1;
position = index;
return read;
}
}
private final class VectorWriter extends VectorStream implements BitWriter {
private final int operation;
private VectorWriter(int operation, int finalPos, int initialPos) {
super(finalPos, initialPos);
this.operation = operation;
}
private VectorWriter() {
this(SET, start, finish);
}
@Override
public int writeBit(int bit) {
if (position == finalPos) throw new EndOfBitStreamException();
//TODO consider an optimized version of this
performAdj(operation, --position, (bit & 1) == 1);
return 1;
}
@Override
public int writeBoolean(boolean bit) {
if (position == finalPos) throw new EndOfBitStreamException();
performAdj(operation, --position, bit);
return 1;
}
@Override
public long writeBooleans(boolean value, long count) {
//TODO need to guard against overflow?
if (position - count < finalPos) throw new EndOfBitStreamException();
int from = position - (int) count;
performAdj(operation, from, position, value);
position = from;
return count;
}
@Override
public int write(int bits, int count) {
if (count < 0) throw new IllegalArgumentException("negative count");
if (count > 32) throw new IllegalArgumentException("count too great");
if (count == 0) return 0;
if (position - count < finalPos) throw new EndOfBitStreamException();
performAdj(operation, position -= count, bits, count);
return count;
}
@Override
public int write(long bits, int count) {
if (count < 0) throw new IllegalArgumentException("negative count");
if (count > 64) throw new IllegalArgumentException("count too great");
if (count == 0) return 0;
if (position - count < finalPos) throw new EndOfBitStreamException();
performAdj(operation, position -= count, bits, count);
return count;
}
@Override
public int write(BigInteger bits, int count) {
if (bits == null) throw new IllegalArgumentException("null bits");
if (count < 0) throw new IllegalArgumentException("negative count");
if (count == 0) return 0;
if (count <= 64) {
performAdj(operation, position -= count, bits.longValue(), count);
} else {
for (int i = count - 1; i >= 0; i--) {
performAdj(operation, position--, bits.testBit(i));
}
}
return count;
}
}
}