private WAHBitSet genericOp(OpType op, WAHBitSet other) {
    WAHBitSet ret = new WAHBitSet();

    // ensure that they have the same bit length.
    if (this.numBits() < other.numBits()) {
      this.setBit(other.numBits() - 1, 0);
    } else if (this.numBits() > other.numBits()) {
      other.setBit(numBits() - 1, 0);
    }

    if (vec.size() > 0) {
      run xrun = new run(vec), yrun = new run(other.vec);
      xrun.decode();
      yrun.decode();
      do {
        if (xrun.nWords == 0) {
          xrun.inc();
          xrun.decode();
        }

        if (yrun.nWords == 0) {
          yrun.inc();
          yrun.decode();
        }

        if (xrun.isFill()) {
          if (yrun.isFill()) {
            int nWords = Math.min(xrun.nWords, yrun.nWords);
            ret.appendFill(nWords, getOpResult(op, xrun.fillWord, yrun.fillWord));
            xrun.nWords -= nWords;
            yrun.nWords -= nWords;
          } else {
            ret.active.val = getOpResult(op, xrun.fillWord, yrun.get());
            ret.appendLiteral();
            --xrun.nWords;
            yrun.nWords = 0;
          }
        } else if (yrun.isFill()) {
          ret.active.val = getOpResult(op, yrun.fillWord, xrun.get());
          ret.appendLiteral();
          yrun.nWords--;
          xrun.nWords = 0;
        } else {
          ret.active.val = getOpResult(op, xrun.get(), yrun.get());
          ret.appendLiteral();
          yrun.nWords = 0;
          xrun.nWords = 0;
        }

      } while (!(xrun.end() && yrun.end()));
    }
    ret.active.val = getOpResult(op, this.active.val, other.active.val);
    ret.active.nbits = this.active.nbits;

    ret.doCount();
    return ret;
  }
  /**
   * This is an optimization over the and function. This does not create new bitset. This just
   * counts the number of 1 bits common between the two bitsets.
   *
   * @param other the bitset to and with.
   * @return the number of 1s common between two bitsets.
   */
  public int andSize(WAHBitSet other) {
    int size = 0;

    // ensure that they have the same bit length.
    if (this.numBits() < other.numBits()) {
      this.setBit(other.numBits() - 1, 0);
    } else if (this.numBits() > other.numBits()) {
      other.setBit(numBits() - 1, 0);
    }

    if (vec.size() > 0) {
      run xrun = new run(vec), yrun = new run(other.vec);
      xrun.decode();
      yrun.decode();
      do {
        if (xrun.nWords == 0) {
          xrun.inc();
          xrun.decode();
        }

        if (yrun.nWords == 0) {
          yrun.inc();
          yrun.decode();
        }

        if (xrun.isFill()) {
          if (yrun.isFill()) {
            int nWords = Math.min(xrun.nWords, yrun.nWords);
            if ((xrun.fillWord & yrun.fillWord) == 1) {
              size += nWords * MAXBITS;
            }
            xrun.nWords -= nWords;
            yrun.nWords -= nWords;
          } else {
            size += countInRun(xrun, yrun);
          }
        } else if (yrun.isFill()) {
          size += countInRun(yrun, xrun);
        } else {
          int val = xrun.get() & yrun.get();
          if (val > 0) size += Integer.bitCount(val);
          yrun.nWords = 0;
          xrun.nWords = 0;
        }

      } while (!(xrun.end() && yrun.end()));
    }
    size += Integer.bitCount(this.active.val & other.active.val);

    return size;
  }
 /**
  * Sets the index <tt>i</tt> to one. expands the bitset if required.
  *
  * <p>Note: currently we are going to support only increasing the indexes.
  *
  * @param i the index to set as 1.
  */
 public void set(int i) {
   setBit(i, 1);
 }
  /**
   * Returns a new WAH compressed bitset after anding the current bitset with the <i>other</i>
   * bitset.
   *
   * @param other the bitset to and with
   * @return The resulting bitset
   */
  public WAHBitSet and(WAHBitSet other) {
    WAHBitSet ret = new WAHBitSet();

    // ensure that they have the same bit length.
    if (this.numBits() < other.numBits()) {
      this.setBit(other.numBits() - 1, 0);
    } else if (this.numBits() > other.numBits()) {
      other.setBit(numBits() - 1, 0);
    }

    // if there is something in the vector.
    if (vec.size() > 0) {
      // create new run objects and decode them.
      run xrun = new run(vec), yrun = new run(other.vec);
      xrun.decode();
      yrun.decode();
      do {
        // if you finished a run, then get the next one.
        if (xrun.nWords == 0) {
          xrun.inc();
          xrun.decode();
        }

        if (yrun.nWords == 0) {
          yrun.inc();
          yrun.decode();
        }

        if (xrun.isFill()) {
          if (yrun.isFill()) {
            // both are fills... this is the best.
            int nWords = Math.min(xrun.nWords, yrun.nWords);
            ret.appendFill(nWords, xrun.fillWord & yrun.fillWord);
            xrun.nWords -= nWords;
            yrun.nWords -= nWords;
          } else {
            // just cut through the other run
            chewUpRun(xrun, ret, yrun);
          }
        } else if (yrun.isFill()) {
          // again do the same, with different order.
          chewUpRun(yrun, ret, xrun);
        } else {
          // both are literals, so get the new literal and
          // append it to the return value.
          ret.active.val = xrun.get() & yrun.get();
          ret.appendLiteral();
          yrun.nWords = 0;
          xrun.nWords = 0;
        }
        // till they are not at the end.
      } while (!(xrun.end() && yrun.end()));
    }

    // set the active word.
    ret.active.val = this.active.val & other.active.val;
    ret.active.nbits = this.active.nbits;

    // ensure that the counts are set properly.
    ret.doCount();

    return ret;
  }