Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* (c) the authors Licensed under the Apache License, Version 2.0.
*
* The code in this file is a heavily modified version of the original in the RoaringBitmap library; please see
* https://roaringbitmap.org/
*
*/
package io.deephaven.engine.rowset.impl.rsp.container;
import java.util.NoSuchElementException;
import java.util.function.Supplier;
import static java.lang.Long.numberOfLeadingZeros;
import static java.lang.Long.numberOfTrailingZeros;
import static io.deephaven.engine.rowset.impl.rsp.container.MutableInteger.setIfNotNull;
import static io.deephaven.engine.rowset.impl.rsp.container.PositionHint.resetIfNotNull;
import static io.deephaven.engine.rowset.impl.rsp.container.ContainerUtil.*;
/**
* Simple bitset-like container.
*/
public final class BitmapContainer extends Container implements Cloneable {
protected static final int BITMAP_CAPACITY = MAX_RANGE / (8 * Long.BYTES);
protected static final int BITMAP_SIZE_IN_BYTES = BITMAP_CAPACITY * Long.BYTES;
/**
* optimization flag: whether the cardinality of the bitmaps is maintained through branchless operations
*/
public 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 ShortAdvanceIterator getReverseShortIterator(final long[] bitmap) {
return new ReverseShortIterator(bitmap);
}
/**
* Return a bitmap iterator over this array
*
* @param bitmap array to be iterated over
* @return an iterator
*/
public static ShortIterator getShortIterator(final long[] bitmap) {
return new ForwardShortIterator(bitmap);
}
final long[] bitmap;
int cardinality;
private boolean shared = false;
/**
* Create a bitmap container with all bits set to false
*/
public BitmapContainer() {
cardinality = 0;
bitmap = new long[BITMAP_CAPACITY];
}
private BitmapContainer(final int start, final int end) {
cardinality = end - start;
bitmap = new long[BITMAP_CAPACITY];
setBitmapRange(bitmap, start, end);
}
/**
* Create a bitmap container with a run of ones from start to end. Caller must ensure that the range isn't so small
* that an ArrayContainer should have been created instead
*
* @param start first index
* @param end end index (exclusive)
* @return the new container.
*/
public static BitmapContainer singleRange(final int start, final int end) {
return new BitmapContainer(start, end);
}
private BitmapContainer(final BitmapContainer other) {
cardinality = other.cardinality;
bitmap = new long[BITMAP_CAPACITY];
System.arraycopy(other.bitmap, 0, bitmap, 0, bitmap.length);
}
/**
* Create a new container, no copy is made.
*
* @param newBitmap content
* @param newCardinality desired cardinality.
*/
// For tests.
BitmapContainer(final long[] newBitmap, final int newCardinality) {
if (newBitmap.length != BITMAP_CAPACITY ||
newCardinality < 0 ||
newCardinality > MAX_RANGE) {
throw new IllegalArgumentException(
"newBitmap.length=" + newBitmap.length + ", newCardinality=" + newCardinality);
}
cardinality = newCardinality;
bitmap = newBitmap;
}
private int findFirstZeroBit() {
return findFirstZeroBit(0);
}
private int findFirstZeroBit(final int iStart) {
for (int i = iStart; i < bitmap.length; ++i) {
long v = bitmap[i];
if (v == ~(0L)) {
continue;
}
return 64 * i + numberOfTrailingZeros(~v);
}
return -1;
}
Container maybeSwitchContainer() {
final Container c = maybeSwitchContainerAfterShrinking();
if (c != this) {
return c;
}
return maybeSwitchContainerAfterGrowing();
}
private Container maybeSwitchContainerToArrayOrRun() {
if (cardinality == 0) {
return Container.empty();
}
if (cardinality <= ArrayContainer.SWITCH_CONTAINER_CARDINALITY_THRESHOLD) {
return toArrayContainer();
}
int frontZeroes = 0;
int i = 0;
while (bitmap[i++] == 0) {
++frontZeroes;
}
int backZeroes = 0;
i = BITMAP_CAPACITY - 1;
while (bitmap[i--] == 0) {
++backZeroes;
}
final int bitsAvailableInNonZeroWords = (BITMAP_CAPACITY - frontZeroes - backZeroes) * Long.BYTES * 8;
// One every other bit set maximizes number of runs.
final int runsUpperBound1 = bitsAvailableInNonZeroWords / 2;
final int zeroBitsInNonZeroWords = bitsAvailableInNonZeroWords - cardinality;
final int runsUpperBound2 = 1 + zeroBitsInNonZeroWords; // Spread the fingers in one hand: 4 holes => 5 fingers.
if (Math.min(runsUpperBound1, runsUpperBound2) < ArrayContainer.SWITCH_CONTAINER_CARDINALITY_THRESHOLD / 2) {
return toRunContainer();
}
return this;
}
private Container maybeSwitchContainerAfterGrowing() {
if (cardinality < MAX_VALUE) {
return this;
}
if (cardinality == MAX_VALUE) {
final int v = findFirstZeroBit();
if (v == 0) {
return Container.singleRange(1, MAX_RANGE);
}
if (v == MAX_VALUE) {
return Container.singleRange(0, MAX_VALUE);
}
return Container.twoRanges(0, v, v + 1, MAX_RANGE);
}
// cardinality > MAX_VALUE => all ones.
return Container.full();
}
private Container maybeSwitchContainerAfterShrinking() {
if (cardinality == 0) {
return Container.empty();
}
if (cardinality == 1) {
return Container.singleton(lowbits(first()));
}
if (cardinality == 2) {
return Container.twoValues(lowbits(first()), lowbits(last()));
}
if (cardinality <= ArrayContainer.SWITCH_CONTAINER_CARDINALITY_THRESHOLD) {
return toArrayContainer();
}
return this;
}
@Override
public Container add(final int begin, final int end) {
// TODO: may need to convert to a RunContainer
if (end == begin) {
return cowRef();
}
if (begin > end || end > MAX_RANGE) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
final BitmapContainer ans = deepCopy();
return ans.iaddImpl(begin, end);
}
@Override
public Container iset(final short x) {
return setImpl(x, () -> this, this::deepCopyIfShared);
}
@Override
public Container set(final short x) {
return setImpl(x, this::cowRef, this::deepCopy);
}
@Override
Container iset(final short x, final PositionHint positionHint) {
positionHint.reset();
return iset(x);
}
@Override
Container set(final short x, final PositionHint positionHint) {
positionHint.reset();
return set(x);
}
private Container setImpl(
final short x, final Supplier self, final Supplier copy) {
if (contains(x)) {
return self.get();
}
final BitmapContainer ans = copy.get();
ans.setUnsafe(x);
return ans.maybeSwitchContainerAfterGrowing();
}
void setUnsafe(final short x) {
final int xAsInt = toIntUnsigned(x);
final long previous = bitmap[xAsInt / 64];
long newval = previous | (1L << xAsInt);
bitmap[xAsInt / 64] = newval;
if (USE_BRANCHLESS) {
cardinality += (previous ^ newval) >>> xAsInt;
} else if (previous != newval) {
++cardinality;
}
}
@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) {
short v = value2.content[k];
answer.content[answer.cardinality] = v;
answer.cardinality += bitValue(v);
}
return answer;
}
@Override
public Container and(final BitmapContainer value2) {
return iandImpl(value2, false);
}
@Override
public Container and(RunContainer x) {
return x.and(this);
}
@Override
public Container andRange(final int rangeStart, final int rangeEnd) {
return andRangeImpl(false, rangeStart, rangeEnd);
}
@Override
public Container iandRange(final int rangeStart, final int rangeEnd) {
return andRangeImpl(!shared, rangeStart, rangeEnd);
}
private Container andRangeImpl(final boolean inPlace, final int rangeStart, final int rangeEnd) {
if (rangeEnd <= rangeStart || isEmpty()) {
return Container.empty();
}
final int first = first();
if (first >= rangeEnd) {
return Container.empty();
}
// first < end.
if (cardinality == 1) {
if (first < rangeStart) {
return Container.empty();
}
return Container.singleton(lowbits(first));
}
// cardinality >= 2.
final int last = last();
if (last < rangeStart) {
return Container.empty();
}
if (rangeStart <= first && last <= rangeEnd - 1) {
if (inPlace) {
return this;
}
return cowRef();
}
final int rFirst = Math.max(first, rangeStart);
final int rLast = Math.min(last, rangeEnd - 1);
final ValuesInRangeContext ctx = new ValuesInRangeContext(rFirst, rLast + 1);
final int newCard = ctx.cardinalityInRange(bitmap);
if (newCard == 0) {
return Container.empty();
}
if (newCard <= 2) {
final ValuesInRangeIter it = new ValuesInRangeIter(bitmap, ctx);
if (!it.hasNext()) {
throw new IllegalStateException();
}
final short v0 = it.next();
if (!it.hasNext()) {
return Container.singleton(v0);
}
final short v1 = it.next();
if (it.hasNext()) {
throw new IllegalStateException("v=" + it.next());
}
return Container.twoValues(v0, v1);
}
if (newCard <= ArrayContainer.SWITCH_CONTAINER_CARDINALITY_THRESHOLD) {
ArrayContainer ans = new ArrayContainer(newCard);
final ValuesInRangeIter it = new ValuesInRangeIter(bitmap, ctx);
while (it.hasNext()) {
ans.content[ans.cardinality++] = it.next();
}
return ans;
}
final BitmapContainer ans;
if (inPlace) {
ans = this;
} else {
ans = new BitmapContainer();
}
long v = bitmap[ctx.iFirst] & ctx.maskFirst;
if (ctx.iFirst == ctx.iLast) {
v &= ctx.maskLast;
ans.bitmap[ctx.iFirst] = v;
ans.cardinality = Long.bitCount(v);
return ans;
}
ans.bitmap[ctx.iFirst] = v;
ans.cardinality = Long.bitCount(v);
for (int i = ctx.iFirst + 1; i < ctx.iLast; ++i) {
v = bitmap[i];
ans.bitmap[i] = v;
ans.cardinality += Long.bitCount(v);
}
v = bitmap[ctx.iLast] & ctx.maskLast;
ans.bitmap[ctx.iLast] = v;
ans.cardinality += Long.bitCount(v);
return ans;
}
@Override
public Container andNot(final ArrayContainer value2) {
if (value2.isEmpty()) {
return cowRef();
}
final BitmapContainer answer = deepCopy();
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
short v = value2.content[k];
final int i = toIntUnsigned(v) >>> 6;
long w = answer.bitmap[i];
long aft = w & (~(1L << v));
answer.bitmap[i] = aft;
answer.cardinality -= (w ^ aft) >>> v;
}
return answer.maybeSwitchContainerAfterShrinking();
}
@Override
public Container andNot(final BitmapContainer value2) {
if (value2.isEmpty()) {
return cowRef();
}
return iandNotImpl(value2, false);
}
@Override
public Container andNot(final RunContainer x) {
if (x.isEmpty()) {
return cowRef();
}
// could be rewritten as return andNot(x.toBitmapOrArrayContainer());
final BitmapContainer answer = deepCopy();
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int start = toIntUnsigned(x.getValue(rlepos));
int end = start + toIntUnsigned(x.getLength(rlepos)) + 1;
int prevOnesInRange = answer.cardinalityInRange(start, end);
resetBitmapRange(answer.bitmap, start, end);
answer.updateCardinality(prevOnesInRange, 0);
}
return answer.maybeSwitchContainerAfterShrinking();
}
@Override
public BitmapContainer cowRef() {
setCopyOnWrite();
return this;
}
@Override
public BitmapContainer deepCopy() {
return new BitmapContainer(this);
}
@Override
public boolean isEmpty() {
return cardinality == 0;
}
@Override
public boolean isAllOnes() {
return cardinality == MAX_RANGE;
}
private static int computeCardinality(final BitmapContainer bc) {
int cardinality = 0;
for (int k = 0; k < bc.bitmap.length; k++) {
cardinality += Long.bitCount(bc.bitmap[k]);
}
return cardinality;
}
protected int cardinalityInRange(int start, int end) {
if (end - start > MAX_RANGE / 2) {
int before = cardinalityInBitmapRange(bitmap, 0, start);
int after = cardinalityInBitmapRange(bitmap, end, MAX_RANGE);
return cardinality - before - after;
}
return cardinalityInBitmapRange(bitmap, start, end);
}
protected void updateCardinality(int prevOnes, int newOnes) {
int oldCardinality = cardinality;
cardinality = oldCardinality - prevOnes + newOnes;
}
@Override
public boolean contains(final short i) {
final int x = toIntUnsigned(i);
return (bitmap[x / 64] & (1L << x)) != 0;
}
@Override
public boolean contains(final int rangeStart, final int rangeEnd) {
final ValuesInRangeContext ctx = new ValuesInRangeContext(rangeStart, rangeEnd);
if (ctx.iFirst == ctx.iLast) {
return ((bitmap[ctx.iLast] & ctx.maskFirst & ctx.maskLast) == (ctx.maskFirst & ctx.maskLast));
}
if ((bitmap[ctx.iFirst] & ctx.maskFirst) != ctx.maskFirst) {
return false;
}
if ((bitmap[ctx.iLast] & ctx.maskLast) != ctx.maskLast) {
return false;
}
for (int i = ctx.iFirst + 1; i < bitmap.length && i < ctx.iLast; ++i) {
if (bitmap[i] != -1L) {
return false;
}
}
return true;
}
@Override
protected boolean contains(final BitmapContainer bitmapContainer) {
if (cardinality < bitmapContainer.cardinality) {
return false;
}
for (int i = 0; i < bitmapContainer.bitmap.length; ++i) {
final long v = bitmapContainer.bitmap[i];
if ((bitmap[i] & v) != v) {
return false;
}
}
return true;
}
@Override
protected boolean contains(final RunContainer runContainer) {
final int numRuns = runContainer.numberOfRuns();
if (cardinality < numRuns) {
return false;
}
for (int i = 0; i < numRuns; ++i) {
int start = toIntUnsigned(runContainer.getValue(i));
int length = toIntUnsigned(runContainer.getLength(i));
if (!contains(start, start + length + 1)) {
return false;
}
}
return true;
}
@Override
protected boolean contains(final ArrayContainer arrayContainer) {
if (cardinality < arrayContainer.cardinality) {
return false;
}
for (int i = 0; i < arrayContainer.cardinality; ++i) {
if (!contains(arrayContainer.content[i])) {
return false;
}
}
return true;
}
protected long bitValue(final short i) {
final int x = toIntUnsigned(i);
return (bitmap[x / 64] >>> x) & 1;
}
/**
* Fill the array with set bits
*
* @param array container (should be sufficiently large)
*/
protected void fillArray(final short[] array) {
int pos = 0;
int base = 0;
for (long bits : bitmap) {
while (bits != 0) {
array[pos++] = (short) (base + numberOfTrailingZeros(bits));
bits &= bits - 1;
}
base += 64;
}
}
int fillArrayWithSkipValue(final short[] array, final short valueToSkip, final PositionHint positionHintOut) {
int pos = 0;
int base = 0;
for (long bits : bitmap) {
while (bits != 0) {
final short v = (short) (base + numberOfTrailingZeros(bits));
if (v != valueToSkip) {
array[pos++] = v;
} else {
setIfNotNull(positionHintOut, pos);
}
bits &= bits - 1;
}
base += 64;
}
return pos;
}
@Override
public Container iflip(final short x) {
final int xAsInt = toIntUnsigned(x);
int index = xAsInt / 64;
long bef = bitmap[index];
long mask = 1L << xAsInt;
final boolean isOnAndWillBeTurnedOff = (bef & mask) != 0;
if (isOnAndWillBeTurnedOff && cardinality <= ArrayContainer.SWITCH_CONTAINER_CARDINALITY_THRESHOLD) {
if (cardinality > 3) {
final ArrayContainer ac = new ArrayContainer(cardinality - 1);
ac.loadDataWithSkipValue(this, x, null);
return ac;
}
// cardinality in { 1, 2, 3 }.
if (cardinality == 1) {
return Container.empty();
}
// cardinality is either 2 or 3, and we are going to be removing an element.
int first = -1;
int second = -1;
final ShortIterator it = getShortIterator();
while (it.hasNext()) {
final int v = it.nextAsInt();
if (v == xAsInt) {
continue;
}
if (first == -1) {
first = v;
continue;
}
second = v;
break;
}
if (first != -1) {
if (second != -1) {
if (first + 1 == second) {
return makeSingleRangeContainer(first, second + 1);
}
return makeTwoValuesContainer(lowbits(first), lowbits(second));
}
return makeSingletonContainer(lowbits(first));
}
throw new IllegalStateException("first=-1");
}
final BitmapContainer ans = deepCopyIfShared();
ans.cardinality += 1 - 2 * ((bef & mask) >>> xAsInt);
ans.bitmap[index] ^= mask;
if (isOnAndWillBeTurnedOff) {
return ans.maybeSwitchContainerAfterShrinking();
}
return ans.maybeSwitchContainerAfterGrowing();
}
@Override
public int getCardinality() {
return cardinality;
}
static final class ReverseShortIterator implements ShortAdvanceIterator {
private int curr;
private long nextWord;
private int nextPos;
private long[] bitmap;
ReverseShortIterator(final long[] bitmap) {
wrap(bitmap);
}
@Override
public boolean hasNext() {
return nextPos >= 0;
}
private void eatZeroes() {
while (nextWord == 0) {
--nextPos;
if (nextPos < 0) {
break;
}
nextWord = bitmap[nextPos];
}
}
@Override
public short next() {
final int shift = Long.numberOfLeadingZeros(nextWord) + 1;
curr = (nextPos + 1) * 64 - shift;
nextWord &= ~(1L << (64 - shift));
eatZeroes();
return (short) curr;
}
@Override
public int currAsInt() {
return curr;
}
@Override
public short curr() {
return (short) curr;
}
@Override
public int nextAsInt() {
return toIntUnsigned(next());
}
@Override
public boolean advance(final int v) {
if (curr == -1) {
if (!hasNext()) {
return false;
}
next();
}
if (v >= curr) {
return true;
}
nextPos = v >> 6;
final long mod64 = v & 63;
final long mask;
if (mod64 != 63) {
mask = (1L << (mod64 + 1)) - 1L;
} else {
mask = -1L;
}
nextWord = bitmap[nextPos] & mask;
int savedPos = nextPos;
eatZeroes();
if (nextPos < 0) {
while (bitmap[savedPos] == 0) {
++savedPos; // there is some nonzero element otherwise we would have returned earlier.
}
curr = (savedPos + 1) * 64 - Long.numberOfLeadingZeros(bitmap[savedPos]) - 1;
return false;
}
next();
return true;
}
void wrap(final long[] b) {
curr = -1;
bitmap = b;
for (nextPos = bitmap.length - 1; nextPos >= 0; --nextPos) {
if ((nextWord = bitmap[nextPos]) != 0) {
break;
}
}
}
}
@Override
public ShortAdvanceIterator getReverseShortIterator() {
return new ReverseShortIterator(bitmap);
}
static class ForwardShortIterator implements ShortIterator {
protected final long[] bitmap;
protected long w;
protected int x;
ForwardShortIterator(final long[] p) {
bitmap = p;
for (x = 0; x < bitmap.length; ++x) {
if ((w = bitmap[x]) != 0) {
break;
}
}
}
@Override
public boolean hasNext() {
return x < bitmap.length;
}
@Override
public short next() {
final short answer = (short) (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 toIntUnsigned(next());
}
}
static class ValuesInRangeContext {
public final int iFirst;
public final int iLast;
public final long maskFirst;
public final long maskLast;
public ValuesInRangeContext(final int rangeStart, final int rangeEnd) {
iFirst = rangeStart >>> 6;
final int max = rangeEnd - 1;
iLast = max >>> 6;
maskFirst = ~((1L << rangeStart) - 1);
final long lastShift = (1L << rangeEnd) - 1;
maskLast = (lastShift == 0) ? -1 : lastShift;
}
public int cardinalityInRange(final long[] bitmap) {
long w = bitmap[iFirst] & maskFirst;
if (iFirst == iLast) {
w &= maskLast;
return Long.bitCount(w);
}
int card = Long.bitCount(w);
for (int i = iFirst + 1; i < iLast; ++i) {
card += Long.bitCount(bitmap[i]);
}
card += Long.bitCount(bitmap[iLast] & maskLast);
return card;
}
}
static class ValuesInRangeIter implements ShortIterator {
private final long[] bitmap;
private int x;
private final int xlast;
private final long lastMask;
private long w;
private void eatZeroesForward() {
while (w == 0 && x < xlast) {
w = bitmap[++x];
}
if (x == xlast) {
w &= lastMask;
}
if (w == 0) {
++x;
}
}
public ValuesInRangeIter(final long[] bitmap, final ValuesInRangeContext ctx) {
this.bitmap = bitmap;
lastMask = ctx.maskLast;
xlast = ctx.iLast;
x = ctx.iFirst;
w = bitmap[x] & ctx.maskFirst;
if (x == xlast) {
w &= lastMask;
if (w == 0) {
++x;
}
return;
}
eatZeroesForward();
}
public ValuesInRangeIter(final long[] bitmap, final int rangeStart, final int rangeEnd) {
this(bitmap, new ValuesInRangeContext(rangeStart, rangeEnd));
}
@Override
public boolean hasNext() {
return x <= xlast;
}
@Override
public short next() {
final short answer = (short) (x * 64 + numberOfTrailingZeros(w));
w &= (w - 1);
eatZeroesForward();
return answer;
}
@Override
public int nextAsInt() {
return toIntUnsigned(next());
}
}
@Override
public ShortIterator getShortIterator() {
return new ForwardShortIterator(bitmap);
}
@Override
public ContainerShortBatchIterator getShortBatchIterator(final int skipCount) {
if (DEBUG && skipCount != 0 && skipCount >= cardinality) {
throw new IllegalArgumentException("initialSeek=" + skipCount);
}
return new BitmapShortBatchIterator(this, skipCount);
}
@Override
public SearchRangeIterator getShortRangeIterator(final int initialSeek) {
if (DEBUG && initialSeek != 0 && initialSeek >= cardinality) {
throw new IllegalArgumentException("initialSeek=" + initialSeek);
}
return new BitmapContainerRangeIterator(this, initialSeek);
}
@Override
public Container iadd(final int begin, final int end) {
// TODO: may need to convert to a RunContainer
if (end == begin) {
return this;
}
if (begin > end || end > MAX_RANGE) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
final BitmapContainer ans = deepCopyIfShared();
return ans.iaddImpl(begin, end);
}
private Container iaddImpl(final int begin, final int end) {
int prevOnesInRange = cardinalityInRange(begin, end);
setBitmapRange(bitmap, begin, end);
updateCardinality(prevOnesInRange, end - begin);
return maybeSwitchContainerAfterGrowing();
}
@Override
public Container iappend(final int begin, final int end) {
final BitmapContainer ans = deepCopyIfShared();
setBitmapRange(ans.bitmap, begin, end);
ans.cardinality += end - begin;
return ans.maybeSwitchContainerAfterGrowing();
}
@Override
public Container iand(final ArrayContainer b2) {
// We always produce an ArrayContainer result for and(ArrayContainer), so this will never be in-place.
return and(b2);
}
@Override
public Container iand(final BitmapContainer b2) {
return iandImpl(b2, true);
}
private Container iandImpl(final BitmapContainer b2, final boolean inPlace) {
if (b2.isEmpty()) {
return Container.empty();
}
int newCardinality = 0;
int ixFirstNonZero = -1;
int ixSecondNonZero = -1;
for (int k = 0; k < bitmap.length; ++k) {
final long r = bitmap[k] & b2.bitmap[k];
if (r == 0) {
continue;
}
if (ixFirstNonZero == -1) {
ixFirstNonZero = k;
} else if (ixSecondNonZero == -1) {
ixSecondNonZero = k;
}
newCardinality += Long.bitCount(r);
}
if (newCardinality == 0) {
return Container.empty();
}
if (newCardinality == 1) {
final long v = bitmap[ixFirstNonZero] & b2.bitmap[ixFirstNonZero];
return Container.singleton(lowbits(64 * ixFirstNonZero + numberOfTrailingZeros(v)));
}
if (newCardinality == 2) {
final long v0word = bitmap[ixFirstNonZero] & b2.bitmap[ixFirstNonZero];
final int v0 = 64 * ixFirstNonZero + numberOfTrailingZeros(v0word);
final int v1;
if (ixSecondNonZero == -1) {
// We want the last (=second) bit set in the and result.
v1 = (ixFirstNonZero + 1) * 64 - Long.numberOfLeadingZeros(v0word) - 1;
} else {
final long v1word = bitmap[ixSecondNonZero] & b2.bitmap[ixSecondNonZero];
v1 = 64 * ixSecondNonZero + numberOfTrailingZeros(v1word);
}
return Container.twoValues(lowbits(v0), lowbits(v1));
}
if (newCardinality > ArrayContainer.SWITCH_CONTAINER_CARDINALITY_THRESHOLD) {
final BitmapContainer ans = inPlace ? deepCopyIfShared() : new BitmapContainer();
for (int k = 0; k < ans.bitmap.length; ++k) {
ans.bitmap[k] = bitmap[k] & b2.bitmap[k];
}
ans.cardinality = newCardinality;
return ans;
}
final ArrayContainer ac = new ArrayContainer(newCardinality);
fillArrayAND(ac.content, bitmap, b2.bitmap);
ac.cardinality = newCardinality;
return ac;
}
@Override
public Container iand(final RunContainer x) {
if (x.isEmpty()) {
return Container.empty();
}
// could probably be replaced with return iand(x.toBitmapOrArrayContainer());
final int card = x.getCardinality();
if (card <= ArrayContainer.SWITCH_CONTAINER_CARDINALITY_THRESHOLD) {
// no point in doing it in-place
final ArrayContainer answer = new ArrayContainer(card);
answer.cardinality = 0;
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int runStart = toIntUnsigned(x.getValue(rlepos));
int runEnd = runStart + toIntUnsigned(x.getLength(rlepos));
for (int runValue = runStart; runValue <= runEnd; ++runValue) {
answer.content[answer.cardinality] = (short) runValue;
answer.cardinality += bitValue((short) runValue);
}
}
return answer.maybeSwitchContainer();
}
final BitmapContainer ans = deepCopyIfShared();
return ans.iandImpl(x);
}
private Container iandImpl(final RunContainer x) {
int start = 0;
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int end = toIntUnsigned(x.getValue(rlepos));
int prevOnes = cardinalityInRange(start, end);
resetBitmapRange(bitmap, start, end);
updateCardinality(prevOnes, 0);
start = end + toIntUnsigned(x.getLength(rlepos)) + 1;
}
int ones = cardinalityInRange(start, MAX_RANGE);
resetBitmapRange(bitmap, start, MAX_RANGE);
updateCardinality(ones, 0);
return maybeSwitchContainerAfterShrinking();
}
@Override
public Container iandNot(final ArrayContainer b2) {
if (b2.isEmpty()) {
return this;
}
final BitmapContainer ans = deepCopyIfShared();
return ans.iandNotImpl(b2);
}
private Container iandNotImpl(final ArrayContainer b2) {
for (int k = 0; k < b2.cardinality; ++k) {
final int x = toIntUnsigned(b2.content[k]);
int index = x / 64;
long bef = bitmap[index];
long mask = 1L << x;
long aft = bef & (~mask);
cardinality -= (aft - bef) >>> 63;
bitmap[index] = aft;
}
return maybeSwitchContainerAfterShrinking();
}
@Override
public Container iandNot(final BitmapContainer b2) {
if (b2.isEmpty()) {
return this;
}
return iandNotImpl(b2, true);
}
private Container iandNotImpl(final BitmapContainer b2, final boolean inPlace) {
int newCardinality = 0;
int ixFirstNonZero = -1;
int ixSecondNonZero = -1;
for (int k = 0; k < bitmap.length; ++k) {
final long v = bitmap[k] & ~b2.bitmap[k];
if (v == 0) {
continue;
}
if (ixFirstNonZero == -1) {
ixFirstNonZero = k;
} else if (ixSecondNonZero == -1) {
ixSecondNonZero = k;
}
newCardinality += Long.bitCount(v);
}
if (newCardinality == 0) {
return Container.empty();
}
if (newCardinality == 1) {
final long v = bitmap[ixFirstNonZero] & ~b2.bitmap[ixFirstNonZero];
return Container.singleton(lowbits(64 * ixFirstNonZero + numberOfTrailingZeros(v)));
}
if (newCardinality == 2) {
final long v0word = bitmap[ixFirstNonZero] & ~b2.bitmap[ixFirstNonZero];
final int v0 = 64 * ixFirstNonZero + numberOfTrailingZeros(v0word);
final int v1;
if (ixSecondNonZero == -1) {
v1 = (ixFirstNonZero + 1) * 64 - Long.numberOfLeadingZeros(v0word) - 1;
} else {
final long v2word = bitmap[ixSecondNonZero] & ~b2.bitmap[ixSecondNonZero];
v1 = 64 * ixSecondNonZero + (63 - numberOfLeadingZeros(v2word));
}
return Container.twoValues(lowbits(v0), lowbits(v1));
}
if (newCardinality > ArrayContainer.SWITCH_CONTAINER_CARDINALITY_THRESHOLD) {
final BitmapContainer ans = inPlace ? deepCopyIfShared() : new BitmapContainer();
for (int k = 0; k < bitmap.length; ++k) {
ans.bitmap[k] = bitmap[k] & ~b2.bitmap[k];
}
ans.cardinality = newCardinality;
return ans;
}
final ArrayContainer ac = new ArrayContainer(newCardinality);
fillArrayANDNOT(ac.content, bitmap, b2.bitmap);
ac.cardinality = newCardinality;
return ac.maybeSwitchContainer();
}
@Override
public Container iandNot(RunContainer x) {
if (x.isEmpty()) {
return this;
}
final BitmapContainer ans = deepCopyIfShared();
return ans.iandNotImpl(x);
}
private Container iandNotImpl(final RunContainer x) {
// could probably be replaced with return iandNot(x.toBitmapOrArrayContainer());
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int start = toIntUnsigned(x.getValue(rlepos));
int end = start + toIntUnsigned(x.getLength(rlepos)) + 1;
int prevOnesInRange = cardinalityInRange(start, end);
resetBitmapRange(bitmap, start, end);
updateCardinality(prevOnesInRange, 0);
}
return maybeSwitchContainerAfterShrinking();
}
@Override
public Container inot(final int firstOfRange, final int lastOfRange) {
final BitmapContainer ans = deepCopyIfShared();
return ans.inotImpl(firstOfRange, lastOfRange);
}
private Container inotImpl(final int firstOfRange, final int lastOfRange) {
int prevOnes = cardinalityInRange(firstOfRange, lastOfRange);
flipBitmapRange(bitmap, firstOfRange, lastOfRange);
updateCardinality(prevOnes, lastOfRange - firstOfRange - prevOnes);
return maybeSwitchContainer();
}
@Override
public Container ior(final ArrayContainer value2) {
if (shared) {
return or(value2);
}
if (isAllOnes()) {
return Container.full();
}
if (value2.isEmpty()) {
return this;
}
if (isEmpty()) {
return value2.cowRef();
}
final BitmapContainer ans = deepCopyIfShared();
return ans.iorImpl(value2);
}
private BitmapContainer iorImpl(final ArrayContainer value2) {
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
final int i = toIntUnsigned(value2.content[k]) >>> 6;
long bef = bitmap[i];
long aft = bef | (1L << value2.content[k]);
bitmap[i] = aft;
if (USE_BRANCHLESS) {
cardinality += (bef - aft) >>> 63;
} else {
if (bef != aft) {
cardinality++;
}
}
}
return this;
}
@Override
public Container ior(final BitmapContainer b2) {
if (shared) {
return or(b2);
}
if (isAllOnes() || b2.isAllOnes()) {
return Container.full();
}
if (b2.isEmpty()) {
return this;
}
if (isEmpty()) {
return b2.cowRef();
}
final BitmapContainer ans = deepCopyIfShared();
return ans.iorImpl(b2);
}
private Container iorImpl(final BitmapContainer b2) {
cardinality = 0;
for (int k = 0; k < bitmap.length; k++) {
long w = bitmap[k] | b2.bitmap[k];
bitmap[k] = w;
cardinality += Long.bitCount(w);
}
if (isAllOnes()) {
return Container.full();
}
return this;
}
@Override
public Container ior(final RunContainer x) {
if (shared) {
return or(x);
}
if (isAllOnes()) {
return Container.full();
}
if (x.isEmpty()) {
return this;
}
if (isEmpty() || x.isAllOnes()) {
return x.cowRef();
}
return iorImpl(x);
}
private Container iorImpl(final RunContainer x) {
// could probably be replaced with return ior(x.toBitmapOrArrayContainer());
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int start = toIntUnsigned(x.getValue(rlepos));
int end = start + toIntUnsigned(x.getLength(rlepos)) + 1;
int prevOnesInRange = cardinalityInRange(start, end);
setBitmapRange(bitmap, start, end);
updateCardinality(prevOnesInRange, end - start);
}
if (isAllOnes()) {
return Container.full();
}
return this;
}
@Override
public Container iremove(final int begin, final int end) {
if (end == begin) {
return this;
}
if (begin > end || end > MAX_RANGE) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
final BitmapContainer ans = deepCopyIfShared();
return ans.iremoveImpl(begin, end);
}
private Container iremoveImpl(final int begin, final int end) {
int prevOnesInRange = cardinalityInRange(begin, end);
resetBitmapRange(bitmap, begin, end);
updateCardinality(prevOnesInRange, 0);
return maybeSwitchContainerAfterShrinking();
}
@Override
public Container ixor(final ArrayContainer value2) {
if (shared) {
return xor(value2);
}
if (value2.isEmpty()) {
return this;
}
if (isEmpty()) {
return value2.cowRef();
}
return ixorImpl(value2);
}
private Container ixorImpl(final ArrayContainer value2) {
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
short vc = value2.content[k];
long mask = 1L << vc;
final int index = toIntUnsigned(vc) >>> 6;
long ba = bitmap[index];
// TODO: check whether a branchy version could be faster
cardinality += 1 - 2 * ((ba & mask) >>> vc);
bitmap[index] = ba ^ mask;
}
return maybeSwitchContainer();
}
@Override
public Container ixor(final BitmapContainer b2) {
if (shared) {
return xor(b2);
}
if (b2.isEmpty()) {
return this;
}
if (isEmpty()) {
return b2.cowRef();
}
int newCardinality = 0;
for (int k = 0; k < bitmap.length; ++k) {
newCardinality += Long.bitCount(bitmap[k] ^ b2.bitmap[k]);
}
if (newCardinality > ArrayContainer.SWITCH_CONTAINER_CARDINALITY_THRESHOLD) {
for (int k = 0; k < bitmap.length; ++k) {
bitmap[k] = bitmap[k] ^ b2.bitmap[k];
}
cardinality = newCardinality;
return this;
}
final ArrayContainer ac = new ArrayContainer(newCardinality);
fillArrayXOR(ac.content, bitmap, b2.bitmap);
ac.cardinality = newCardinality;
return ac.maybeSwitchContainer();
}
@Override
public Container ixor(final RunContainer x) {
if (shared) {
return xor(x);
}
if (x.isEmpty()) {
return this;
}
if (isEmpty()) {
return x.cowRef();
}
// could probably be replaced with return ixor(x.toBitmapOrArrayContainer());
for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
int start = toIntUnsigned(x.getValue(rlepos));
int end = start + toIntUnsigned(x.getLength(rlepos)) + 1;
int prevOnes = cardinalityInRange(start, end);
flipBitmapRange(bitmap, start, end);
updateCardinality(prevOnes, end - start - prevOnes);
}
return maybeSwitchContainer();
}
protected void loadData(final ArrayContainer arrayContainer) {
cardinality = arrayContainer.cardinality;
for (int k = 0; k < arrayContainer.cardinality; ++k) {
final short x = arrayContainer.content[k];
bitmapSet(x);
}
}
void bitmapSet(final short x) {
bitmap[toIntUnsigned(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; // i / 64 with sign extension
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;
}
@Override
public Container not(final int firstOfRange, final int lastOfRange) {
final BitmapContainer answer = deepCopy();
return answer.inot(firstOfRange, lastOfRange);
}
/**
* Computes the number of runs
*
* @return the number of runs
*/
@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)) + ((word >>> 63) & ~nextWord);
}
final long word = nextWord;
numRuns += Long.bitCount((~word) & (word << 1));
if ((word & 0x8000000000000000L) != 0) {
numRuns++;
}
return numRuns;
}
@Override
public Container or(final ArrayContainer value2) {
if (isAllOnes()) {
return Container.full();
}
if (value2.isEmpty()) {
return cowRef();
}
if (isEmpty()) {
return value2.cowRef();
}
final BitmapContainer answer = deepCopy();
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
short v = value2.content[k];
final int i = toIntUnsigned(v) >>> 6;
long w = answer.bitmap[i];
long aft = w | (1L << v);
answer.bitmap[i] = aft;
if (USE_BRANCHLESS) {
answer.cardinality += (w - aft) >>> 63;
} else {
if (w != aft) {
answer.cardinality++;
}
}
}
if (answer.isAllOnes()) {
return Container.full();
}
return answer;
}
@Override
public Container or(final BitmapContainer value2) {
if (isAllOnes() || value2.isAllOnes()) {
return Container.full();
}
if (value2.isEmpty()) {
return cowRef();
}
if (isEmpty()) {
return value2.cowRef();
}
final BitmapContainer value1 = deepCopy();
return value1.iorImpl(value2);
}
@Override
public Container or(final 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
*/
public 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;
}
@Override
public int rank(final short lowbits) {
int x = toIntUnsigned(lowbits);
int leftover = (x + 1) & 63;
int answer = 0;
for (int k = 0; k < (x + 1) / 64; ++k) {
answer += Long.bitCount(bitmap[k]);
}
if (leftover != 0) {
answer += Long.bitCount(bitmap[(x + 1) / 64] << (64 - leftover));
}
return answer;
}
@Override
public Container remove(final int begin, final int end) {
if (end == begin) {
return cowRef();
}
if ((begin > end) || (end > MAX_RANGE)) {
throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
}
final BitmapContainer answer = deepCopy();
return answer.iremoveImpl(begin, end);
}
@Override
public Container iunset(final short v) {
return unsetImpl(v, true, null);
}
@Override
public Container unset(final short v) {
return unsetImpl(v, false, null);
}
@Override
Container iunset(final short x, final PositionHint positionHint) {
return unsetImpl(x, true, positionHint);
}
@Override
Container unset(final short x, final PositionHint positionHint) {
return unsetImpl(x, false, positionHint);
}
private Container unsetImpl(
final short v,
final boolean inPlace,
final PositionHint positionHintOut) {
final int x = toIntUnsigned(v);
final int index = x / 64;
final long bef = bitmap[index];
final long mask = 1L << x;
if ((bef & mask) == 0) {
resetIfNotNull(positionHintOut);
return inPlace ? this : cowRef();
}
final int newCardinality = cardinality - 1;
if (newCardinality > ArrayContainer.SWITCH_CONTAINER_CARDINALITY_THRESHOLD) {
final long aft = bef & (~mask);
final BitmapContainer ans = inPlace ? deepCopyIfShared() : deepCopy();
ans.cardinality = newCardinality;
ans.bitmap[index] = aft;
resetIfNotNull(positionHintOut);
return ans;
}
if (newCardinality == 0) {
return Container.empty();
}
if (newCardinality == 1) {
final MutableInteger mi = new MutableInteger(-1);
searchForwardForNonzeroWord(0, (final int k, long word) -> {
if (k == index) {
word = word & ~mask;
if (word == 0) {
return false;
}
}
mi.value = 64 * k + numberOfTrailingZeros(word);
return true;
});
resetIfNotNull(positionHintOut);
return Container.singleton(lowbits(mi.value));
}
if (newCardinality == 2) {
final MutableInteger mi0 = new MutableInteger(-1);
final MutableInteger mi1 = new MutableInteger(-1);
searchForwardForNonzeroWord(0, (final int k, long word) -> {
if (k == index) {
word = word & ~mask;
if (word == 0) {
return false;
}
}
final int trail0s = numberOfTrailingZeros(word);
final int kValue = 64 * k + trail0s;
if (mi0.value == -1) {
mi0.value = kValue;
if (Long.bitCount(word) > 1) {
final int lead0s = Long.numberOfLeadingZeros(word);
mi1.value = (k + 1) * 64 - lead0s - 1;
return true;
}
return false;
}
mi1.value = kValue;
return true;
});
resetIfNotNull(positionHintOut);
return Container.twoValues(lowbits(mi0.value), lowbits(mi1.value));
}
final ArrayContainer ac = new ArrayContainer(newCardinality);
ac.loadDataWithSkipValue(this, v, positionHintOut);
return ac;
}
@Override
public Container runOptimize() {
final Container c = maybeSwitchContainer();
if (c != this) {
return c;
}
return maybeSwitchContainerToArrayOrRun();
}
@Override
public short select(final int j) {
int leftover = j;
for (int k = 0; k < bitmap.length; ++k) {
int w = Long.bitCount(bitmap[k]);
if (w > leftover) {
return (short) (k * 64 + ContainerUtil.select(bitmap[k], leftover));
}
leftover -= w;
}
throw new IllegalArgumentException("Insufficient cardinality.");
}
private class SeekToRankContext {
public final int cardinalityBeforeIndex;
public final int index;
public final long wordAtIndexAfterDiscards;
public SeekToRankContext(final int startRank) {
int cardBefore = 0;
int i = 0;
long word = 0;
int bitCount;
while (i < bitmap.length) {
word = bitmap[i];
if (word == 0) {
++i;
continue;
}
bitCount = Long.bitCount(word);
final int posCard = cardBefore + bitCount;
if (posCard >= startRank) {
break;
}
cardBefore = posCard;
++i;
}
cardinalityBeforeIndex = cardBefore;
index = i;
final int discard = startRank - cardBefore;
for (int j = 0; j < discard; ++j) {
word &= word - 1;
}
wordAtIndexAfterDiscards = word;
}
}
private ArrayContainer selectToArrayContainer(final int startRank, final int count) {
final ArrayContainer c = new ArrayContainer(count);
final SeekToRankContext seeker = new SeekToRankContext(startRank);
int i = seeker.index;
long word = seeker.wordAtIndexAfterDiscards;
int base = 64 * i;
int pos = 0;
FILLING: while (true) {
while (word != 0) {
final short v = (short) (base + numberOfTrailingZeros(word));
c.content[pos++] = v;
if (pos >= count) {
break FILLING;
}
word &= word - 1;
}
base += 64;
++i;
if (i >= bitmap.length) {
break;
}
word = bitmap[i];
}
c.cardinality = count;
return c;
}
private BitmapContainer selectToBitmapContainer(final int startRank, final int count) {
final BitmapContainer c = new BitmapContainer();
final SeekToRankContext seeker = new SeekToRankContext(startRank);
int i = seeker.index;
long word = seeker.wordAtIndexAfterDiscards;
int bitCount = Long.bitCount(word);
int preCard = 0;
while (true) {
final int preCardPlusBitCount = preCard + bitCount;
if (preCardPlusBitCount < count) {
c.bitmap[i] = word;
++i;
if (i >= bitmap.length) {
break;
}
preCard = preCardPlusBitCount;
word = bitmap[i];
bitCount = Long.bitCount(word);
continue;
} else if (bitCount + preCard == count) {
c.bitmap[i] = word;
break;
}
// bitCount + preCard > count
int remaining = count - preCard;
do {
final long v = Long.lowestOneBit(word);
c.bitmap[i] |= v;
word &= word - 1;
--remaining;
} while (remaining > 0);
break;
}
c.cardinality = count;
return c;
}
@Override
public Container select(final int startRank, final int endRank) {
if (endRank <= startRank || endRank > cardinality) {
throw new IllegalArgumentException(
"startRank=" + startRank + ", endRank=" + endRank + ", cardinality=" + cardinality);
}
final int card = endRank - startRank;
if (card < ArrayContainer.DEFAULT_MAX_SIZE) {
return selectToArrayContainer(startRank, card);
}
return selectToBitmapContainer(startRank, card);
}
private int findSecondHalf(final int index, final int rest, final int bitCountPreIndex) {
long preMask = (1L << rest) - 1;
long bitsAtIndex = bitmap[index];
long preMasked = bitsAtIndex & preMask;
int pos = bitCountPreIndex + Long.bitCount(preMasked);
long mask = 1L << rest;
long masked = bitsAtIndex & mask;
if (masked != 0) {
return pos;
} else {
return -pos - 1;
}
}
@Override
public int find(final short x) {
final int value = toIntUnsigned(x);
int index = value >>> 6; // value / 64.
int r = value & 63; // value % 64.
int bitCountPreIndex = 0;
for (int k = 0; k < index; ++k) {
bitCountPreIndex += Long.bitCount(bitmap[k]);
}
return findSecondHalf(index, r, bitCountPreIndex);
}
@Override
public void selectRanges(final RangeConsumer outValues, final RangeIterator inPositions) {
if (!inPositions.hasNext()) {
return;
}
int ostart = -1;
int oend = -1; // inclusive
int wordIndex = 0;
if (isEmpty()) {
throw new IllegalArgumentException("select Ranges for invalid pos=" + inPositions.start());
}
int wordAccumBitCount = Long.bitCount(bitmap[0]);
int prevWordAccumBitCount = 0;
do {
inPositions.next();
int istart = inPositions.start();
while (istart >= wordAccumBitCount) {
++wordIndex;
if (wordIndex >= bitmap.length) {
throw new IllegalArgumentException("selectRanges for invalid pos=" + istart);
}
prevWordAccumBitCount = wordAccumBitCount;
wordAccumBitCount += Long.bitCount(bitmap[wordIndex]);
}
int key = wordIndex * 64 + ContainerUtil.select(bitmap[wordIndex], istart - prevWordAccumBitCount);
if (ostart == -1) {
ostart = oend = key;
} else {
if (oend + 1 == key) {
oend = key;
} else {
outValues.accept(ostart, oend + 1);
ostart = oend = key;
}
}
int iend = inPositions.end();
for (int j = istart + 1; j < iend; ++j) {
while (j >= wordAccumBitCount) {
++wordIndex;
if (wordIndex >= bitmap.length) {
throw new IllegalArgumentException("selectRanges for invalid pos=" + j);
}
prevWordAccumBitCount = wordAccumBitCount;
wordAccumBitCount += Long.bitCount(bitmap[wordIndex]);
}
key = wordIndex * 64 + ContainerUtil.select(bitmap[wordIndex], j - prevWordAccumBitCount);
if (oend + 1 == key) {
oend = key;
} else {
outValues.accept(ostart, oend + 1);
ostart = oend = key;
}
}
} while (inPositions.hasNext());
outValues.accept(ostart, oend + 1);
}
@Override
public boolean findRanges(final RangeConsumer outPositions, final RangeIterator inValues, final int maxPos) {
if (!inValues.hasNext()) {
return false;
}
if (isEmpty()) {
throw new IllegalArgumentException("find Ranges for invalid key=" + inValues.start());
}
int ostart = -1;
int oend = -1; // inclusive
int k = 0;
int kAccumBitCount = 0;
do {
inValues.next();
int istart = inValues.start();
int kistart = istart >>> 6; // istart / 64.
int ristart = istart & 63; // istart % 64.
while (k < kistart) {
if (k >= bitmap.length) {
throw new IllegalArgumentException("findRanges for invalid key=" + istart);
}
kAccumBitCount += Long.bitCount(bitmap[k]);
++k;
}
int pos = findSecondHalf(k, ristart, kAccumBitCount);
if (pos < 0) {
throw new IllegalArgumentException("findRanges for invalid key=" + istart);
}
if (ostart == -1) {
if (pos > maxPos) {
return true;
}
ostart = oend = pos;
} else {
if (pos > maxPos) {
outPositions.accept(ostart, oend + 1);
return true;
}
if (oend + 1 == pos) {
oend = pos;
} else {
outPositions.accept(ostart, oend + 1);
ostart = oend = pos;
}
}
int iend = inValues.end();
for (int j = istart + 1; j < iend; ++j) {
int kj = j >>> 6; // j / 64.
int rj = j & 63; // j % 64.
while (k < kj) {
if (k >= bitmap.length) {
throw new IllegalArgumentException("findRanges for invalid key=" + j);
}
kAccumBitCount += Long.bitCount(bitmap[k]);
++k;
}
// k == j
pos = findSecondHalf(k, rj, kAccumBitCount);
if (pos < 0) {
throw new IllegalArgumentException("findRanges for invalid key=" + j);
// Note we do not validate potential values between istart and iend, just the endpoints of the
// range.
}
if (pos > maxPos) {
outPositions.accept(ostart, oend + 1);
return true;
}
if (oend + 1 == pos) {
oend = pos;
} else {
outPositions.accept(ostart, oend + 1);
ostart = oend = pos;
}
}
} while (inValues.hasNext());
outPositions.accept(ostart, oend + 1);
return false;
}
/**
* Copies the data to an array container
*
* @return the array container
*/
public ArrayContainer toArrayContainer() {
final ArrayContainer ac = new ArrayContainer(cardinality);
ac.loadData(this);
if (ac.getCardinality() != cardinality) {
throw new RuntimeException("Internal error.");
}
return ac;
}
public RunContainer toRunContainer() {
return new RunContainer(this);
}
@Override
public void trim() {}
@Override
public Container xor(final ArrayContainer value2) {
if (value2.isEmpty()) {
return cowRef();
}
if (isEmpty()) {
return value2.cowRef();
}
final BitmapContainer answer = deepCopy();
int c = value2.cardinality;
for (int k = 0; k < c; ++k) {
short vc = value2.content[k];
final int index = toIntUnsigned(vc) >>> 6;
final long mask = 1L << vc;
final long val = answer.bitmap[index];
// TODO: check whether a branchy version could be faster
answer.cardinality += 1 - 2 * ((val & mask) >>> vc);
answer.bitmap[index] = val ^ mask;
}
return answer.maybeSwitchContainer();
}
@Override
public Container xor(BitmapContainer value2) {
if (value2.isEmpty()) {
return cowRef();
}
if (isEmpty()) {
return value2.cowRef();
}
int newCardinality = 0;
for (int k = 0; k < bitmap.length; ++k) {
newCardinality += Long.bitCount(bitmap[k] ^ value2.bitmap[k]);
}
if (newCardinality > ArrayContainer.SWITCH_CONTAINER_CARDINALITY_THRESHOLD) {
final BitmapContainer answer = new BitmapContainer();
for (int k = 0; k < answer.bitmap.length; ++k) {
answer.bitmap[k] = bitmap[k] ^ value2.bitmap[k];
}
answer.cardinality = newCardinality;
return answer;
}
final ArrayContainer ac = new ArrayContainer(newCardinality);
fillArrayXOR(ac.content, bitmap, value2.bitmap);
ac.cardinality = newCardinality;
return ac.maybeSwitchContainer();
}
@Override
public Container xor(RunContainer x) {
return x.xor(this);
}
private boolean acceptBitsFrom(final int x, final long win, final ShortConsumer sc) {
long w = win;
while (w != 0) {
final int v = x * 64 + numberOfTrailingZeros(w);
final boolean wantMore = sc.accept((short) v);
if (!wantMore) {
return false;
}
w &= w - 1;
}
return true;
}
@Override
public boolean forEach(final ShortConsumer sc) {
for (int x = 0; x < bitmap.length; ++x) {
long w = bitmap[x];
if (!acceptBitsFrom(x, w, sc)) {
return false;
}
}
return true;
}
@Override
public boolean forEach(final int rankOffset, final ShortConsumer sc) {
int rank = 0;
int x = 0;
while (rank < rankOffset && x < bitmap.length) {
long w = bitmap[x];
if (w != 0) {
final int bitCount = Long.bitCount(w);
final int nextRank = rank + bitCount;
if (nextRank > rankOffset) {
while (rank < rankOffset) {
w &= w - 1;
++rank;
}
if (!acceptBitsFrom(x, w, sc)) {
return false;
}
++x;
break;
}
rank = nextRank;
}
++x;
}
for (; x < bitmap.length; ++x) {
long w = bitmap[x];
if (!acceptBitsFrom(x, w, sc)) {
return false;
}
}
return true;
}
@Override
public boolean forEachRange(final int rankOffset, final ShortRangeConsumer sc) {
int rank = 0;
int x = 0;
long w = 0;
while (rank <= rankOffset && x < bitmap.length) {
w = bitmap[x];
if (w != 0) {
final int bitCount = Long.bitCount(w);
final int nextRank = rank + bitCount;
if (nextRank >= rankOffset) {
while (rank < rankOffset) {
w &= w - 1;
++rank;
}
break;
}
rank = nextRank;
}
++x;
}
if (w == 0) {
do {
if (x >= bitmap.length - 1) {
return true;
}
++x;
w = bitmap[x];
} while (w == 0);
}
int xTimes64 = x * 64;
int pendingStart = xTimes64 + numberOfTrailingZeros(w);
int pendingEnd = pendingStart;
w &= w - 1;
while (w != 0) {
final int v = xTimes64 + numberOfTrailingZeros(w);
if (pendingEnd + 1 == v) {
pendingEnd = v;
} else {
if (!sc.accept((short) pendingStart, (short) pendingEnd)) {
return false;
}
pendingStart = pendingEnd = v;
}
w &= w - 1;
}
++x;
for (; x < bitmap.length; ++x) {
w = bitmap[x];
if (w != 0) {
xTimes64 = x * 64;
do {
final int v = xTimes64 + numberOfTrailingZeros(w);
if (pendingEnd + 1 == v) {
pendingEnd = v;
} else {
if (!sc.accept((short) pendingStart, (short) pendingEnd)) {
return false;
}
pendingStart = pendingEnd = v;
}
w &= w - 1;
} while (w != 0);
}
}
return sc.accept((short) pendingStart, (short) pendingEnd);
}
@Override
public BitmapContainer toBitmapContainer() {
return this;
}
@Override
public int nextValue(short fromValue) {
return nextSetBit(toIntUnsigned(fromValue));
}
private void assertNonEmpty() {
if (cardinality == 0) {
throw new NoSuchElementException("Empty " + getContainerName());
}
}
@Override
public int first() {
assertNonEmpty();
return first(0, null);
}
private int first(final int startPos, final MutableInteger positionOutput) {
int i = startPos;
while (i < bitmap.length - 1 && bitmap[i] == 0) {
++i;
}
if (positionOutput != null) {
positionOutput.value = i;
}
return i * 64 + numberOfTrailingZeros(bitmap[i]);
}
@FunctionalInterface
private interface WordMatcher {
boolean accept(int pos, long word);
}
private void searchForwardForNonzeroWord(final int startPos, final WordMatcher m) {
for (int i = startPos; i < bitmap.length; ++i) {
final long word = bitmap[i];
if (word == 0) {
continue;
}
if (m.accept(i, bitmap[i])) {
return;
}
}
}
@Override
public int last() {
assertNonEmpty();
return last(bitmap.length - 1, null);
}
private int last(final int startPos, final MutableInteger positionOutput) {
int i = startPos;
while (i > 0 && bitmap[i] == 0) {
--i;
}
if (positionOutput != null) {
positionOutput.value = i;
}
return (i + 1) * 64 - Long.numberOfLeadingZeros(bitmap[i]) - 1;
}
@Override
public boolean subsetOf(final ArrayContainer c) {
if (isEmpty()) {
return true;
}
if (c.isEmpty()) {
return false;
}
// don't do a first/last based check since those are not cached in bitmap.
if (getCardinality() > c.getCardinality()) {
return false;
}
// This is not commonly expected as a bitmap container should have more elements
// than an array container in normal circumstances.
int count = 0;
for (int i = 0; i < c.getCardinality(); ++i) {
short k = c.content[i];
if (contains(k)) {
++count;
if (count == getCardinality()) {
return true;
}
}
}
return false;
}
@Override
public boolean subsetOf(BitmapContainer c) {
if (isEmpty()) {
return true;
}
if (c.isEmpty()) {
return false;
}
// don't do a first/last based check since those are not cached in bitmap.
if (getCardinality() > c.getCardinality()) {
return false;
}
for (int i = 0; i < bitmap.length; ++i) {
long w1 = bitmap[i];
long w2 = c.bitmap[i];
if ((w1 | w2) != w2) {
return false;
}
}
return true;
}
@Override
public boolean subsetOf(final RunContainer c) {
if (isEmpty()) {
return true;
}
if (c.isEmpty()) {
return false;
}
// don't do a first/last based check since those are not cached in bitmap;
// don't do a cardinality based check since that is not cached in run.
// This is not commonly expected as a bitmap container should have more elements
// than a run container in normal circumstances.
int count = 0;
for (int i = 0; i < c.nbrruns; ++i) {
int ival = toIntUnsigned(c.getValue(i));
int ilen = toIntUnsigned(c.getLength(i));
for (int j = ival; j <= ival + ilen; ++j) {
if (contains(lowbits(j))) {
++count;
if (count == getCardinality()) {
return true;
}
}
}
}
return false;
}
final long endMask(final int endBit) {
if (endBit == 63) {
return ~0L;
}
return (1L << (endBit + 1)) - 1;
}
// rangeEnd is exclusive.
@Override
public boolean overlapsRange(final int rangeStart, final int rangeEnd) {
final int ifirst = rangeStart >>> 6;
final int last = rangeEnd - 1;
final int ilast = last >>> 6;
final int startBit = rangeStart & 63;
final int endBit = last & 63;
final long startMask = ~((1L << startBit) - 1);
if (ifirst == ilast) {
final long endMask = endMask(endBit);
final long v = bitmap[ifirst] & startMask & endMask;
return v != 0;
}
long v = bitmap[ifirst] & startMask;
if (v != 0) {
return true;
}
int j = ifirst + 1;
while (j < ilast) {
v = bitmap[j];
if (v != 0) {
return true;
}
++j;
}
final long endMask = endMask(endBit);
v = bitmap[ilast] & endMask;
return v != 0;
}
@Override
public boolean overlaps(final ArrayContainer c) {
// A bitmap container should have more elements
// than an array container in normal circumstances,
// and a bitmap container has O(1) contains checks.
for (int i = 0; i < c.getCardinality(); ++i) {
final short k = c.content[i];
if (contains(k)) {
return true;
}
}
return false;
}
@Override
public boolean overlaps(final BitmapContainer c) {
for (int i = 0; i < bitmap.length; ++i) {
long w1 = bitmap[i];
long w2 = c.bitmap[i];
if ((w1 & w2) != 0) {
return true;
}
}
return false;
}
@Override
public boolean overlaps(final RunContainer c) {
// A bitmap container should have more elements
// than a run container in normal circumstances,
// and a bitmap container has O(1) contains checks.
for (int i = 0; i < c.nbrruns; ++i) {
int ival = toIntUnsigned(c.getValue(i));
int ilen = toIntUnsigned(c.getLength(i));
for (int j = ival; j <= ival + ilen; ++j) {
if (contains(lowbits(j))) {
return true;
}
}
}
return false;
}
@Override
public void setCopyOnWrite() {
shared = true;
}
private BitmapContainer deepCopyIfShared() {
return shared ? deepCopy() : this;
}
@Override
public int bytesAllocated() {
return BITMAP_SIZE_IN_BYTES;
}
@Override
public int bytesUsed() {
return bytesAllocated();
}
@Override
public Container toLargeContainer() {
return this;
}
@Override
public void validate() {
final int computedCard = computeCardinality(this);
if (computedCard != cardinality) {
throw new IllegalStateException("computedCard=" + computedCard + ", cardinality=" + cardinality);
}
}
@Override
public boolean isShared() {
return shared;
}
}