org.roaringbitmap.BitmapContainer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of RoaringBitmap Show documentation
Show all versions of RoaringBitmap Show documentation
Roaring bitmaps are compressed bitmaps (also called bitsets) which tend to outperform
conventional compressed bitmaps such as WAH or Concise.
/*
* (c) the authors Licensed under the Apache License, Version 2.0.
*/
package org.roaringbitmap;
import org.roaringbitmap.buffer.MappeableBitmapContainer;
import org.roaringbitmap.buffer.MappeableContainer;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.LongBuffer;
import java.util.Arrays;
import java.util.Iterator;
import static java.lang.Long.bitCount;
import static java.lang.Long.numberOfTrailingZeros;
/**
* Simple bitset-like container.
*/
public final class BitmapContainer extends Container implements Cloneable {
public static final int MAX_CAPACITY = 1 << 16;
private static final long serialVersionUID = 2L;
// bail out early when the number of runs is excessive, without
// an exact count (just a decent lower bound)
private static final int BLOCKSIZE = 128;
// 64 words can have max 32 runs per word, max 2k runs
/**
* optimization flag: whether the cardinality of the bitmaps is maintained through branchless
* operations
*/
private static final boolean USE_BRANCHLESS = true;
/**
* Return a bitmap iterator over this array
*
* @param bitmap array to be iterated over
* @return an iterator
*/
public static CharIterator getReverseShortIterator(long[] bitmap) {
return new ReverseBitmapContainerCharIterator(bitmap);
}
/**
* Return a bitmap iterator over this array
*
* @param bitmap array to be iterated over
* @return an iterator
*/
public static PeekableCharIterator getShortIterator(long[] bitmap) {
return new BitmapContainerCharIterator(bitmap);
}
// the parameter is for overloading and symmetry with ArrayContainer
protected static int serializedSizeInBytes(int unusedCardinality) {
return MAX_CAPACITY / 8;
}
final long[] bitmap;
int cardinality;
// nruns value for which RunContainer.serializedSizeInBytes ==
// BitmapContainer.getArraySizeInBytes()
private final int MAXRUNS = (getArraySizeInBytes() - 2) / 4;
/**
* Create a bitmap container with all bits set to false
*/
public BitmapContainer() {
this.cardinality = 0;
this.bitmap = new long[MAX_CAPACITY / 64];
}
/**
* Create a bitmap container with a run of ones from firstOfRun to lastOfRun. Caller must ensure
* that the range isn't so small that an ArrayContainer should have been created instead
*
* @param firstOfRun first index
* @param lastOfRun last index (range is exclusive)
*/
public BitmapContainer(final int firstOfRun, final int lastOfRun) {
this.cardinality = lastOfRun - firstOfRun;
this.bitmap = new long[MAX_CAPACITY / 64];
Util.setBitmapRange(bitmap, firstOfRun, lastOfRun);
}
private BitmapContainer(int newCardinality, long[] newBitmap) {
this.cardinality = newCardinality;
this.bitmap = Arrays.copyOf(newBitmap, newBitmap.length);
}
/**
* Create a new container, no copy is made.
*
* @param newBitmap content
* @param newCardinality desired cardinality.
*/
public BitmapContainer(long[] newBitmap, int newCardinality) {
this.cardinality = newCardinality;
this.bitmap = newBitmap;
}
/**
* Creates a new non-mappeable container from a mappeable one. This copies the data.
*
* @param bc the original container
*/
public BitmapContainer(MappeableBitmapContainer bc) {
this.cardinality = bc.getCardinality();
this.bitmap = bc.toLongArray();
}
@Override
public Container add(int begin, int end) {
// TODO: may need to convert to a RunContainer
if (end == begin) {
return clone();
}
if ((begin > end) || (end > (1 << 16))) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
BitmapContainer answer = clone();
int prevOnesInRange = answer.cardinalityInRange(begin, end);
Util.setBitmapRange(answer.bitmap, begin, end);
answer.updateCardinality(prevOnesInRange, end - begin);
return answer;
}
@Override
public Container add(final char i) {
final long previous = bitmap[i >>> 6];
long newval = previous | (1L << i);
bitmap[i >>> 6] = newval;
if (USE_BRANCHLESS) {
cardinality += (int)((previous ^ newval) >>> i);
} else if (previous != newval) {
++cardinality;
}
return this;
}
@Override
public ArrayContainer and(final ArrayContainer value2) {
final ArrayContainer answer = new ArrayContainer(value2.content.length);
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
char v = value2.content[k];
answer.content[answer.cardinality] = v;
answer.cardinality += (int)this.bitValue(v);
}
return answer;
}
@Override
public Container and(final BitmapContainer value2) {
int newCardinality = andCardinality(value2);
if (newCardinality > ArrayContainer.DEFAULT_MAX_SIZE) {
final BitmapContainer answer = new BitmapContainer();
for (int k = 0; k < answer.bitmap.length; ++k) {
answer.bitmap[k] = this.bitmap[k] & value2.bitmap[k];
}
answer.cardinality = newCardinality;
return answer;
}
ArrayContainer ac = new ArrayContainer(newCardinality);
Util.fillArrayAND(ac.content, this.bitmap, value2.bitmap);
ac.cardinality = newCardinality;
return ac;
}
@Override
public Container and(RunContainer x) {
return x.and(this);
}
@Override
public int andCardinality(final ArrayContainer value2) {
int answer = 0;
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
char v = value2.content[k];
answer += (int)this.bitValue(v);
}
return answer;
}
@Override
public int andCardinality(final BitmapContainer value2) {
int newCardinality = 0;
for (int k = 0; k < this.bitmap.length; ++k) {
newCardinality += Long.bitCount(this.bitmap[k] & value2.bitmap[k]);
}
return newCardinality;
}
@Override
public int andCardinality(RunContainer x) {
return x.andCardinality(this);
}
@Override
public Container andNot(final ArrayContainer value2) {
final BitmapContainer answer = clone();
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
char v = value2.content[k];
final int i = (v) >>> 6;
long w = answer.bitmap[i];
long aft = w & (~(1L << v));
answer.bitmap[i] = aft;
answer.cardinality -= (w ^ aft) >>> v;
}
if (answer.cardinality <= ArrayContainer.DEFAULT_MAX_SIZE) {
return answer.toArrayContainer();
}
return answer;
}
@Override
public Container andNot(final BitmapContainer value2) {
int newCardinality = 0;
for (int k = 0; k < this.bitmap.length; ++k) {
newCardinality += Long.bitCount(this.bitmap[k] & (~value2.bitmap[k]));
}
if (newCardinality > ArrayContainer.DEFAULT_MAX_SIZE) {
final BitmapContainer answer = new BitmapContainer();
for (int k = 0; k < answer.bitmap.length; ++k) {
answer.bitmap[k] = this.bitmap[k] & (~value2.bitmap[k]);
}
answer.cardinality = newCardinality;
return answer;
}
ArrayContainer ac = new ArrayContainer(newCardinality);
Util.fillArrayANDNOT(ac.content, this.bitmap, value2.bitmap);
ac.cardinality = newCardinality;
return ac;
}
@Override
public Container andNot(RunContainer x) {
// could be rewritten as return andNot(x.toBitmapOrArrayContainer());
BitmapContainer answer = this.clone();
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int start = (x.getValue(rlepos));
int end = start + (x.getLength(rlepos)) + 1;
int prevOnesInRange = answer.cardinalityInRange(start, end);
Util.resetBitmapRange(answer.bitmap, start, end);
answer.updateCardinality(prevOnesInRange, 0);
}
if (answer.getCardinality() > ArrayContainer.DEFAULT_MAX_SIZE) {
return answer;
} else {
return answer.toArrayContainer();
}
}
@Override
public void clear() {
if (cardinality != 0) {
cardinality = 0;
Arrays.fill(bitmap, 0);
}
}
@Override
public BitmapContainer clone() {
return new BitmapContainer(this.cardinality, this.bitmap);
}
@Override
public boolean isEmpty() {
return cardinality == 0;
}
/**
* Recomputes the cardinality of the bitmap.
*/
void computeCardinality() {
this.cardinality = 0;
for (int k = 0; k < this.bitmap.length; k++) {
this.cardinality += Long.bitCount(this.bitmap[k]);
}
}
int cardinalityInRange(int start, int end) {
if (cardinality != -1 && end - start > MAX_CAPACITY / 2) {
int before = Util.cardinalityInBitmapRange(bitmap, 0, start);
int after = Util.cardinalityInBitmapRange(bitmap, end, MAX_CAPACITY);
return cardinality - before - after;
}
return Util.cardinalityInBitmapRange(bitmap, start, end);
}
void updateCardinality(int prevOnes, int newOnes) {
int oldCardinality = this.cardinality;
this.cardinality = oldCardinality - prevOnes + newOnes;
}
@Override
public boolean contains(final char i) {
return (bitmap[i >>> 6] & (1L << i)) != 0;
}
@Override
public boolean contains(int minimum, int supremum) {
int start = minimum >>> 6;
int end = supremum >>> 6;
long first = -(1L << minimum);
long last = ((1L << supremum) - 1);
if (start == end) {
return ((bitmap[end] & first & last) == (first & last));
}
if ((bitmap[start] & first) != first) {
return false;
}
if (end < bitmap.length && (bitmap[end] & last) != last) {
return false;
}
for (int i = start + 1; i < bitmap.length && i < end; ++i) {
if (bitmap[i] != -1L) {
return false;
}
}
return true;
}
@Override
protected boolean contains(BitmapContainer bitmapContainer) {
if((cardinality != -1) && (bitmapContainer.cardinality != -1)) {
if(cardinality < bitmapContainer.cardinality) {
return false;
}
}
for(int i = 0; i < bitmapContainer.bitmap.length; ++i ) {
if((this.bitmap[i] & bitmapContainer.bitmap[i]) != bitmapContainer.bitmap[i]) {
return false;
}
}
return true;
}
@Override
protected boolean contains(RunContainer runContainer) {
final int runCardinality = runContainer.getCardinality();
if (cardinality != -1) {
if (cardinality < runCardinality) {
return false;
}
} else {
int card = cardinality;
if (card < runCardinality) {
return false;
}
}
for (int i = 0; i < runContainer.numberOfRuns(); ++i) {
int start = (runContainer.getValue(i));
int length = (runContainer.getLength(i));
if (!contains(start, start + length)) {
return false;
}
}
return true;
}
@Override
protected boolean contains(ArrayContainer arrayContainer) {
if (arrayContainer.cardinality != -1) {
if (cardinality < arrayContainer.cardinality) {
return false;
}
}
for (int i = 0; i < arrayContainer.cardinality; ++i) {
if(!contains(arrayContainer.content[i])) {
return false;
}
}
return true;
}
int bitValue(final char i) {
return (int)(bitmap[i >>> 6] >>> i ) & 1;
}
@Override
public void deserialize(DataInput in) throws IOException {
// little endian
this.cardinality = 0;
for (int k = 0; k < bitmap.length; ++k) {
long w = Long.reverseBytes(in.readLong());
bitmap[k] = w;
this.cardinality += Long.bitCount(w);
}
}
@Override
public boolean equals(Object o) {
if (o instanceof BitmapContainer) {
BitmapContainer srb = (BitmapContainer) o;
if (srb.cardinality != this.cardinality) {
return false;
}
return Arrays.equals(this.bitmap, srb.bitmap);
} else if (o instanceof RunContainer) {
return o.equals(this);
}
return false;
}
/**
* Fill the array with set bits
*
* @param array container (should be sufficiently large)
*/
void fillArray(final char[] array) {
int pos = 0;
int base = 0;
for (int k = 0; k < bitmap.length; ++k) {
long bitset = bitmap[k];
while (bitset != 0) {
array[pos++] = (char) (base + numberOfTrailingZeros(bitset));
bitset &= (bitset - 1);
}
base += 64;
}
}
@Override
public void fillLeastSignificant16bits(int[] x, int i, int mask) {
int pos = i;
int base = mask;
for (int k = 0; k < bitmap.length; ++k) {
long bitset = bitmap[k];
while (bitset != 0) {
x[pos++] = base + numberOfTrailingZeros(bitset);
bitset &= (bitset - 1);
}
base += 64;
}
}
@Override
public Container flip(char i) {
int index = i >>> 6;
long bef = bitmap[index];
long mask = 1L << i;
if (cardinality == ArrayContainer.DEFAULT_MAX_SIZE + 1) {// this is
// the
// uncommon
// path
if ((bef & mask) != 0) {
--cardinality;
bitmap[index] &= ~mask;
return this.toArrayContainer();
}
}
// TODO: check whether a branchy version could be faster
cardinality += 1 - 2 * (int)((bef & mask) >>> i);
bitmap[index] ^= mask;
return this;
}
@Override
public int getArraySizeInBytes() {
return MAX_CAPACITY / 8;
}
@Override
public int getCardinality() {
return cardinality;
}
@Override
public PeekableCharIterator getReverseCharIterator() {
return new ReverseBitmapContainerCharIterator(this.bitmap);
}
@Override
public PeekableCharIterator getCharIterator() {
return new BitmapContainerCharIterator(this.bitmap);
}
@Override
public PeekableCharRankIterator getCharRankIterator() {
return new BitmapContainerCharRankIterator(this.bitmap);
}
@Override
public ContainerBatchIterator getBatchIterator() {
return new BitmapBatchIterator(this);
}
@Override
public int getSizeInBytes() {
return this.bitmap.length * 8;
}
@Override
public int hashCode() {
return Arrays.hashCode(this.bitmap);
}
@Override
public Container iadd(int begin, int end) {
// TODO: may need to convert to a RunContainer
if (end == begin) {
return this;
}
if ((begin > end) || (end > (1 << 16))) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
int prevOnesInRange = cardinalityInRange(begin, end);
Util.setBitmapRange(bitmap, begin, end);
updateCardinality(prevOnesInRange, end - begin);
return this;
}
@Override
public Container iand(final ArrayContainer b2) {
if (-1 == cardinality) {
// actually we can avoid allocating in lazy mode
Util.intersectArrayIntoBitmap(bitmap, b2.content, b2.cardinality);
return this;
} else {
return b2.and(this);
}
}
@Override
public Container iand(final BitmapContainer b2) {
if (-1 == cardinality) {
// in lazy mode, just intersect the bitmaps, can repair afterwards
for (int i = 0; i < bitmap.length; ++i) {
bitmap[i] &= b2.bitmap[i];
}
return this;
} else {
int newCardinality = andCardinality(b2);
if (newCardinality > ArrayContainer.DEFAULT_MAX_SIZE) {
for (int k = 0; k < this.bitmap.length; ++k) {
this.bitmap[k] = this.bitmap[k] & b2.bitmap[k];
}
this.cardinality = newCardinality;
return this;
}
ArrayContainer ac = new ArrayContainer(newCardinality);
Util.fillArrayAND(ac.content, this.bitmap, b2.bitmap);
ac.cardinality = newCardinality;
return ac;
}
}
@Override
public Container iand(RunContainer x) {
// could probably be replaced with return iand(x.toBitmapOrArrayContainer());
final int card = x.getCardinality();
if (-1 != cardinality && card <= ArrayContainer.DEFAULT_MAX_SIZE) {
// no point in doing it in-place, unless it's a lazy operation
ArrayContainer answer = new ArrayContainer(card);
answer.cardinality = 0;
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int runStart = (x.getValue(rlepos));
int runEnd = runStart + (x.getLength(rlepos));
for (int runValue = runStart; runValue <= runEnd; ++runValue) {
answer.content[answer.cardinality] = (char) runValue;
answer.cardinality += (int)this.bitValue((char) runValue);
}
}
return answer;
}
int start = 0;
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int end = x.getValue(rlepos);
if (-1 == cardinality) {
Util.resetBitmapRange(this.bitmap, start, end);
} else {
int prevOnes = cardinalityInRange(start, end);
Util.resetBitmapRange(this.bitmap, start, end);
updateCardinality(prevOnes, 0);
}
start = end + x.getLength(rlepos) + 1;
}
if (-1 == cardinality) {
// in lazy mode don't try to trim
Util.resetBitmapRange(this.bitmap, start, MAX_CAPACITY);
} else {
int ones = cardinalityInRange(start, MAX_CAPACITY);
Util.resetBitmapRange(this.bitmap, start, MAX_CAPACITY);
updateCardinality(ones, 0);
if (getCardinality() <= ArrayContainer.DEFAULT_MAX_SIZE) {
return toArrayContainer();
}
}
return this;
}
@Override
public Container iandNot(final ArrayContainer b2) {
for (int k = 0; k < b2.cardinality; ++k) {
this.remove(b2.content[k]);
}
if (cardinality <= ArrayContainer.DEFAULT_MAX_SIZE) {
return this.toArrayContainer();
}
return this;
}
@Override
public Container iandNot(final BitmapContainer b2) {
int newCardinality = 0;
for (int k = 0; k < this.bitmap.length; ++k) {
newCardinality += Long.bitCount(this.bitmap[k] & (~b2.bitmap[k]));
}
if (newCardinality > ArrayContainer.DEFAULT_MAX_SIZE) {
for (int k = 0; k < this.bitmap.length; ++k) {
this.bitmap[k] = this.bitmap[k] & (~b2.bitmap[k]);
}
this.cardinality = newCardinality;
return this;
}
ArrayContainer ac = new ArrayContainer(newCardinality);
Util.fillArrayANDNOT(ac.content, this.bitmap, b2.bitmap);
ac.cardinality = newCardinality;
return ac;
}
@Override
public Container iandNot(RunContainer x) {
// could probably be replaced with return iandNot(x.toBitmapOrArrayContainer());
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int start = (x.getValue(rlepos));
int end = start + (x.getLength(rlepos)) + 1;
int prevOnesInRange = cardinalityInRange(start, end);
Util.resetBitmapRange(this.bitmap, start, end);
updateCardinality(prevOnesInRange, 0);
}
if (getCardinality() > ArrayContainer.DEFAULT_MAX_SIZE) {
return this;
} else {
return toArrayContainer();
}
}
Container ilazyor(ArrayContainer value2) {
this.cardinality = -1;// invalid
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
char v = value2.content[k];
final int i = (v) >>> 6;
this.bitmap[i] |= (1L << v);
}
return this;
}
Container ilazyor(BitmapContainer x) {
this.cardinality = -1;// invalid
for (int k = 0; k < this.bitmap.length; k++) {
this.bitmap[k] |= x.bitmap[k];
}
return this;
}
Container ilazyor(RunContainer x) {
// could be implemented as return ilazyor(x.toTemporaryBitmap());
cardinality = -1; // invalid
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int start = (x.getValue(rlepos));
int end = start + (x.getLength(rlepos)) + 1;
Util.setBitmapRange(this.bitmap, start, end);
}
return this;
}
@Override
public Container inot(final int firstOfRange, final int lastOfRange) {
int prevOnes = cardinalityInRange(firstOfRange, lastOfRange);
Util.flipBitmapRange(bitmap, firstOfRange, lastOfRange);
updateCardinality(prevOnes, lastOfRange - firstOfRange - prevOnes);
if (cardinality <= ArrayContainer.DEFAULT_MAX_SIZE) {
return toArrayContainer();
}
return this;
}
@Override
public boolean intersects(ArrayContainer value2) {
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
if (this.contains(value2.content[k])) {
return true;
}
}
return false;
}
@Override
public boolean intersects(BitmapContainer value2) {
for (int k = 0; k < this.bitmap.length; ++k) {
if ((this.bitmap[k] & value2.bitmap[k]) != 0) {
return true;
}
}
return false;
}
@Override
public boolean intersects(RunContainer x) {
return x.intersects(this);
}
@Override
public boolean intersects(int minimum, int supremum) {
if((minimum < 0) || (supremum < minimum) || (supremum > (1<<16))) {
throw new RuntimeException("This should never happen (bug).");
}
int start = minimum >>> 6;
int end = supremum >>> 6;
if (start == end) {
return ((bitmap[end] & (-(1L << minimum) & ((1L << supremum) - 1))) != 0);
}
if ((bitmap[start] & -(1L << minimum)) != 0) {
return true;
}
if (end < bitmap.length && (bitmap[end] & ((1L << supremum) - 1)) != 0) {
return true;
}
for (int i = 1 + start; i < end && i < bitmap.length; ++i) {
if (bitmap[i] != 0) {
return true;
}
}
return false;
}
@Override
public BitmapContainer ior(final ArrayContainer value2) {
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
final int i = (value2.content[k]) >>> 6;
long bef = this.bitmap[i];
long aft = bef | (1L << value2.content[k]);
this.bitmap[i] = aft;
if (USE_BRANCHLESS) {
cardinality += (int)((bef - aft) >>> 63);
} else {
if (bef != aft) {
cardinality++;
}
}
}
return this;
}
@Override
public Container ior(final BitmapContainer b2) {
for (int k = 0; k < this.bitmap.length & k < b2.bitmap.length; k++) {
this.bitmap[k] |= b2.bitmap[k];
}
computeCardinality();
if (isFull()) {
return RunContainer.full();
}
return this;
}
@Override
public Container ior(RunContainer x) {
// could probably be replaced with return ior(x.toBitmapOrArrayContainer());
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int start = (x.getValue(rlepos));
int end = start + (x.getLength(rlepos)) + 1;
int prevOnesInRange = cardinalityInRange(start, end);
Util.setBitmapRange(this.bitmap, start, end);
updateCardinality(prevOnesInRange, end - start);
}
if (isFull()) {
return RunContainer.full();
}
return this;
}
@Override
public Container iremove(int begin, int end) {
if (end == begin) {
return this;
}
if ((begin > end) || (end > (1 << 16))) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
int prevOnesInRange = cardinalityInRange(begin, end);
Util.resetBitmapRange(bitmap, begin, end);
updateCardinality(prevOnesInRange, 0);
if (getCardinality() <= ArrayContainer.DEFAULT_MAX_SIZE) {
return toArrayContainer();
}
return this;
}
@Override
public Iterator iterator() {
return new Iterator() {
final CharIterator si = BitmapContainer.this.getCharIterator();
@Override
public boolean hasNext() {
return si.hasNext();
}
@Override
public Character next() {
return si.next();
}
@Override
public void remove() {
// TODO: implement
throw new RuntimeException("unsupported operation: remove");
}
};
}
@Override
public Container ixor(final ArrayContainer value2) {
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
char vc = value2.content[k];
long mask = 1L << vc;
final int index = (vc) >>> 6;
long ba = this.bitmap[index];
// TODO: check whether a branchy version could be faster
this.cardinality += 1 - 2 * (int)((ba & mask) >>> vc);
this.bitmap[index] = ba ^ mask;
}
if (this.cardinality <= ArrayContainer.DEFAULT_MAX_SIZE) {
return this.toArrayContainer();
}
return this;
}
@Override
public Container ixor(BitmapContainer b2) {
// do this first because we have to compute the xor no matter what, and this loop gets
// vectorized and is faster than computing the cardinality or filling the array
for (int k = 0; k < this.bitmap.length & k < b2.bitmap.length; ++k) {
this.bitmap[k] ^= b2.bitmap[k];
}
// now count the bits
computeCardinality();
if (cardinality > ArrayContainer.DEFAULT_MAX_SIZE) {
return this;
}
return toArrayContainer();
}
@Override
public Container ixor(RunContainer x) {
// could probably be replaced with return ixor(x.toBitmapOrArrayContainer());
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int start = x.getValue(rlepos);
int end = start + x.getLength(rlepos) + 1;
int prevOnes = cardinalityInRange(start, end);
Util.flipBitmapRange(this.bitmap, start, end);
updateCardinality(prevOnes, end - start - prevOnes);
}
if (this.getCardinality() > ArrayContainer.DEFAULT_MAX_SIZE) {
return this;
} else {
return toArrayContainer();
}
}
protected Container lazyor(ArrayContainer value2) {
BitmapContainer answer = this.clone();
answer.cardinality = -1;// invalid
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
char v = value2.content[k];
final int i = (v) >>> 6;
answer.bitmap[i] |= (1L << v);
}
return answer;
}
protected Container lazyor(BitmapContainer x) {
BitmapContainer answer = new BitmapContainer();
answer.cardinality = -1;// invalid
for (int k = 0; k < this.bitmap.length; k++) {
answer.bitmap[k] = this.bitmap[k] | x.bitmap[k];
}
return answer;
}
protected Container lazyor(RunContainer x) {
BitmapContainer bc = clone();
bc.cardinality = -1; // invalid
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int start = (x.getValue(rlepos));
int end = start + (x.getLength(rlepos)) + 1;
Util.setBitmapRange(bc.bitmap, start, end);
}
return bc;
}
@Override
public Container limit(int maxcardinality) {
if (maxcardinality >= this.cardinality) {
return clone();
}
if (maxcardinality <= ArrayContainer.DEFAULT_MAX_SIZE) {
ArrayContainer ac = new ArrayContainer(maxcardinality);
int pos = 0;
for (int k = 0; (ac.cardinality < maxcardinality) && (k < bitmap.length); ++k) {
long bitset = bitmap[k];
while ((ac.cardinality < maxcardinality) && (bitset != 0)) {
ac.content[pos++] = (char) (k * 64 + numberOfTrailingZeros(bitset));
ac.cardinality++;
bitset &= (bitset - 1);
}
}
return ac;
}
BitmapContainer bc = new BitmapContainer(maxcardinality, this.bitmap);
int s = (select(maxcardinality));
int usedwords = (s + 63) / 64;
int todelete = this.bitmap.length - usedwords;
for (int k = 0; k < todelete; ++k) {
bc.bitmap[bc.bitmap.length - 1 - k] = 0;
}
int lastword = s % 64;
if (lastword != 0) {
bc.bitmap[s / 64] &= (0xFFFFFFFFFFFFFFFFL >>> (64 - lastword));
}
return bc;
}
void loadData(final ArrayContainer arrayContainer) {
this.cardinality = arrayContainer.cardinality;
for (int k = 0; k < arrayContainer.cardinality; ++k) {
final char x = arrayContainer.content[k];
bitmap[(x) / 64] |= (1L << x);
}
}
/**
* Find the index of the next set bit greater or equal to i, returns -1 if none found.
*
* @param i starting index
* @return index of the next set bit
*/
public int nextSetBit(final int i) {
int x = i >> 6;
long w = bitmap[x];
w >>>= i;
if (w != 0) {
return i + numberOfTrailingZeros(w);
}
for (++x; x < bitmap.length; ++x) {
if (bitmap[x] != 0) {
return x * 64 + numberOfTrailingZeros(bitmap[x]);
}
}
return -1;
}
/**
* Find the index of the next clear bit greater or equal to i.
*
* @param i starting index
* @return index of the next clear bit
*/
private int nextClearBit(final int i) {
int x = i >> 6;
long w = ~bitmap[x];
w >>>= i;
if (w != 0) {
return i + numberOfTrailingZeros(w);
}
for (++x; x < bitmap.length; ++x) {
long map = ~bitmap[x];
if (map != 0) {
return x * 64 + numberOfTrailingZeros(map);
}
}
return MAX_CAPACITY;
}
@Override
public Container not(final int firstOfRange, final int lastOfRange) {
BitmapContainer answer = clone();
return answer.inot(firstOfRange, lastOfRange);
}
@Override
int numberOfRuns() {
int numRuns = 0;
long nextWord = bitmap[0];
for (int i = 0; i < bitmap.length - 1; i++) {
long word = nextWord;
nextWord = bitmap[i + 1];
numRuns += Long.bitCount((~word) & (word << 1)) + (int)((word >>> 63) & ~nextWord);
}
long word = nextWord;
numRuns += Long.bitCount((~word) & (word << 1));
if ((word & 0x8000000000000000L) != 0) {
numRuns++;
}
return numRuns;
}
/**
* Computes the number of runs
*
* @return the number of runs
*/
public int numberOfRunsAdjustment() {
int ans = 0;
long nextWord = bitmap[0];
for (int i = 0; i < bitmap.length - 1; i++) {
final long word = nextWord;
nextWord = bitmap[i + 1];
ans += (int)((word >>> 63) & ~nextWord);
}
final long word = nextWord;
if ((word & 0x8000000000000000L) != 0) {
ans++;
}
return ans;
}
/**
* Counts how many runs there is in the bitmap, up to a maximum
*
* @param mustNotExceed maximum of runs beyond which counting is pointless
* @return estimated number of courses
*/
public int numberOfRunsLowerBound(int mustNotExceed) {
int numRuns = 0;
for (int blockOffset = 0; blockOffset + BLOCKSIZE <= bitmap.length; blockOffset += BLOCKSIZE) {
for (int i = blockOffset; i < blockOffset + BLOCKSIZE; i++) {
long word = bitmap[i];
numRuns += Long.bitCount((~word) & (word << 1));
}
if (numRuns > mustNotExceed) {
return numRuns;
}
}
return numRuns;
}
@Override
public Container or(final ArrayContainer value2) {
final BitmapContainer answer = clone();
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
char v = value2.content[k];
final int i = (v) >>> 6;
long w = answer.bitmap[i];
long aft = w | (1L << v);
answer.bitmap[i] = aft;
if (USE_BRANCHLESS) {
answer.cardinality += (int)((w - aft) >>> 63);
} else {
if (w != aft) {
answer.cardinality++;
}
}
}
if (answer.isFull()) {
return RunContainer.full();
}
return answer;
}
@Override
public boolean isFull() {
return cardinality == MAX_CAPACITY;
}
@Override
public Container or(final BitmapContainer value2) {
BitmapContainer value1 = this.clone();
return value1.ior(value2);
}
@Override
public Container or(RunContainer x) {
return x.or(this);
}
/**
* Find the index of the previous set bit less than or equal to i, returns -1 if none found.
*
* @param i starting index
* @return index of the previous set bit
*/
int prevSetBit(final int i) {
int x = i >> 6; // i / 64 with sign extension
long w = bitmap[x];
w <<= 64 - i - 1;
if (w != 0) {
return i - Long.numberOfLeadingZeros(w);
}
for (--x; x >= 0; --x) {
if (bitmap[x] != 0) {
return x * 64 + 63 - Long.numberOfLeadingZeros(bitmap[x]);
}
}
return -1;
}
/**
* Find the index of the previous clear bit less than or equal to i.
*
* @param i starting index
* @return index of the previous clear bit
*/
private int prevClearBit(final int i) {
int x = i >> 6; // i / 64 with sign extension
long w = ~bitmap[x];
w <<= 64 - (i + 1);
if (w != 0) {
return i - Long.numberOfLeadingZeros(w);
}
for (--x; x >= 0; --x) {
long map = ~bitmap[x];
if (map != 0) {
return x * 64 + 63 - Long.numberOfLeadingZeros(map);
}
}
return -1;
}
@Override
public int rank(char lowbits) {
int leftover = (lowbits + 1) & 63;
int answer = 0;
for (int k = 0; k < (lowbits + 1) >>> 6; ++k) {
answer += Long.bitCount(bitmap[k]);
}
if (leftover != 0) {
answer += Long.bitCount(bitmap[(lowbits + 1) >>> 6] << (64 - leftover));
}
return answer;
}
@Override
public void readExternal(ObjectInput in) throws IOException {
deserialize(in);
}
@Override
public Container remove(int begin, int end) {
if (end == begin) {
return clone();
}
if ((begin > end) || (end > (1 << 16))) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
BitmapContainer answer = clone();
int prevOnesInRange = answer.cardinalityInRange(begin, end);
Util.resetBitmapRange(answer.bitmap, begin, end);
answer.updateCardinality(prevOnesInRange, 0);
if (answer.getCardinality() <= ArrayContainer.DEFAULT_MAX_SIZE) {
return answer.toArrayContainer();
}
return answer;
}
@Override
public Container remove(final char i) {
int index = i >>> 6;
long bef = bitmap[index];
long mask = 1L << i;
if (cardinality == ArrayContainer.DEFAULT_MAX_SIZE + 1) {// this is
// the
// uncommon
// path
if ((bef & mask) != 0) {
--cardinality;
bitmap[i >>> 6] = bef & ~mask;
return this.toArrayContainer();
}
}
long aft = bef & ~mask;
cardinality -= (aft - bef) >>> 63;
bitmap[index] = aft;
return this;
}
@Override
public Container repairAfterLazy() {
if (getCardinality() < 0) {
computeCardinality();
if(getCardinality() <= ArrayContainer.DEFAULT_MAX_SIZE) {
return this.toArrayContainer();
} else if (isFull()) {
return RunContainer.full();
}
}
return this;
}
@Override
public Container runOptimize() {
int numRuns = numberOfRunsLowerBound(MAXRUNS); // decent choice
int sizeAsRunContainerLowerBound = RunContainer.serializedSizeInBytes(numRuns);
if (sizeAsRunContainerLowerBound >= getArraySizeInBytes()) {
return this;
}
// else numRuns is a relatively tight bound that needs to be exact
// in some cases (or if we need to make the runContainer the right
// size)
numRuns += numberOfRunsAdjustment();
int sizeAsRunContainer = RunContainer.serializedSizeInBytes(numRuns);
if (getArraySizeInBytes() > sizeAsRunContainer) {
return new RunContainer(this, numRuns);
} else {
return this;
}
}
@Override
public char select(int j) {
int leftover = j;
for (int k = 0; k < bitmap.length; ++k) {
int w = Long.bitCount(bitmap[k]);
if (w > leftover) {
return (char) (k * 64 + Util.select(bitmap[k], leftover));
}
leftover -= w;
}
throw new IllegalArgumentException("Insufficient cardinality.");
}
@Override
public void serialize(DataOutput out) throws IOException {
// little endian
for (long w : bitmap) {
out.writeLong(Long.reverseBytes(w));
}
}
@Override
public int serializedSizeInBytes() {
return serializedSizeInBytes(0);
}
/**
* Copies the data to an array container
*
* @return the array container
*/
ArrayContainer toArrayContainer() {
ArrayContainer ac = new ArrayContainer(cardinality);
if (cardinality != 0) {
ac.loadData(this);
}
if (ac.getCardinality() != cardinality) {
throw new RuntimeException("Internal error.");
}
return ac;
}
/**
* Return the content of this container as a LongBuffer. This creates a copy and might be
* relatively slow.
*
* @return the LongBuffer
*/
public LongBuffer toLongBuffer() {
LongBuffer lb = LongBuffer.allocate(bitmap.length);
lb.put(bitmap);
return lb;
}
@Override
public MappeableContainer toMappeableContainer() {
return new MappeableBitmapContainer(this);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("{}".length() + "-123456789,".length() * 256);
final CharIterator i = this.getCharIterator();
sb.append('{');
while (i.hasNext()) {
sb.append((int)(i.next()));
if (i.hasNext()) {
sb.append(',');
}
}
sb.append('}');
return sb.toString();
}
@Override
public void trim() {
}
@Override
public void writeArray(DataOutput out) throws IOException {
serialize(out);
}
@Override
public void writeArray(ByteBuffer buffer) {
assert buffer.order() == ByteOrder.LITTLE_ENDIAN;
LongBuffer buf = buffer.asLongBuffer();
buf.put(bitmap);
int bytesWritten = bitmap.length * 8;
buffer.position(buffer.position() + bytesWritten);
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
serialize(out);
}
@Override
public Container xor(final ArrayContainer value2) {
final BitmapContainer answer = clone();
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
char vc = value2.content[k];
final int index = vc >>> 6;
final long mask = 1L << vc;
final long val = answer.bitmap[index];
// TODO: check whether a branchy version could be faster
answer.cardinality += (int)(1 - 2 * ((val & mask) >>> vc));
answer.bitmap[index] = val ^ mask;
}
if (answer.cardinality <= ArrayContainer.DEFAULT_MAX_SIZE) {
return answer.toArrayContainer();
}
return answer;
}
@Override
public Container xor(BitmapContainer value2) {
int newCardinality = 0;
for (int k = 0; k < this.bitmap.length; ++k) {
newCardinality += Long.bitCount(this.bitmap[k] ^ value2.bitmap[k]);
}
if (newCardinality > ArrayContainer.DEFAULT_MAX_SIZE) {
final BitmapContainer answer = new BitmapContainer();
for (int k = 0; k < answer.bitmap.length; ++k) {
answer.bitmap[k] = this.bitmap[k] ^ value2.bitmap[k];
}
answer.cardinality = newCardinality;
return answer;
}
ArrayContainer ac = new ArrayContainer(newCardinality);
Util.fillArrayXOR(ac.content, this.bitmap, value2.bitmap);
ac.cardinality = newCardinality;
return ac;
}
@Override
public Container xor(RunContainer x) {
return x.xor(this);
}
@Override
public void forEach(char msb, IntConsumer ic) {
int high = msb << 16;
for (int x = 0; x < bitmap.length; ++x) {
long w = bitmap[x];
while (w != 0) {
ic.accept(((x << 6) + numberOfTrailingZeros(w)) | high);
w &= (w - 1);
}
}
}
@Override
public void forAll(int offset, final RelativeRangeConsumer rrc) {
for (int wordIndex = 0; wordIndex < bitmap.length; wordIndex++) {
long word = bitmap[wordIndex];
int bufferWordStart = offset + (wordIndex << 6);
int bufferWordEnd = bufferWordStart + 64;
addWholeWordToRangeConsumer(word, bufferWordStart, bufferWordEnd, rrc);
}
}
@Override
public void forAllFrom(char startValue, final RelativeRangeConsumer rrc) {
int startIndex = startValue >>> 6;
for (int wordIndex = startIndex; wordIndex < bitmap.length; wordIndex++) {
long word = bitmap[wordIndex];
int wordStart = wordIndex << 6;
int wordEnd = wordStart + 64;
if (wordStart < startValue) {
// startValue is in the middle of the word
// some special cases for efficiency
if (word == 0) {
rrc.acceptAllAbsent(0, wordEnd - startValue);
} else if (word == -1) { // all 1s
rrc.acceptAllPresent(0, wordEnd - startValue);
} else {
int nextPos = startValue;
while (word != 0) {
int pos = wordStart + numberOfTrailingZeros(word);
if (nextPos < pos) {
rrc.acceptAllAbsent(nextPos - startValue, pos - startValue);
rrc.acceptPresent(pos - startValue);
nextPos = pos + 1;
} else if (nextPos == pos) {
rrc.acceptPresent(pos - startValue);
nextPos++;
} // else just we out before startValue, so ignore
word &= (word - 1);
}
if (nextPos < wordEnd) {
rrc.acceptAllAbsent(nextPos - startValue, wordEnd - startValue);
}
}
} else {
// startValue is aligned with word
addWholeWordToRangeConsumer(word, wordStart - startValue, wordEnd - startValue, rrc);
}
}
}
@Override
public void forAllUntil(int offset, char endValue, final RelativeRangeConsumer rrc) {
int bufferEndPos = offset + endValue;
for (int wordIndex = 0; wordIndex < bitmap.length; wordIndex++) {
long word = bitmap[wordIndex];
int bufferWordStart = offset + (wordIndex << 6);
int bufferWordEnd = bufferWordStart + 64;
if (bufferWordStart >= bufferEndPos) {
return;
}
if (bufferEndPos < bufferWordEnd) {
// we end on this word
// some special cases for efficiency
if (word == 0) {
rrc.acceptAllAbsent(bufferWordStart, bufferEndPos);
} else if (word == -1) { // all 1s
rrc.acceptAllPresent(bufferWordStart, bufferEndPos);
} else {
int nextPos = bufferWordStart;
while (word != 0) {
int pos = bufferWordStart + numberOfTrailingZeros(word);
if (bufferEndPos <= pos) {
// we've moved past the end
if (nextPos < bufferEndPos) {
rrc.acceptAllAbsent(nextPos, bufferEndPos);
}
return;
}
if (nextPos < pos) {
rrc.acceptAllAbsent(nextPos, pos);
nextPos = pos;
}
rrc.acceptPresent(pos);
nextPos++;
word &= (word - 1);
}
if (nextPos < bufferEndPos) {
rrc.acceptAllAbsent(nextPos, bufferEndPos);
}
return;
}
} else {
addWholeWordToRangeConsumer(word, bufferWordStart, bufferWordEnd, rrc);
}
}
}
@Override
public void forAllInRange(char startValue, char endValue, final RelativeRangeConsumer rrc) {
if (endValue <= startValue) {
throw new IllegalArgumentException(
"startValue (" + startValue + ") must be less than endValue (" + endValue + ")");
}
int startIndex = startValue >>> 6;
for (int wordIndex = startIndex; wordIndex < bitmap.length; wordIndex++) {
long word = bitmap[wordIndex];
int wordStart = wordIndex << 6;
int wordEndExclusive = wordStart + 64;
if (wordStart >= endValue) {
return;
}
boolean startInWord = wordStart < startValue;
boolean endInWord = endValue < wordEndExclusive;
boolean wordAllZeroes = word == 0;
boolean wordAllOnes = word == -1;
if (startInWord && endInWord) {
if (wordAllZeroes) {
rrc.acceptAllAbsent(0, endValue - startValue);
} else if (wordAllOnes) {
rrc.acceptAllPresent(0, endValue - startValue);
} else {
int nextPos = startValue;
while (word != 0) {
int pos = wordStart + numberOfTrailingZeros(word);
if (endValue <= pos) {
// we've moved past the end
if (nextPos < endValue) {
rrc.acceptAllAbsent(nextPos - startValue, endValue - startValue);
}
return;
}
if (nextPos < pos) {
rrc.acceptAllAbsent(nextPos - startValue, pos - startValue);
rrc.acceptPresent(pos - startValue);
nextPos = pos + 1;
} else if (nextPos == pos) {
rrc.acceptPresent(pos - startValue);
nextPos++;
}
word &= (word - 1);
}
if (nextPos < endValue) {
rrc.acceptAllAbsent(nextPos - startValue, endValue - startValue);
}
}
return;
} else if (startInWord) {
if (wordAllZeroes) {
rrc.acceptAllAbsent(0, 64 - (startValue - wordStart));
} else if (wordAllOnes) {
rrc.acceptAllPresent(0, 64 - (startValue - wordStart));
} else {
int nextPos = startValue;
while (word != 0) {
int pos = wordStart + numberOfTrailingZeros(word);
if (nextPos < pos) {
rrc.acceptAllAbsent(nextPos - startValue, pos - startValue);
rrc.acceptPresent(pos - startValue);
nextPos = pos + 1;
} else if (nextPos == pos) {
rrc.acceptPresent(pos - startValue);
nextPos++;
}
word &= (word - 1);
}
if (nextPos < wordEndExclusive) {
rrc.acceptAllAbsent(nextPos - startValue, wordEndExclusive - startValue);
}
}
} else if (endInWord) {
if (wordAllZeroes) {
rrc.acceptAllAbsent(wordStart - startValue, endValue - startValue);
} else if (wordAllOnes) {
rrc.acceptAllPresent(wordStart - startValue, endValue - startValue);
} else {
int nextPos = wordStart;
while (word != 0) {
int pos = wordStart + numberOfTrailingZeros(word);
if (endValue <= pos) {
// we've moved past the end
if (nextPos < endValue) {
rrc.acceptAllAbsent(nextPos - startValue, endValue - startValue);
}
return;
}
if (nextPos < pos) {
rrc.acceptAllAbsent(nextPos - startValue, pos - startValue);
nextPos = pos;
}
rrc.acceptPresent(pos - startValue);
nextPos++;
word &= (word - 1);
}
if (nextPos < endValue) {
rrc.acceptAllAbsent(nextPos - startValue, endValue - startValue);
}
}
return;
} else {
addWholeWordToRangeConsumer(
word,
wordStart - startValue,
wordEndExclusive - startValue,
rrc);
}
}
}
private void addWholeWordToRangeConsumer(
long word,
int bufferWordStart,
int bufferWordEnd,
final RelativeRangeConsumer rrc) {
// some special cases for efficiency
if (word == 0) {
rrc.acceptAllAbsent(bufferWordStart, bufferWordEnd);
} else if (word == -1) { // all 1s
rrc.acceptAllPresent(bufferWordStart, bufferWordEnd);
} else {
int nextPos = bufferWordStart;
while (word != 0) {
int pos = bufferWordStart + numberOfTrailingZeros(word);
if (nextPos < pos) {
rrc.acceptAllAbsent(nextPos, pos);
nextPos = pos;
}
rrc.acceptPresent(pos);
nextPos++;
word &= (word - 1);
}
if (nextPos < bufferWordEnd) {
rrc.acceptAllAbsent(nextPos, bufferWordEnd);
}
}
}
@Override
public BitmapContainer toBitmapContainer() {
return this;
}
@Override
public int nextValue(char fromValue) {
return nextSetBit((fromValue));
}
@Override
public int previousValue(char fromValue) {
return prevSetBit((fromValue));
}
@Override
public int nextAbsentValue(char fromValue) {
return nextClearBit((fromValue));
}
@Override
public int previousAbsentValue(char fromValue) {
return prevClearBit((fromValue));
}
@Override
public int first() {
assertNonEmpty(cardinality == 0);
int i = 0;
while(i < bitmap.length - 1 && bitmap[i] == 0) {
++i; // seek forward
}
// sizeof(long) * #empty words at start + number of bits preceding the first bit set
return i * 64 + numberOfTrailingZeros(bitmap[i]);
}
@Override
public int last() {
assertNonEmpty(cardinality == 0);
int i = bitmap.length - 1;
while(i > 0 && bitmap[i] == 0) {
--i; // seek backward
}
// sizeof(long) * #words from start - number of bits after the last bit set
return (i + 1) * 64 - Long.numberOfLeadingZeros(bitmap[i]) - 1;
}
}
class BitmapContainerCharIterator implements PeekableCharIterator {
long w;
int x;
long[] bitmap;
BitmapContainerCharIterator() {
}
BitmapContainerCharIterator(long[] p) {
wrap(p);
}
@Override
public PeekableCharIterator clone() {
try {
return (PeekableCharIterator) super.clone();
} catch (CloneNotSupportedException e) {
return null;// will not happen
}
}
@Override
public boolean hasNext() {
return x < bitmap.length;
}
@Override
public char next() {
char answer = (char) (x * 64 + numberOfTrailingZeros(w));
w &= (w - 1);
while (w == 0) {
++x;
if (x == bitmap.length) {
break;
}
w = bitmap[x];
}
return answer;
}
@Override
public int nextAsInt() {
return (next());
}
@Override
public void remove() {
// TODO: implement
throw new RuntimeException("unsupported operation: remove");
}
public void wrap(long[] b) {
bitmap = b;
for (x = 0; x < bitmap.length; ++x) {
if ((w = bitmap[x]) != 0) {
break;
}
}
}
@Override
public void advanceIfNeeded(char minval) {
if (!hasNext()) {
return;
}
if (minval >= x * 64) {
if (minval >= (x + 1) * 64) {
x = minval / 64;
w = bitmap[x];
}
w &= ~0L << (minval & 63);
while (w == 0) {
x++;
if (!hasNext()) {
return;
}
w = bitmap[x];
}
}
}
@Override
public char peekNext() {
return (char) (x * 64 + numberOfTrailingZeros(w));
}
}
final class BitmapContainerCharRankIterator extends BitmapContainerCharIterator
implements PeekableCharRankIterator {
private int nextRank = 1;
BitmapContainerCharRankIterator(long[] p) {
super(p);
}
@Override
public int peekNextRank() {
return nextRank;
}
@Override
public char next() {
++nextRank;
return super.next();
}
@Override
public void advanceIfNeeded(char minval) {
if (!hasNext()) {
return;
}
if (minval >= x * 64) {
if (minval >= (x + 1) * 64) {
int nextX = minval / 64;
nextRank += bitCount(w);
for(x = x + 1; x < nextX; x++) {
w = bitmap[x];
nextRank += bitCount(w);
}
w = bitmap[nextX];
}
nextRank += bitCount(w);
w &= ~0L << (minval & 63);
nextRank -= bitCount(w);
while (w == 0) {
++x;
if (!hasNext()) {
return;
}
w = bitmap[x];
}
}
}
@Override
public PeekableCharRankIterator clone() {
return (PeekableCharRankIterator) super.clone();
}
}
final class ReverseBitmapContainerCharIterator implements PeekableCharIterator {
long word;
int position;
long[] bitmap;
ReverseBitmapContainerCharIterator() {
}
ReverseBitmapContainerCharIterator(long[] bitmap) {
wrap(bitmap);
}
@Override
public PeekableCharIterator clone() {
try {
return (PeekableCharIterator) super.clone();
} catch (CloneNotSupportedException e) {
return null;
}
}
@Override
public boolean hasNext() {
return position >= 0;
}
@Override
public char next() {
int shift = Long.numberOfLeadingZeros(word) + 1;
char answer = (char)((position + 1) * 64 - shift);
word &= (0xFFFFFFFFFFFFFFFEL >>> shift);
while (word == 0) {
--position;
if (position < 0) {
break;
}
word = bitmap[position];
}
return answer;
}
@Override
public int nextAsInt() {
return next();
}
@Override
public void advanceIfNeeded(char maxval) {
if (maxval < (position + 1) * 64) {
if (maxval < position * 64) {
position = maxval / 64;
}
long currentWord = bitmap[position];
currentWord &= ~0L >>> (63 - (maxval & 63));
if (position > 0) {
while (currentWord == 0) {
position--;
if (position == 0) {
break;
}
currentWord = bitmap[position];
}
}
word = currentWord;
}
}
@Override
public char peekNext() {
int shift = Long.numberOfLeadingZeros(word) + 1;
return (char) ((position + 1) * 64 - shift);
}
@Override
public void remove() {
// TODO: implement
throw new RuntimeException("unsupported operation: remove");
}
void wrap(long[] b) {
bitmap = b;
for (position = bitmap.length - 1; position >= 0; --position) {
if ((word = bitmap[position]) != 0) {
break;
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy