@Override
 public Container remove(int begin, int end) {
   BitmapContainer answer = clone();
   Util.resetBitmapRange(answer.bitmap, begin, end);
   answer.computeCardinality();
   if (answer.getCardinality() <= ArrayContainer.DEFAULT_MAX_SIZE)
     return answer.toArrayContainer();
   return answer;
 }
 @Override
 public Container andNot(final ArrayContainer value2) {
   final BitmapContainer answer = clone();
   for (int k = 0; k < value2.cardinality; ++k) {
     final int i = Util.toIntUnsigned(value2.content[k]) >>> 6;
     answer.bitmap[i] = answer.bitmap[i] & (~(1l << value2.content[k]));
     answer.cardinality -= (answer.bitmap[i] ^ this.bitmap[i]) >>> value2.content[k];
   }
   if (answer.cardinality <= ArrayContainer.DEFAULT_MAX_SIZE) return answer.toArrayContainer();
   return answer;
 }
 @Override
 public Container xor(final ArrayContainer value2) {
   final BitmapContainer answer = clone();
   for (int k = 0; k < value2.getCardinality(); ++k) {
     final int index = Util.toIntUnsigned(value2.content[k]) >>> 6;
     answer.cardinality +=
         1 - 2 * ((answer.bitmap[index] & (1l << value2.content[k])) >>> value2.content[k]);
     answer.bitmap[index] = answer.bitmap[index] ^ (1l << value2.content[k]);
   }
   if (answer.cardinality <= ArrayContainer.DEFAULT_MAX_SIZE) return answer.toArrayContainer();
   return answer;
 }
 @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 = Util.toIntUnsigned(x.getValue(rlepos));
     int end = start + Util.toIntUnsigned(x.getLength(rlepos)) + 1;
     Util.resetBitmapRange(answer.bitmap, start, end);
   }
   answer.computeCardinality();
   if (answer.getCardinality() > ArrayContainer.DEFAULT_MAX_SIZE) return answer;
   else return answer.toArrayContainer();
 }
  // 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;
  }