All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.roaringbitmap.buffer.MappeableBitmapContainer Maven / Gradle / Ivy

Go to download

Roaring bitmaps are compressed bitmaps (also called bitsets) which tend to outperform conventional compressed bitmaps such as WAH or Concise.

There is a newer version: 1.3.0
Show newest version
/*
 * (c) the authors Licensed under the Apache License, Version 2.0.
 */

package org.roaringbitmap.buffer;

import org.roaringbitmap.*;

import java.io.DataOutput;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.nio.ByteBuffer;
import java.nio.LongBuffer;
import java.util.Iterator;

import static java.lang.Long.numberOfLeadingZeros;
import static java.lang.Long.numberOfTrailingZeros;
import static java.nio.ByteOrder.LITTLE_ENDIAN;


/**
 * Simple bitset-like container. Unlike org.roaringbitmap.BitmapContainer, this class uses a
 * LongBuffer to store data.
 */
public final class MappeableBitmapContainer extends MappeableContainer implements Cloneable {
  protected 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
   * operation
   */
  private static final boolean USE_BRANCHLESS = true;


  // the parameter is for overloading and symmetry with ArrayContainer
  protected static int serializedSizeInBytes(int unusedCardinality) {
    return MAX_CAPACITY / 8;
  }

  LongBuffer 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 MappeableBitmapContainer() {
    this.cardinality = 0;
    this.bitmap = LongBuffer.allocate(MAX_CAPACITY / 64);
  }

  /**
   * Creates a new bitmap container from a non-mappeable one. This copies the data.
   * 
   * @param bc the original container
   */
  public MappeableBitmapContainer(BitmapContainer bc) {
    this.cardinality = bc.getCardinality();
    this.bitmap = bc.toLongBuffer();
  }

  /**
   * Create a bitmap container with a run of ones from firstOfRun to lastOfRun, inclusive 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 MappeableBitmapContainer(final int firstOfRun, final int lastOfRun) {
    this.cardinality = lastOfRun - firstOfRun;
    this.bitmap = LongBuffer.allocate(MAX_CAPACITY / 64);
    Util.setBitmapRange(bitmap.array(), firstOfRun, lastOfRun);
  }



  MappeableBitmapContainer(int newCardinality, LongBuffer newBitmap) {
    this.cardinality = newCardinality;
    LongBuffer tmp = newBitmap.duplicate(); // for thread safety
    this.bitmap = LongBuffer.allocate(tmp.limit());
    tmp.rewind();
    this.bitmap.put(tmp);
  }

  /**
   * Construct a new BitmapContainer backed by the provided LongBuffer.
   * 
   * @param array LongBuffer where the data is stored
   * @param initCardinality cardinality (number of values stored)
   */
  public MappeableBitmapContainer(final LongBuffer array, final int initCardinality) {
    if (array.limit() != MAX_CAPACITY / 64) {
      throw new RuntimeException("Mismatch between buffer and storage requirements: "
          + array.limit() + " vs. " + MAX_CAPACITY / 64);
    }
    this.cardinality = initCardinality;
    this.bitmap = array;
  }

  @Override
  public MappeableContainer 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 + ")");
    }
    MappeableBitmapContainer answer = clone();
    int prevOnesInRange = answer.cardinalityInRange(begin, end);
    BufferUtil.setBitmapRange(answer.bitmap, begin, end);
    answer.updateCardinality(prevOnesInRange, end - begin);
    return answer;
  }


  @Override
  public MappeableContainer add(final char i) {
    final long previous = bitmap.get(i / 64);
    final long newv = previous | (1L << i);
    bitmap.put(i / 64, newv);
    if (USE_BRANCHLESS) {
      cardinality += (int)((previous ^ newv) >>> i);
    } else if (previous != newv) {
      cardinality++;
    }
    return this;
  }

  @Override
  public boolean isEmpty() {
    return cardinality == 0;
  }

  @Override
  public MappeableArrayContainer and(final MappeableArrayContainer value2) {

    final MappeableArrayContainer answer = new MappeableArrayContainer(value2.content.limit());
    if (!BufferUtil.isBackedBySimpleArray(answer.content)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    char[] sarray = answer.content.array();
    if (BufferUtil.isBackedBySimpleArray(value2.content)) {
      char[] c = value2.content.array();
      int ca = value2.cardinality;
      for (int k = 0; k < ca; ++k) {
        char v = c[k];
        sarray[answer.cardinality] = v;
        answer.cardinality += (int)this.bitValue(v);
      }

    } else {
      int ca = value2.cardinality;
      for (int k = 0; k < ca; ++k) {
        char v = value2.content.get(k);
        sarray[answer.cardinality] = v;
        answer.cardinality += (int)this.bitValue(v);
      }
    }
    return answer;
  }

  @Override
  public MappeableContainer and(final MappeableBitmapContainer value2) {
    int newCardinality = 0;
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)
        && BufferUtil.isBackedBySimpleArray(value2.bitmap)) {
      long[] tb = this.bitmap.array();
      long[] v2b = value2.bitmap.array();
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        newCardinality += Long.bitCount(tb[k] & v2b[k]);
      }
    } else {
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        newCardinality += Long.bitCount(this.bitmap.get(k) & value2.bitmap.get(k));
      }
    }
    if (newCardinality > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      final MappeableBitmapContainer answer = new MappeableBitmapContainer();
      if (!BufferUtil.isBackedBySimpleArray(answer.bitmap)) {
        throw new RuntimeException("Should not happen. Internal bug.");
      }
      long[] bitArray = answer.bitmap.array();
      if (BufferUtil.isBackedBySimpleArray(this.bitmap)
          && BufferUtil.isBackedBySimpleArray(value2.bitmap)) {
        long[] tb = this.bitmap.array();
        long[] v2b = value2.bitmap.array();
        int len = this.bitmap.limit();
        for (int k = 0; k < len; ++k) {
          bitArray[k] = tb[k] & v2b[k];
        }
      } else {
        int len = this.bitmap.limit();
        for (int k = 0; k < len; ++k) {
          bitArray[k] = this.bitmap.get(k) & value2.bitmap.get(k);
        }
      }
      answer.cardinality = newCardinality;
      return answer;
    }
    final MappeableArrayContainer ac = new MappeableArrayContainer(newCardinality);
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)
        && BufferUtil.isBackedBySimpleArray(value2.bitmap)) {
      org.roaringbitmap.Util.fillArrayAND(ac.content.array(), this.bitmap.array(),
          value2.bitmap.array());
    } else {
      BufferUtil.fillArrayAND(ac.content.array(), this.bitmap, value2.bitmap);
    }
    ac.cardinality = newCardinality;
    return ac;
  }

  @Override
  public MappeableContainer and(final MappeableRunContainer value2) {
    return value2.and(this);
  }



  @Override
  public MappeableContainer andNot(final MappeableArrayContainer value2) {
    final MappeableBitmapContainer answer = clone();
    if (!BufferUtil.isBackedBySimpleArray(answer.bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] bitArray = answer.bitmap.array();
    if (BufferUtil.isBackedBySimpleArray(value2.content)
        && BufferUtil.isBackedBySimpleArray(this.bitmap)) {
      char[] v2 = value2.content.array();
      int c = value2.cardinality;
      for (int k = 0; k < c; ++k) {
        char v = v2[k];
        final int i = v >>> 6;
        long w = bitArray[i];
        long aft = w & (~(1L << v));
        bitArray[i] = aft;
        answer.cardinality -= (w ^ aft) >>> v;
      }
    } else {
      int c = value2.cardinality;
      for (int k = 0; k < c; ++k) {
        char v2 = value2.content.get(k);
        final int i = (v2) >>> 6;
        long w = bitArray[i];
        long aft = bitArray[i] & (~(1L << v2));
        bitArray[i] = aft;
        answer.cardinality -= (w ^ aft) >>> v2;
      }
    }
    if (answer.cardinality <= MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      return answer.toArrayContainer();
    }
    return answer;
  }



  @Override
  public MappeableContainer andNot(final MappeableBitmapContainer value2) {

    int newCardinality = 0;
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)
        && BufferUtil.isBackedBySimpleArray(value2.bitmap)) {
      long[] b = this.bitmap.array();
      long[] v2 = value2.bitmap.array();
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        newCardinality += Long.bitCount(b[k] & (~v2[k]));
      }

    } else {
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        newCardinality += Long.bitCount(this.bitmap.get(k) & (~value2.bitmap.get(k)));
      }
    }
    if (newCardinality > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      final MappeableBitmapContainer answer = new MappeableBitmapContainer();
      if (!BufferUtil.isBackedBySimpleArray(answer.bitmap)) {
        throw new RuntimeException("Should not happen. Internal bug.");
      }
      long[] bitArray = answer.bitmap.array();
      if (BufferUtil.isBackedBySimpleArray(this.bitmap)
          && BufferUtil.isBackedBySimpleArray(value2.bitmap)) {
        long[] b = this.bitmap.array();
        long[] v2 = value2.bitmap.array();
        int len = answer.bitmap.limit();
        for (int k = 0; k < len; ++k) {
          bitArray[k] = b[k] & (~v2[k]);
        }
      } else {
        int len = answer.bitmap.limit();
        for (int k = 0; k < len; ++k) {
          bitArray[k] = this.bitmap.get(k) & (~value2.bitmap.get(k));
        }
      }
      answer.cardinality = newCardinality;
      return answer;
    }
    final MappeableArrayContainer ac = new MappeableArrayContainer(newCardinality);
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)
        && BufferUtil.isBackedBySimpleArray(value2.bitmap)) {
      org.roaringbitmap.Util.fillArrayANDNOT(ac.content.array(), this.bitmap.array(),
          value2.bitmap.array());
    } else {
      BufferUtil.fillArrayANDNOT(ac.content.array(), this.bitmap, value2.bitmap);
    }
    ac.cardinality = newCardinality;
    return ac;
  }

  @Override
  public MappeableContainer andNot(final MappeableRunContainer value2) {
    MappeableBitmapContainer answer = this.clone();
    long[] b = answer.bitmap.array();
    for (int rlepos = 0; rlepos < value2.nbrruns; ++rlepos) {
      int start = (value2.getValue(rlepos));
      int end = (value2.getValue(rlepos))
          + (value2.getLength(rlepos)) + 1;
      int prevOnesInRange = Util.cardinalityInBitmapRange(b, start, end);
      Util.resetBitmapRange(b, start, end);
      answer.updateCardinality(prevOnesInRange, 0);
    }
    if (answer.getCardinality() > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      return answer;
    } else {
      return answer.toArrayContainer();
    }
  }

  @Override
  public void clear() {
    if (cardinality != 0) {
      cardinality = 0;
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        bitmap.put(k, 0);
      }
    }
  }



  @Override
  public MappeableBitmapContainer clone() {
    return new MappeableBitmapContainer(this.cardinality, this.bitmap);
  }

  /**
   * Recomputes the cardinality of the bitmap.
   */
  private void computeCardinality() {
    this.cardinality = 0;
    if (BufferUtil.isBackedBySimpleArray(bitmap)) {
      long[] b = this.bitmap.array();
      for (int k = 0; k < b.length; k++) {
        this.cardinality += Long.bitCount(b[k]);
      }
    } else {
      int m = this.bitmap.limit();
      for (int k = 0; k < m; k++) {
        this.cardinality += Long.bitCount(this.bitmap.get(k));
      }
    }
  }

  int cardinalityInRange(int start, int end) {
    assert (cardinality != -1);
    if (end - start > MAX_CAPACITY / 2) {
      int before = BufferUtil.cardinalityInBitmapRange(bitmap, 0, start);
      int after = BufferUtil.cardinalityInBitmapRange(bitmap, end, MAX_CAPACITY);
      return cardinality - before - after;
    }
    return BufferUtil.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.get(i >>> 6) & (1L << i)) != 0;
  }

  long bitValue(final char i) {
    return (bitmap.get(i >>> 6) >>> i) & 1;
  }
  
  
  /**
   * Checks whether the container contains the value i.
   * 
   * @param buf underlying buffer
   * @param position position of the container in the buffer
   * @param i index
   * @return whether the container contains the value i
   */
  public static boolean contains(ByteBuffer buf, int position, final char i) {
    return (buf.getLong(((i >>> 6) << 3) + position) & (1L << i)) != 0;
  }

  @Override
  public boolean equals(Object o) {
    if (o instanceof MappeableBitmapContainer) {
      final MappeableBitmapContainer srb = (MappeableBitmapContainer) o;
      if (srb.cardinality != this.cardinality) {
        return false;
      }
      if (BufferUtil.isBackedBySimpleArray(this.bitmap)
          && BufferUtil.isBackedBySimpleArray(srb.bitmap)) {
        long[] b = this.bitmap.array();
        long[] s = srb.bitmap.array();
        int len = this.bitmap.limit();
        for (int k = 0; k < len; ++k) {
          if (b[k] != s[k]) {
            return false;
          }
        }
      } else {
        int len = this.bitmap.limit();
        for (int k = 0; k < len; ++k) {
          if (this.bitmap.get(k) != srb.bitmap.get(k)) {
            return false;
          }
        }
      }
      return true;

    } else if (o instanceof MappeableRunContainer) {
      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;
    if (BufferUtil.isBackedBySimpleArray(bitmap)) {
      long[] b = bitmap.array();
      int base = 0;
      for (int k = 0; k < b.length; ++k) {
        long bitset = b[k];
        while (bitset != 0) {
          array[pos++] = (char) (base + numberOfTrailingZeros(bitset));
          bitset &= (bitset - 1);
        }
        base += 64;
      }

    } else {
      int len = this.bitmap.limit();
      int base = 0;
      for (int k = 0; k < len; ++k) {
        long bitset = bitmap.get(k);
        while (bitset != 0) {
          array[pos++] = (char) (base + numberOfLeadingZeros(bitset));
          bitset &= (bitset - 1);
        }
        base += 64;
      }
    }
  }

  @Override
  public void fillLeastSignificant16bits(int[] x, int i, int mask) {
    int pos = i;
    int base = mask;
    if (BufferUtil.isBackedBySimpleArray(bitmap)) {
      long[] b = bitmap.array();
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        long bitset = b[k];
        while (bitset != 0) {
          x[pos++] = base + numberOfTrailingZeros(bitset);
          bitset &= (bitset - 1);
        }
        base += 64;
      }

    } else {
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        long bitset = bitmap.get(k);
        while (bitset != 0) {
          x[pos++] = base + numberOfTrailingZeros(bitset);
          bitset &= (bitset - 1);
        }
        base += 64;
      }
    }
  }

  @Override
  public MappeableContainer flip(char i) {
    final long bef = bitmap.get(i >>> 6);
    final long mask = 1L << i;
    if (cardinality == MappeableArrayContainer.DEFAULT_MAX_SIZE + 1) {// this
                                                                      // is
      // the
      // uncommon
      // path
      if ((bef & mask) != 0) {
        --cardinality;
        bitmap.put(i >>> 6, bef & ~mask);
        return this.toArrayContainer();
      }
    }
    long aft = bef ^ mask;
    // TODO: check whether a branchy version could be faster
    cardinality += 1 - 2 * (int)((bef & mask) >>> i);
    bitmap.put(i >>> 6, aft);
    return this;
  }


  @Override
  protected int getArraySizeInBytes() {
    return MAX_CAPACITY / 8;
  }



  @Override
  public int getCardinality() {
    return cardinality;
  }

  @Override
  public CharIterator getReverseCharIterator() {
    if (this.isArrayBacked()) {
      return BitmapContainer.getReverseShortIterator(bitmap.array());
    }
    return new ReverseMappeableBitmapContainerCharIterator(this);
  }

  @Override
  public PeekableCharIterator getCharIterator() {
    if (this.isArrayBacked()) {
      return BitmapContainer.getShortIterator(bitmap.array());
    }
    return new MappeableBitmapContainerCharIterator(this);
  }

  @Override
  public ContainerBatchIterator getBatchIterator() {
    return new BitmapBatchIterator(this);
  }

  @Override
  public int getSizeInBytes() {
    return this.bitmap.limit() * 8;
  }

  @Override
  public int hashCode() {
    long hash = 0;
    int len = this.bitmap.limit();
    for (int k = 0; k < len; ++k) {
      hash += 31 * hash + this.bitmap.get(k);
    }
    return (int) hash;
  }

  @Override
  public MappeableContainer 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);
    BufferUtil.setBitmapRange(bitmap, begin, end);
    updateCardinality(prevOnesInRange, end - begin);
    return this;
  }

  @Override
  public MappeableContainer iand(final MappeableArrayContainer b2) {
    if (-1 == cardinality) {
      BufferUtil.intersectArrayIntoBitmap(bitmap, b2.content, b2.cardinality);
      return this;
    } else {
      return b2.and(this);// no inplace possible
    }
  }


  @Override
  public MappeableContainer iand(final MappeableBitmapContainer b2) {
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)
        && BufferUtil.isBackedBySimpleArray(b2.bitmap)) {
      int newCardinality = 0;
      long[] tb = this.bitmap.array();
      long[] tb2 = b2.bitmap.array();
      int len = this.bitmap.limit();
      if (-1 == cardinality) {
        for (int k = 0; k < len; ++k) {
          tb[k] &= tb2[k];
        }
        return this;
      } else {
        for (int k = 0; k < len; ++k) {
          newCardinality += Long.bitCount(tb[k] & tb2[k]);
        }
        if (newCardinality > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
          for (int k = 0; k < len; ++k) {
            tb[k] &= tb2[k];
          }
          this.cardinality = newCardinality;
          return this;
        }
        final MappeableArrayContainer ac = new MappeableArrayContainer(newCardinality);
        BufferUtil.fillArrayAND(ac.content.array(), this.bitmap, b2.bitmap);
        ac.cardinality = newCardinality;
        return ac;
      }
    } else if (-1 != cardinality) {
      int newCardinality = 0;
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        newCardinality += Long.bitCount(this.bitmap.get(k) & b2.bitmap.get(k));
      }
      if (newCardinality > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
        for (int k = 0; k < len; ++k) {
          this.bitmap.put(k, this.bitmap.get(k) & b2.bitmap.get(k));
        }
        this.cardinality = newCardinality;
        return this;
      }
      final MappeableArrayContainer ac = new MappeableArrayContainer(newCardinality);
      BufferUtil.fillArrayAND(ac.content.array(), this.bitmap, b2.bitmap);
      ac.cardinality = newCardinality;
      return ac;
    } else { // lazy operation, direct buffer
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        this.bitmap.put(k, this.bitmap.get(k) & b2.bitmap.get(k));
      }
      return this;
    }
  }

  @Override
  public MappeableContainer iand(final MappeableRunContainer x) {
    final int card = x.getCardinality();
    if (-1 != cardinality && card <= MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      // no point in doing it in-place, unless it's a lazy operation
      MappeableArrayContainer answer = new MappeableArrayContainer(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.put(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) {
        BufferUtil.resetBitmapRange(this.bitmap, start, end);
      } else {
        int prevOnes = cardinalityInRange(start, end);
        BufferUtil.resetBitmapRange(this.bitmap, start, end);
        updateCardinality(prevOnes, 0);
      }
      start = end + (x.getLength(rlepos)) + 1;
    }
    if (-1 == cardinality) {
      BufferUtil.resetBitmapRange(this.bitmap, start, MAX_CAPACITY);
    } else {
      int ones = cardinalityInRange(start, MAX_CAPACITY);
      BufferUtil.resetBitmapRange(this.bitmap, start, MAX_CAPACITY);
      updateCardinality(ones, 0);
      if (getCardinality() <= MappeableArrayContainer.DEFAULT_MAX_SIZE) {
        return toArrayContainer();
      }
    }
    return this;
  }

  @Override
  public MappeableContainer iandNot(final MappeableArrayContainer b2) {
    for (int k = 0; k < b2.cardinality; ++k) {
      this.remove(b2.content.get(k));
    }
    if (cardinality <= MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      return this.toArrayContainer();
    }
    return this;
  }



  @Override
  public MappeableContainer iandNot(final MappeableBitmapContainer b2) {
    int newCardinality = 0;
    if (!BufferUtil.isBackedBySimpleArray(bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] b = this.bitmap.array();
    if (BufferUtil.isBackedBySimpleArray(b2.bitmap)) {
      long[] b2Arr = b2.bitmap.array();
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        newCardinality += Long.bitCount(b[k] & (~b2Arr[k]));
      }
      if (newCardinality > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
        for (int k = 0; k < len; ++k) {
          this.bitmap.put(k, b[k] & (~b2Arr[k]));
        }
        this.cardinality = newCardinality;
        return this;
      }
      final MappeableArrayContainer ac = new MappeableArrayContainer(newCardinality);
      org.roaringbitmap.Util.fillArrayANDNOT(ac.content.array(), b, b2Arr);
      ac.cardinality = newCardinality;
      return ac;

    }
    int len = this.bitmap.limit();
    for (int k = 0; k < len; ++k) {
      newCardinality += Long.bitCount(b[k] & (~b2.bitmap.get(k)));
    }
    if (newCardinality > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      for (int k = 0; k < len; ++k) {
        b[k] &= (~b2.bitmap.get(k));
      }
      this.cardinality = newCardinality;
      return this;
    }
    final MappeableArrayContainer ac = new MappeableArrayContainer(newCardinality);

    BufferUtil.fillArrayANDNOT(ac.content.array(), this.bitmap, b2.bitmap);
    ac.cardinality = newCardinality;
    return ac;
  }

  @Override
  public MappeableContainer iandNot(final MappeableRunContainer x) {
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)) {
      long[] b = this.bitmap.array();
      for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
        int start = (x.getValue(rlepos));
        int end = start + (x.getLength(rlepos)) + 1;
        int prevOnesInRange = Util.cardinalityInBitmapRange(b, start, end);
        Util.resetBitmapRange(b, start, end);
        updateCardinality(prevOnesInRange, 0);
      }
      if (getCardinality() > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
        return this;
      } else {
        return toArrayContainer();
      }
    }
    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);
      BufferUtil.resetBitmapRange(this.bitmap, start, end);
      updateCardinality(prevOnesInRange, 0);
    }
    if (getCardinality() > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      return this;
    } else {
      return toArrayContainer();
    }

  }

  MappeableContainer ilazyor(MappeableArrayContainer value2) {
    this.cardinality = -1;// invalid
    if (!BufferUtil.isBackedBySimpleArray(bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] b = this.bitmap.array();
    int c = value2.cardinality;
    for (int k = 0; k < c; ++k) {
      char v2 = value2.content.get(k);
      final int i = (v2) >>> 6;
      b[i] |= (1L << v2);
    }
    return this;
  }

  MappeableContainer ilazyor(MappeableBitmapContainer x) {
    if (BufferUtil.isBackedBySimpleArray(x.bitmap)) {
      long[] b = this.bitmap.array();
      long[] b2 = x.bitmap.array();
      for (int k = 0; k < b.length; k++) {
        b[k] |= b2[k];
      }
    } else {
      final int m = this.bitmap.limit();
      for (int k = 0; k < m; k++) {
        this.bitmap.put(k, this.bitmap.get(k) | x.bitmap.get(k));
      }
    }
    this.cardinality = -1;// invalid
    return this;
  }


  MappeableContainer ilazyor(MappeableRunContainer x) {
    for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
      int start = (x.getValue(rlepos));
      int end = start + (x.getLength(rlepos)) + 1;
      BufferUtil.setBitmapRange(this.bitmap, start, end);
    }
    this.cardinality = -1;
    return this;
  }

  @Override
  public MappeableContainer inot(final int firstOfRange, final int lastOfRange) {
    int prevOnes = cardinalityInRange(firstOfRange, lastOfRange);
    BufferUtil.flipBitmapRange(bitmap, firstOfRange, lastOfRange);
    updateCardinality(prevOnes, lastOfRange - firstOfRange - prevOnes);
    if (cardinality <= MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      return toArrayContainer();
    }
    return this;
  }


  @Override
  public boolean intersects(MappeableArrayContainer value2) {
    if (BufferUtil.isBackedBySimpleArray(value2.content)) {
      char[] c = value2.content.array();
      int ca = value2.cardinality;
      for (int k = 0; k < ca; ++k) {
        if (this.contains(c[k])) {
          return true;
        }
      }

    } else {
      int ca = value2.cardinality;
      for (int k = 0; k < ca; ++k) {
        char v = value2.content.get(k);
        if (this.contains(v)) {
          return true;
        }
      }
    }
    return false;
  }

  @Override
  public boolean intersects(MappeableBitmapContainer value2) {
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)
        && BufferUtil.isBackedBySimpleArray(value2.bitmap)) {
      long[] tb = this.bitmap.array();
      long[] v2b = value2.bitmap.array();
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        if ((tb[k] & v2b[k]) != 0) {
          return true;
        }
      }
    } else {
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        if ((this.bitmap.get(k) & value2.bitmap.get(k)) != 0) {
          return true;
        }
      }
    }
    return false;
  }

  @Override
  public boolean intersects(MappeableRunContainer x) {
    return x.intersects(this);
  }



  @Override
  public MappeableBitmapContainer ior(final MappeableArrayContainer value2) {
    if (!BufferUtil.isBackedBySimpleArray(this.bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] b = this.bitmap.array();
    if (BufferUtil.isBackedBySimpleArray(value2.content)) {
      char[] v2 = value2.content.array();
      int c = value2.cardinality;
      for (int k = 0; k < c; ++k) {
        final int i = (v2[k]) >>> 6;
        long bef = b[i];
        long aft = bef | (1L << v2[k]);
        b[i] = aft;
        if (USE_BRANCHLESS) {
          cardinality += (int)((bef - aft) >>> 63);
        } else {
          if (aft != bef) {
            cardinality++;
          }
        }
      }
      return this;
    }
    int c = value2.cardinality;
    for (int k = 0; k < c; ++k) {
      char v2 = value2.content.get(k);
      final int i = (v2) >>> 6;
      long bef = b[i];
      long aft = bef | (1L << v2);
      b[i] = aft;
      if (USE_BRANCHLESS) {
        cardinality += (int)((bef - aft) >>> 63);
      } else {
        if (aft != bef) {
          cardinality++;
        }
      }
    }
    return this;
  }

  @Override
  public MappeableContainer ior(final MappeableBitmapContainer b2) {
    if (!BufferUtil.isBackedBySimpleArray(bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] b = this.bitmap.array();
    this.cardinality = 0;
    if (BufferUtil.isBackedBySimpleArray(b2.bitmap)) {
      long[] b2Arr = b2.bitmap.array();
      int len = this.bitmap.limit();
      for (int k = 0; k < len; k++) {
        long w = b[k] | b2Arr[k];
        b[k] = w;
        this.cardinality += Long.bitCount(w);
      }
      if (isFull()) {
        return MappeableRunContainer.full();
      }
      return this;
    }
    int len = this.bitmap.limit();
    for (int k = 0; k < len; k++) {
      long w = b[k] | b2.bitmap.get(k);
      b[k] = w;
      this.cardinality += Long.bitCount(w);
    }
    if (isFull()) {
      return MappeableRunContainer.full();
    }
    return this;
  }

  @Override
  public boolean isFull() {
    return this.cardinality == MAX_CAPACITY;
  }

  @Override
  public void orInto(long[] bits) {
    for (int i = 0; i < bits.length; ++i) {
      bits[i] |= bitmap.get(i);
    }
  }

  @Override
  public void andInto(long[] bits) {
    for (int i = 0; i < bits.length; ++i) {
      bits[i] &= bitmap.get(i);
    }
  }

  @Override
  public void removeFrom(long[] bits) {
    for (int i = 0; i < bits.length; ++i) {
      bits[i] &= ~bitmap.get(i);
    }
  }

  @Override
  public MappeableContainer ior(final MappeableRunContainer x) {
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)) {
      long[] b = this.bitmap.array();
      for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
        int start = (x.getValue(rlepos));
        int end = start + (x.getLength(rlepos)) + 1;
        int prevOnesInRange = Util.cardinalityInBitmapRange(b, start, end);
        Util.setBitmapRange(b, start, end);
        updateCardinality(prevOnesInRange, end - start);
      }
    } else {
      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);
        BufferUtil.setBitmapRange(this.bitmap, start, end);
        updateCardinality(prevOnesInRange, end - start);
      }
    }
    if (isFull()) {
      return MappeableRunContainer.full();
    }
    return this;
  }

  @Override
  public MappeableContainer 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);
    BufferUtil.resetBitmapRange(bitmap, begin, end);
    updateCardinality(prevOnesInRange, 0);
    if (getCardinality() < MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      return toArrayContainer();
    }
    return this;
  }

  @Override
  protected boolean isArrayBacked() {
    return BufferUtil.isBackedBySimpleArray(this.bitmap);
  }

  @Override
  public Iterator iterator() {
    return new Iterator() {
      final CharIterator si = MappeableBitmapContainer.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 MappeableContainer ixor(final MappeableArrayContainer value2) {
    if (!BufferUtil.isBackedBySimpleArray(bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] b = bitmap.array();
    if (BufferUtil.isBackedBySimpleArray(value2.content)) {
      char[] v2 = value2.content.array();
      int c = value2.cardinality;
      for (int k = 0; k < c; ++k) {
        char vc = v2[k];
        long mask = 1L << v2[k];
        final int index = (vc) >>> 6;
        long ba = b[index];
        // TODO: check whether a branchy version could be faster
        this.cardinality += 1 - 2 * (int)((ba & mask) >>> vc);
        b[index] = ba ^ mask;
      }

    } else {
      int c = value2.cardinality;
      for (int k = 0; k < c; ++k) {
        char v2 = value2.content.get(k);
        long mask = 1L << v2;
        final int index = (v2) >>> 6;
        long ba = b[index];
        // TODO: check whether a branchy version could be faster
        this.cardinality += 1 - 2 * (int)((ba & mask) >>> v2);
        b[index] = ba ^ mask;
      }
    }
    if (this.cardinality <= MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      return this.toArrayContainer();
    }
    return this;
  }

  @Override
  public MappeableContainer ixor(MappeableBitmapContainer b2) {
    if (!BufferUtil.isBackedBySimpleArray(bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] b = bitmap.array();
    if (BufferUtil.isBackedBySimpleArray(b2.bitmap)) {
      long[] b2Arr = b2.bitmap.array();

      int newCardinality = 0;
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        newCardinality += Long.bitCount(b[k] ^ b2Arr[k]);
      }
      if (newCardinality > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
        for (int k = 0; k < len; ++k) {
          b[k] ^= b2Arr[k];
        }
        this.cardinality = newCardinality;
        return this;
      }
      final MappeableArrayContainer ac = new MappeableArrayContainer(newCardinality);

      org.roaringbitmap.Util.fillArrayXOR(ac.content.array(), b, b2Arr);
      ac.cardinality = newCardinality;
      return ac;

    }
    int newCardinality = 0;
    int len = this.bitmap.limit();
    for (int k = 0; k < len; ++k) {
      newCardinality += Long.bitCount(b[k] ^ b2.bitmap.get(k));
    }
    if (newCardinality > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      for (int k = 0; k < len; ++k) {
        b[k] ^= b2.bitmap.get(k);
      }
      this.cardinality = newCardinality;
      return this;
    }
    final MappeableArrayContainer ac = new MappeableArrayContainer(newCardinality);

    BufferUtil.fillArrayXOR(ac.content.array(), this.bitmap, b2.bitmap);
    ac.cardinality = newCardinality;
    return ac;
  }

  @Override
  public MappeableContainer ixor(final MappeableRunContainer x) {
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)) {
      long[] b = this.bitmap.array();
      for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
        int start = (x.getValue(rlepos));
        int end = start + (x.getLength(rlepos)) + 1;
        int prevOnes = Util.cardinalityInBitmapRange(b, start, end);
        Util.flipBitmapRange(b, start, end);
        updateCardinality(prevOnes, end - start - prevOnes);
      }
    } else {
      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);
        BufferUtil.flipBitmapRange(this.bitmap, start, end);
        updateCardinality(prevOnes, end - start - prevOnes);
      }
    }
    if (this.getCardinality() > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      return this;
    } else {
      return toArrayContainer();
    }
  }

  protected MappeableContainer lazyor(MappeableArrayContainer value2) {
    MappeableBitmapContainer answer = clone();
    answer.cardinality = -1;// invalid
    if (!BufferUtil.isBackedBySimpleArray(answer.bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] b = answer.bitmap.array();
    int c = value2.cardinality;
    for (int k = 0; k < c; ++k) {
      char v2 = value2.content.get(k);
      final int i = (v2) >>> 6;
      b[i] |= 1L << v2;
    }
    return answer;
  }

  protected MappeableContainer lazyor(MappeableBitmapContainer x) {
    MappeableBitmapContainer answer = new MappeableBitmapContainer();
    answer.cardinality = -1;// invalid
    if (!BufferUtil.isBackedBySimpleArray(answer.bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] b = answer.bitmap.array();
    for (int k = 0; k < b.length; k++) {
      b[k] = this.bitmap.get(k) | x.bitmap.get(k);
    }
    return answer;
  }

  protected MappeableContainer lazyor(MappeableRunContainer x) {
    MappeableBitmapContainer bc = clone();
    bc.cardinality = -1;
    long[] b = bc.bitmap.array();
    for (int rlepos = 0; rlepos < x.nbrruns; ++rlepos) {
      int start = (x.getValue(rlepos));
      int end = start + (x.getLength(rlepos)) + 1;
      Util.setBitmapRange(b, start, end);
    }
    return bc;
  }

  @Override
  public MappeableContainer limit(int maxcardinality) {
    if (maxcardinality >= this.cardinality) {
      return clone();
    }
    if (maxcardinality <= MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      MappeableArrayContainer ac = new MappeableArrayContainer(maxcardinality);
      int pos = 0;
      if (!BufferUtil.isBackedBySimpleArray(ac.content)) {
        throw new RuntimeException("Should not happen. Internal bug.");
      }
      char[] cont = ac.content.array();
      int len = this.bitmap.limit();
      for (int k = 0; (ac.cardinality < maxcardinality) && (k < len); ++k) {
        long bitset = bitmap.get(k);
        while ((ac.cardinality < maxcardinality) && (bitset != 0)) {
          cont[pos++] = (char) (k * 64 + numberOfTrailingZeros(bitset));
          ac.cardinality++;
          bitset &= (bitset - 1);
        }
      }
      return ac;
    }
    MappeableBitmapContainer bc = new MappeableBitmapContainer(maxcardinality, this.bitmap);
    int s = (select(maxcardinality));
    int usedwords = (s + 63) >>> 6;
    int len = this.bitmap.limit();
    int todelete = len - usedwords;
    for (int k = 0; k < todelete; ++k) {
      bc.bitmap.put(len - 1 - k, 0);
    }
    int lastword = s % 64;
    if (lastword != 0) {
      bc.bitmap.put(s >>> 6, (bc.bitmap.get(s >>> 6) & (0xFFFFFFFFFFFFFFFFL >>> (64 - lastword))));
    }
    return bc;
  }

  void loadData(final MappeableArrayContainer arrayContainer) {
    this.cardinality = arrayContainer.cardinality;
    if (!BufferUtil.isBackedBySimpleArray(bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] bitArray = bitmap.array();
    if (BufferUtil.isBackedBySimpleArray(bitmap)
        && BufferUtil.isBackedBySimpleArray(arrayContainer.content)) {
      long[] b = bitmap.array();
      char[] ac = arrayContainer.content.array();
      for (int k = 0; k < arrayContainer.cardinality; ++k) {
        final char x = ac[k];
        bitArray[(x) >>> 6] =
            b[(x) >>> 6] | (1L << x);
      }

    } else {
      for (int k = 0; k < arrayContainer.cardinality; ++k) {
        final char x = arrayContainer.content.get(k);
        bitArray[(x) >>> 6] =
            bitmap.get((x) >>> 6) | (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; // signed i >>> 6
    long w = bitmap.get(x);
    w >>>= i;
    if (w != 0) {
      return i + numberOfTrailingZeros(w);
    }
    // for speed, replaced bitmap.limit() with hardcoded MAX_CAPACITY >>> 6
    for (++x; x < MAX_CAPACITY >>> 6; ++x) {
      long X = bitmap.get(x);
      if (X != 0) {
        return x * 64 + numberOfTrailingZeros(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; // i >>> 6 with sign extension
    long w = ~bitmap.get(x);
    w >>>= i;
    if (w != 0) {
      return i + numberOfTrailingZeros(w);
    }
    int length = bitmap.limit();
    for (++x; x < length; ++x) {
      long map = ~bitmap.get(x);
      if (map != 0L) {
        return x * 64 + numberOfTrailingZeros(map);
      }
    }
    return MAX_CAPACITY;
  }

  @Override
  public MappeableContainer not(final int firstOfRange, final int lastOfRange) {
    MappeableBitmapContainer answer = clone();
    return answer.inot(firstOfRange, lastOfRange);
  }


  @Override
  int numberOfRuns() {
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)) {
      long[] src = this.bitmap.array();
      int numRuns = 0;
      long nextWord = src[0];

      for (int i = 0; i < src.length - 1; i++) {
        long word = nextWord;
        nextWord = src[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;
    } else {
      int numRuns = 0;
      long nextWord = bitmap.get(0);
      int len = bitmap.limit();

      for (int i = 0; i < len - 1; i++) {
        long word = nextWord;
        nextWord = bitmap.get(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
   */
  private int numberOfRunsAdjustment() {
    int ans = 0;
    if (BufferUtil.isBackedBySimpleArray(bitmap)) {
      long[] b = bitmap.array();
      long nextWord = b[0];
      for (int i = 0; i < b.length - 1; i++) {
        final long word = nextWord;

        nextWord = b[i + 1];
        ans += (int)((word >>> 63) & ~nextWord);
      }

      final long word = nextWord;

      if ((word & 0x8000000000000000L) != 0) {
        ans++;
      }

    } else {
      long nextWord = bitmap.get(0);
      int len = bitmap.limit();
      for (int i = 0; i < len - 1; i++) {
        final long word = nextWord;

        nextWord = bitmap.get(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
   */
  private int numberOfRunsLowerBound(int mustNotExceed) {
    int numRuns = 0;
    if (BufferUtil.isBackedBySimpleArray(bitmap)) {
      long[] b = bitmap.array();

      for (int blockOffset = 0; blockOffset + BLOCKSIZE <= b.length; blockOffset += BLOCKSIZE) {

        for (int i = blockOffset; i < blockOffset + BLOCKSIZE; i++) {
          long word = b[i];
          numRuns += Long.bitCount((~word) & (word << 1));
        }
        if (numRuns > mustNotExceed) {
          return numRuns;
        }
      }
    } else {
      int len = bitmap.limit();
      for (int blockOffset = 0; blockOffset < len; blockOffset += BLOCKSIZE) {

        for (int i = blockOffset; i < blockOffset + BLOCKSIZE; i++) {
          long word = bitmap.get(i);
          numRuns += Long.bitCount((~word) & (word << 1));
        }
        if (numRuns > mustNotExceed) {
          return numRuns;
        }
      }
    }
    return numRuns;
  }



  @Override
  public MappeableContainer or(final MappeableArrayContainer value2) {
    final MappeableBitmapContainer answer = clone();
    if (!BufferUtil.isBackedBySimpleArray(answer.bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] bitArray = answer.bitmap.array();
    if (BufferUtil.isBackedBySimpleArray(answer.bitmap)
        && BufferUtil.isBackedBySimpleArray(value2.content)) {
      long[] ab = answer.bitmap.array();
      char[] v2 = value2.content.array();
      int c = value2.cardinality;
      for (int k = 0; k < c; ++k) {
        char v = v2[k];
        final int i = v >>> 6;
        long w = ab[i];
        long aft = w | (1L << v);
        bitArray[i] = aft;
        if (USE_BRANCHLESS) {
          answer.cardinality += (int)((w - aft) >>> 63);
        } else {
          if (w != aft) {
            answer.cardinality++;
          }
        }
      }
    } else {
      int c = value2.cardinality;
      for (int k = 0; k < c; ++k) {
        char v2 = value2.content.get(k);
        final int i = (v2) >>> 6;
        long w = answer.bitmap.get(i);
        long aft = w | (1L << v2);
        bitArray[i] = aft;
        if (USE_BRANCHLESS) {
          answer.cardinality += (int)((w - aft) >>> 63);
        } else {
          if (w != aft) {
            answer.cardinality++;
          }
        }
      }
    }
    if (answer.isFull()) {
      return MappeableRunContainer.full();
    }
    return answer;
  }



  @Override
  public MappeableContainer or(final MappeableBitmapContainer value2) {
    final MappeableBitmapContainer value1 = this.clone();
    return value1.ior(value2);
  }

  @Override
  public MappeableContainer or(final MappeableRunContainer value2) {
    return value2.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; // signed i >>> 6
    long w = bitmap.get(x);
    w <<= 64 - i - 1;
    if (w != 0) {
      return i - Long.numberOfLeadingZeros(w);
    }
    for (--x; x >= 0; --x) {
      long X = bitmap.get(x);
      if (X != 0) {
        return x * 64 + 63 - Long.numberOfLeadingZeros(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 >>> 6 with sign extension
    long w = ~bitmap.get(x);
    w <<= 64 - i - 1;
    if (w != 0L) {
      return i - Long.numberOfLeadingZeros(w);
    }
    for (--x; x >= 0; --x) {
      long map = ~bitmap.get(x);
      if (map != 0L) {
        return x * 64 + 63 - Long.numberOfLeadingZeros(map);
      }
    }
    return -1;
  }

  @Override
  public int rank(char lowbits) {
    int leftover = (lowbits + 1) & 63;
    int answer = 0;
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)) {
      long[] b = this.bitmap.array();
      for (int k = 0; k < (lowbits + 1) >>> 6; ++k) {
        answer += Long.bitCount(b[k]);
      }
      if (leftover != 0) {
        answer += Long.bitCount(b[(lowbits + 1) >>> 6] << (64 - leftover));
      }
    } else {
      for (int k = 0; k < (lowbits + 1) >>> 6; ++k) {
        answer += Long.bitCount(bitmap.get(k));
      }
      if (leftover != 0) {
        answer += Long.bitCount(bitmap.get((lowbits + 1) >>> 6) << (64 - leftover));
      }
    }
    return answer;
  }


  @Override
  public void readExternal(ObjectInput in) throws IOException {
    // little endian
    this.cardinality = 0;
    int len = this.bitmap.limit();
    if (BufferUtil.isBackedBySimpleArray(bitmap)) {
      long[] b = this.bitmap.array();
      for (int k = 0; k < len; ++k) {
        long w = Long.reverseBytes(in.readLong());
        b[k] = w;
        this.cardinality += Long.bitCount(w);
      }
    } else {
      for (int k = 0; k < len; ++k) {
        long w = Long.reverseBytes(in.readLong());
        bitmap.put(k, w);
        this.cardinality += Long.bitCount(w);
      }
    }
  }

  @Override
  public MappeableContainer remove(int begin, int end) {
    if (end == begin) {
      return clone();
    }
    if ((begin > end) || (end > (1 << 16))) {
      throw new IllegalArgumentException("Invalid range [" + begin + "," + end + ")");
    }
    MappeableBitmapContainer answer = clone();
    int prevOnesInRange = answer.cardinalityInRange(begin, end);
    BufferUtil.resetBitmapRange(answer.bitmap, begin, end);
    answer.updateCardinality(prevOnesInRange, 0);
    if (answer.getCardinality() < MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      return answer.toArrayContainer();
    }
    return answer;
  }



  @Override
  public MappeableContainer remove(final char i) {
    long X = bitmap.get(i >>> 6);
    long mask = 1L << i;

    if (cardinality == MappeableArrayContainer.DEFAULT_MAX_SIZE + 1) {// this is
      // the
      // uncommon
      // path
      if ((X & mask) != 0) {
        --cardinality;
        bitmap.put(i >>> 6, X & (~mask));
        return this.toArrayContainer();
      }
    }
    long aft = X & ~(mask);
    cardinality -= (aft - X) >>> 63;
    bitmap.put(i >>> 6, aft);
    return this;
  }


  @Override
  public MappeableContainer repairAfterLazy() {
    if (getCardinality() < 0) {
      computeCardinality();
      if(getCardinality() <= MappeableArrayContainer.DEFAULT_MAX_SIZE) {
        return this.toArrayContainer();
      } else if (isFull()) {
        return MappeableRunContainer.full();
      }
    }
    return this;
  }

  @Override
  public MappeableContainer runOptimize() {
    int numRuns = numberOfRunsLowerBound(MAXRUNS); // decent choice

    int sizeAsRunContainerLowerBound = MappeableRunContainer.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 = MappeableRunContainer.serializedSizeInBytes(numRuns);

    if (getArraySizeInBytes() > sizeAsRunContainer) {
      return new MappeableRunContainer(this, numRuns);
    } else {
      return this;
    }
  }

  @Override
  public char select(int j) {
    int leftover = j;
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)) {
      long[] b = this.bitmap.array();

      for (int k = 0; k < b.length; ++k) {
        int w = Long.bitCount(b[k]);
        if (w > leftover) {
          return (char) (k * 64 + Util.select(b[k], leftover));
        }
        leftover -= w;
      }
    } else {
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        long X = bitmap.get(k);
        int w = Long.bitCount(X);
        if (w > leftover) {
          return (char) (k * 64 + Util.select(X, leftover));
        }
        leftover -= w;
      }
    }
    throw new IllegalArgumentException("Insufficient cardinality.");
  }

  @Override
  public int serializedSizeInBytes() {
    return serializedSizeInBytes(0);
  }

  /**
   * Copies the data to an array container
   * 
   * @return the array container
   */
  MappeableArrayContainer toArrayContainer() {
    final MappeableArrayContainer ac = new MappeableArrayContainer(cardinality);
    ac.loadData(this);
    if (ac.getCardinality() != cardinality) {
      throw new RuntimeException("Internal error.");
    }
    return ac;
  }

  @Override
  public Container toContainer() {
    return new BitmapContainer(this);
  }

  /**
   * Create a copy of the content of this container as a long array. This creates a copy.
   *
   * @return copy of the content as a long array
   */
  public long[] toLongArray() {
    long[] answer = new long[bitmap.limit()];
    bitmap.rewind();
    bitmap.get(answer);
    return answer;
  }

  @Override
  public String toString() {
    final 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
  protected void writeArray(DataOutput out) throws IOException {
    // little endian
    int len = this.bitmap.limit();
    if (BufferUtil.isBackedBySimpleArray(bitmap)) {
      long[] b = bitmap.array();
      for (int k = 0; k < len; ++k) {
        out.writeLong(Long.reverseBytes(b[k]));
      }
    } else {
      for (int k = 0; k < len; ++k) {
        final long w = bitmap.get(k);
        out.writeLong(Long.reverseBytes(w));
      }
    }
  }

  @Override
  protected void writeArray(ByteBuffer buffer) {
    assert buffer.order() == LITTLE_ENDIAN;
    LongBuffer buf = bitmap.duplicate();
    buf.position(0);
    buffer.asLongBuffer().put(buf);
    buffer.position(buffer.position() + buf.position() * 8);
  }

  @Override
  public void writeExternal(ObjectOutput out) throws IOException {
    writeArray(out);
  }

  @Override
  public MappeableContainer xor(final MappeableArrayContainer value2) {
    final MappeableBitmapContainer answer = clone();
    if (!BufferUtil.isBackedBySimpleArray(answer.bitmap)) {
      throw new RuntimeException("Should not happen. Internal bug.");
    }
    long[] bitArray = answer.bitmap.array();
    if (BufferUtil.isBackedBySimpleArray(value2.content)) {
      char[] v2 = value2.content.array();
      int c = value2.cardinality;
      for (int k = 0; k < c; ++k) {
        char vc = v2[k];
        long mask = 1L << vc;
        final int index = (vc) >>> 6;
        long ba = bitArray[index];
        // TODO: check whether a branchy version could be faster
        answer.cardinality += 1 - 2 * (int)((ba & mask) >>> vc);
        bitArray[index] = ba ^ mask;
      }
    } else {
      int c = value2.cardinality;
      for (int k = 0; k < c; ++k) {
        char v2 = value2.content.get(k);
        long mask = 1L << v2;
        final int index = (v2) >>> 6;
        long ba = bitArray[index];
        // TODO: check whether a branchy version could be faster
        answer.cardinality += 1 - 2 * (int)((ba & mask) >>> v2);
        bitArray[index] = ba ^ mask;
      }
    }
    if (answer.cardinality <= MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      return answer.toArrayContainer();
    }
    return answer;
  }

  @Override
  public MappeableContainer xor(MappeableBitmapContainer value2) {
    int newCardinality = 0;
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)
        && BufferUtil.isBackedBySimpleArray(value2.bitmap)) {
      long[] b = this.bitmap.array();
      long[] v2 = value2.bitmap.array();
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        newCardinality += Long.bitCount(b[k] ^ v2[k]);
      }
    } else {
      int len = this.bitmap.limit();
      for (int k = 0; k < len; ++k) {
        newCardinality += Long.bitCount(this.bitmap.get(k) ^ value2.bitmap.get(k));
      }
    }
    if (newCardinality > MappeableArrayContainer.DEFAULT_MAX_SIZE) {
      final MappeableBitmapContainer answer = new MappeableBitmapContainer();
      long[] bitArray = answer.bitmap.array();
      if (BufferUtil.isBackedBySimpleArray(this.bitmap)
          && BufferUtil.isBackedBySimpleArray(value2.bitmap)) {
        long[] b = this.bitmap.array();
        long[] v2 = value2.bitmap.array();
        int len = answer.bitmap.limit();
        for (int k = 0; k < len; ++k) {
          bitArray[k] = b[k] ^ v2[k];
        }
      } else {
        int len = answer.bitmap.limit();
        for (int k = 0; k < len; ++k) {
          bitArray[k] = this.bitmap.get(k) ^ value2.bitmap.get(k);
        }
      }
      answer.cardinality = newCardinality;
      return answer;
    }
    final MappeableArrayContainer ac = new MappeableArrayContainer(newCardinality);
    BufferUtil.fillArrayXOR(ac.content.array(), this.bitmap, value2.bitmap);
    ac.cardinality = newCardinality;
    return ac;
  }

  @Override
  public MappeableContainer xor(final MappeableRunContainer value2) {
    return value2.xor(this);
  }

  @Override
  public void forEach(char msb, IntConsumer ic) {
    int high = ((int) msb) << 16;
    if (BufferUtil.isBackedBySimpleArray(bitmap)) {
      long[] b = bitmap.array();
      for (int x = 0; x < b.length; ++x) {
        long w = b[x];
        while (w != 0) {
          ic.accept((x * 64 + numberOfTrailingZeros(w)) | high);
          w &= (w - 1);
        }
      }
    } else {
      int l = bitmap.limit();
      for (int x = 0; x < l; ++x) {
        long w = bitmap.get(x);
        while (w != 0) {
          ic.accept((x * 64 + numberOfTrailingZeros(w)) | high);
          w &= (w - 1);
        }
      }
    }
  }


  @Override
  public int andCardinality(final MappeableArrayContainer value2) {
    int answer = 0;
    int c = value2.cardinality;
    for (int k = 0; k < c; ++k) {
      char v = value2.content.get(k);
      answer += (int)this.bitValue(v);
    }
    return answer;
  }

  @Override
  public int andCardinality(final MappeableBitmapContainer value2) {
    int newCardinality = 0;
    if (BufferUtil.isBackedBySimpleArray(this.bitmap)
        && BufferUtil.isBackedBySimpleArray(value2.bitmap)) {
      long[] b1 = this.bitmap.array();
      long[] b2 = value2.bitmap.array();
      for (int k = 0; k < b1.length; ++k) {
        newCardinality += Long.bitCount(b1[k] & b2[k]);
      }
    } else {
      final int size = this.bitmap.limit();
      for (int k = 0; k < size; ++k) {
        newCardinality += Long.bitCount(this.bitmap.get(k) & value2.bitmap.get(k));
      }
    }
    return newCardinality;
  }

  @Override
  public int andCardinality(MappeableRunContainer x) {
    return x.andCardinality(this);
  }

  @Override
  public MappeableBitmapContainer toBitmapContainer() {
    return this;
  }

  @Override
  public int first() {
    assertNonEmpty(cardinality == 0);
    long firstNonZeroWord;
    int i = 0;
    if(BufferUtil.isBackedBySimpleArray(bitmap)) {
      long[] array = bitmap.array();
      while(array[i] == 0) {
        ++i;
      }
      firstNonZeroWord = array[i];
    } else {
      i = bitmap.position();
      while(bitmap.get(i) == 0) {
        ++i;
      }
      firstNonZeroWord = bitmap.get(i);
    }
    return i * 64 + numberOfTrailingZeros(firstNonZeroWord);
  }

  @Override
  public int last() {
    assertNonEmpty(cardinality == 0);
    long lastNonZeroWord;
    int i = bitmap.limit() - 1;
    if(BufferUtil.isBackedBySimpleArray(bitmap)) {
      long[] array = this.bitmap.array();
      while(i > 0 && array[i] == 0) {
        --i;
      }
      lastNonZeroWord = array[i];
    } else {
      while(i > 0 && bitmap.get(i) == 0) {
        --i;
      }
      lastNonZeroWord = bitmap.get(i);
    }
    return (i + 1) * 64 - Long.numberOfLeadingZeros(lastNonZeroWord) - 1;
  }

  @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
  protected boolean contains(MappeableBitmapContainer bitmapContainer) {
    if((cardinality != -1) && (bitmapContainer.cardinality != -1)) {
      if(cardinality < bitmapContainer.cardinality) {
        return false;
      }
    }
    for(int i = 0; i < MAX_CAPACITY >>> 6; ++i ) {
      if((this.bitmap.get(i) & bitmapContainer.bitmap.get(i)) != bitmapContainer.bitmap.get(i)) {
        return false;
      }
    }
    return true;
  }

  @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.get(start) & (-(1L << minimum) & ((1L << supremum) - 1))) != 0);
    }
    if ((bitmap.get(start) & -(1L << minimum)) != 0) {
      return true;
    }
    if (end < bitmap.limit() && (bitmap.get(end) & ((1L << supremum) - 1)) != 0) {
      return true;
    }
    for (int i = 1 + start; i < end && i < bitmap.limit(); ++i) {
      if (bitmap.get(i) != 0) {
        return true;
      }
    }
    return false;
  }

  @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.get(end) & first & last) == (first & last));
    }
    if ((bitmap.get(start) & first) != first) {
      return false;
    }
    if (end < bitmap.limit() && (bitmap.get(end) & last) != last) {
      return false;
    }
    for (int i = start + 1; i < bitmap.limit() && i < end; ++i) {
      if (bitmap.get(i) != -1L) {
        return false;
      }
    }
    return true;
  }

  @Override
  protected boolean contains(MappeableRunContainer 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(MappeableArrayContainer arrayContainer) {
    if (arrayContainer.cardinality != -1) {
      if (cardinality < arrayContainer.cardinality) {
        return false;
      }
    }
    for (int i = 0; i < arrayContainer.cardinality; ++i) {
      if(!contains(arrayContainer.content.get(i))) {
        return false;
      }
    }
    return true;
  }


}


final class MappeableBitmapContainerCharIterator implements PeekableCharIterator {
  private final static int len = MappeableBitmapContainer.MAX_CAPACITY >>> 6;// hard coded for speed
  private long w;
  int x;


  private MappeableBitmapContainer parent;

  MappeableBitmapContainerCharIterator() {

  }

  MappeableBitmapContainerCharIterator(MappeableBitmapContainer 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 < len;
  }

  @Override
  public char next() {
    char answer = (char) (x * 64 + numberOfTrailingZeros(w));
    w &= (w - 1);
    while (w == 0) {
      ++x;
      if (x == len) {
        break;
      }
      w = parent.bitmap.get(x);
    }
    return answer;
  }

  @Override
  public int nextAsInt() {
    return next();
  }

  @Override
  public void remove() {
    // TODO: implement
    throw new RuntimeException("unsupported operation: remove");
  }

  void wrap(MappeableBitmapContainer p) {
    parent = p;
    for (x = 0; x < len; ++x) {
      if ((w = parent.bitmap.get(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 = parent.bitmap.get(x);
      }
      w &= ~0L << (minval & 63);
      while (w == 0) {
        x++;
        if (x == len) {
          return;
        }
        w = parent.bitmap.get(x);
      }
    }
  }

  @Override
  public char peekNext() {
    return (char) (x * 64 + numberOfTrailingZeros(w));
  }
}


final class ReverseMappeableBitmapContainerCharIterator implements CharIterator {

  private final static int len = MappeableBitmapContainer.MAX_CAPACITY >>> 6;// hard coded for speed
  private long w;
  int x;

  private MappeableBitmapContainer parent;

  ReverseMappeableBitmapContainerCharIterator() {

  }

  ReverseMappeableBitmapContainerCharIterator(MappeableBitmapContainer p) {
    wrap(p);
  }

  @Override
  public CharIterator clone() {
    try {
      return (CharIterator) super.clone();
    } catch (CloneNotSupportedException e) {
      return null;
    }
  }

  @Override
  public boolean hasNext() {
    return x >= 0;
  }

  @Override
  public char next() {
    int shift = Long.numberOfLeadingZeros(w) + 1;
    char answer = (char)((x + 1) * 64 - shift);
    w &= (0xFFFFFFFFFFFFFFFEL >>> shift);
    while (w == 0) {
      --x;
      if (x < 0) {
        break;
      }
      w = parent.bitmap.get(x);
    }
    return answer;
  }

  @Override
  public int nextAsInt() {
    return next();
  }

  @Override
  public void remove() {
    // TODO: implement
    throw new RuntimeException("unsupported operation: remove");
  }

  public void wrap(MappeableBitmapContainer p) {
    parent = p;
    for (x = len - 1; x >= 0; --x) {
      if ((w = parent.bitmap.get(x)) != 0) {
        break;
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy