@Override public void deserialize(DataInput in) throws IOException { // little endian this.cardinality = 0; for (int k = 0; k < bitmap.length; ++k) { bitmap[k] = Long.reverseBytes(in.readLong()); this.cardinality += Long.bitCount(bitmap[k]); } }
@Override public int rank(short lowbits) { int x = Util.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 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)) { long t = bitset & -bitset; ac.content[pos++] = (short) (k * 64 + Long.bitCount(t - 1)); ac.cardinality++; bitset ^= t; } } return ac; } BitmapContainer bc = new BitmapContainer(maxcardinality, this.bitmap); int s = Util.toIntUnsigned(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] = (bc.bitmap[s / 64] << (64 - lastword)) >> (64 - lastword); } return bc; }
@Override public MappeableContainer or(final MappeableArrayContainer value2) { final MappeableArrayContainer value1 = this; final int totalCardinality = value1.getCardinality() + value2.getCardinality(); if (totalCardinality > DEFAULT_MAX_SIZE) { // it could be a bitmap! final MappeableBitmapContainer bc = new MappeableBitmapContainer(); if (!BufferUtil.isBackedBySimpleArray(bc.bitmap)) throw new RuntimeException("Should not happen. Internal bug."); long[] bitArray = bc.bitmap.array(); if (BufferUtil.isBackedBySimpleArray(value2.content)) { short[] sarray = value2.content.array(); for (int k = 0; k < value2.cardinality; ++k) { final int i = BufferUtil.toIntUnsigned(sarray[k]) >>> 6; bitArray[i] |= (1l << sarray[k]); } } else for (int k = 0; k < value2.cardinality; ++k) { short v2 = value2.content.get(k); final int i = BufferUtil.toIntUnsigned(v2) >>> 6; bitArray[i] |= (1l << v2); } if (BufferUtil.isBackedBySimpleArray(this.content)) { short[] sarray = this.content.array(); for (int k = 0; k < this.cardinality; ++k) { final int i = BufferUtil.toIntUnsigned(sarray[k]) >>> 6; bitArray[i] |= (1l << sarray[k]); } } else for (int k = 0; k < this.cardinality; ++k) { final int i = BufferUtil.toIntUnsigned(this.content.get(k)) >>> 6; bitArray[i] |= (1l << this.content.get(k)); } bc.cardinality = 0; int len = bc.bitmap.limit(); for (int index = 0; index < len; ++index) { bc.cardinality += Long.bitCount(bitArray[index]); } if (bc.cardinality <= DEFAULT_MAX_SIZE) return bc.toArrayContainer(); return bc; } final MappeableArrayContainer answer = new MappeableArrayContainer(totalCardinality); if (BufferUtil.isBackedBySimpleArray(value1.content) && BufferUtil.isBackedBySimpleArray(value2.content)) answer.cardinality = org.roaringbitmap.Util.unsignedUnion2by2( value1.content.array(), value1.getCardinality(), value2.content.array(), value2.getCardinality(), answer.content.array()); else answer.cardinality = BufferUtil.unsignedUnion2by2( value1.content, value1.getCardinality(), value2.content, value2.getCardinality(), answer.content.array()); return answer; }
@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); } long word = nextWord; numRuns += Long.bitCount((~word) & (word << 1)); if ((word & 0x8000000000000000L) != 0) numRuns++; return numRuns; }
@Override public Container ior(final BitmapContainer b2) { this.cardinality = 0; for (int k = 0; k < this.bitmap.length; k++) { this.bitmap[k] |= b2.bitmap[k]; this.cardinality += Long.bitCount(this.bitmap[k]); } return this; }
/** * Fill the array with set bits * * @param array container (should be sufficiently large) */ protected void fillArray(final short[] array) { int pos = 0; for (int k = 0; k < bitmap.length; ++k) { long bitset = bitmap[k]; while (bitset != 0) { long t = bitset & -bitset; array[pos++] = (short) (k * 64 + Long.bitCount(t - 1)); bitset ^= t; } } }
@Override public short next() { long t = w & -w; short answer = (short) (x * 64 + Long.bitCount(t - 1)); w ^= t; while (w == 0) { ++x; if (x == bitmap.length) break; w = bitmap[x]; } return answer; }
@Override public int nextAsInt() { long t = w & -w; int answer = (x + 1) * 64 - 1 - Long.bitCount(t - 1); w ^= t; while (w == 0) { --x; if (x < 0) break; w = Long.reverse(bitmap[x]); } return answer; }
@Override public short select(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 + Util.select(bitmap[k], leftover)); } leftover -= w; } throw new IllegalArgumentException("Insufficient cardinality."); }
@Override public int nextAsInt() { long t = w & -w; int answer = x * 64 + Long.bitCount(t - 1); w ^= t; while (w == 0) { ++x; if (x == bitmap.length) break; w = bitmap[x]; } return answer; }
@Override public void fillLeastSignificant16bits(int[] x, int i, int mask) { int pos = i; for (int k = 0; k < bitmap.length; ++k) { long bitset = bitmap[k]; while (bitset != 0) { long t = bitset & -bitset; x[pos++] = (k * 64 + Long.bitCount(t - 1)) | mask; bitset ^= t; } } }
/** * 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 < 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 BitmapContainer value2) { if (USE_IN_PLACE) { BitmapContainer value1 = this.clone(); return value1.ior(value2); } final BitmapContainer answer = new BitmapContainer(); answer.cardinality = 0; for (int k = 0; k < answer.bitmap.length; ++k) { answer.bitmap[k] = this.bitmap[k] | value2.bitmap[k]; answer.cardinality += Long.bitCount(answer.bitmap[k]); } return answer; }
@Override public Container ixor(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.fillArrayXOR(ac.content, this.bitmap, b2.bitmap); ac.cardinality = newCardinality; return ac; }
@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; }
/** Recomputes the cardinality of the bitmap. */ protected void computeCardinality() { this.cardinality = 0; for (int k = 0; k < this.bitmap.length; k++) { this.cardinality += Long.bitCount(this.bitmap[k]); } }
// answer could be a new BitmapContainer, or (for inplace) it can be // "this" private Container not(BitmapContainer answer, final int firstOfRange, final int lastOfRange) { assert bitmap.length == MAX_CAPACITY / 64; // checking assumption // that partial // bitmaps are not // allowed // an easy case for full range, should be common if (lastOfRange - firstOfRange == MAX_CAPACITY) { final int newCardinality = MAX_CAPACITY - cardinality; for (int k = 0; k < this.bitmap.length; ++k) answer.bitmap[k] = ~this.bitmap[k]; answer.cardinality = newCardinality; if (newCardinality <= ArrayContainer.DEFAULT_MAX_SIZE) return answer.toArrayContainer(); return answer; } // could be optimized to first determine the answer cardinality, // rather than update/create bitmap and then possibly convert int cardinalityChange = 0; final int rangeFirstWord = firstOfRange / 64; final int rangeFirstBitPos = firstOfRange & 63; final int rangeLastWord = (lastOfRange - 1) / 64; final long rangeLastBitPos = (lastOfRange - 1) & 63; // if not in place, we need to duplicate stuff before // rangeFirstWord and after rangeLastWord if (answer != this) { System.arraycopy(bitmap, 0, answer.bitmap, 0, rangeFirstWord); System.arraycopy( bitmap, rangeLastWord + 1, answer.bitmap, rangeLastWord + 1, bitmap.length - (rangeLastWord + 1)); } // unfortunately, the simple expression gives the wrong mask for // rangeLastBitPos==63 // no branchless way comes to mind final long maskOnLeft = (rangeLastBitPos == 63) ? -1L : (1L << (rangeLastBitPos + 1)) - 1; long mask = -1L; // now zero out stuff in the prefix mask ^= ((1L << rangeFirstBitPos) - 1); if (rangeFirstWord == rangeLastWord) { // range starts and ends in same word (may have // unchanged bits on both left and right) mask &= maskOnLeft; cardinalityChange = -Long.bitCount(bitmap[rangeFirstWord]); answer.bitmap[rangeFirstWord] = bitmap[rangeFirstWord] ^ mask; cardinalityChange += Long.bitCount(answer.bitmap[rangeFirstWord]); answer.cardinality = cardinality + cardinalityChange; if (answer.cardinality <= ArrayContainer.DEFAULT_MAX_SIZE) return answer.toArrayContainer(); return answer; } // range spans words cardinalityChange += -Long.bitCount(bitmap[rangeFirstWord]); answer.bitmap[rangeFirstWord] = bitmap[rangeFirstWord] ^ mask; cardinalityChange += Long.bitCount(answer.bitmap[rangeFirstWord]); cardinalityChange += -Long.bitCount(bitmap[rangeLastWord]); answer.bitmap[rangeLastWord] = bitmap[rangeLastWord] ^ maskOnLeft; cardinalityChange += Long.bitCount(answer.bitmap[rangeLastWord]); // negate the words, if any, strictly between first and last for (int i = rangeFirstWord + 1; i < rangeLastWord; ++i) { cardinalityChange += (64 - 2 * Long.bitCount(bitmap[i])); answer.bitmap[i] = ~bitmap[i]; } answer.cardinality = cardinality + cardinalityChange; if (answer.cardinality <= ArrayContainer.DEFAULT_MAX_SIZE) return answer.toArrayContainer(); return answer; }