pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / util / FixedBitSet.java
diff --git a/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/util/FixedBitSet.java b/lucene-java-3.5.0/lucene/src/java/org/apache/lucene/util/FixedBitSet.java
new file mode 100644 (file)
index 0000000..6baeb7a
--- /dev/null
@@ -0,0 +1,417 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.util;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.lucene.search.DocIdSet;
+import org.apache.lucene.search.DocIdSetIterator;
+
+// TODO: maybe merge with BitVector?  Problem is BitVector
+// caches its cardinality...
+
+/** BitSet of fixed length (numBits), backed by accessible
+ *  ({@link #getBits}) long[], accessed with an int index,
+ *  implementing Bits and DocIdSet.  Unlike {@link
+ *  OpenBitSet} this bit set does not auto-expand, cannot
+ *  handle long index, and does not have fastXX/XX variants
+ *  (just X).
+ *
+ * @lucene.internal
+ **/
+
+public final class FixedBitSet extends DocIdSet implements Bits {
+  private final long[] bits;
+  private int numBits;
+
+  /** returns the number of 64 bit words it would take to hold numBits */
+  public static int bits2words(int numBits) {
+    int numLong = numBits >>> 6;
+    if ((numBits & 63) != 0) {
+      numLong++;
+    }
+    return numLong;
+  }
+
+  public FixedBitSet(int numBits) {
+    this.numBits = numBits;
+    bits = new long[bits2words(numBits)];
+  }
+
+  /** Makes full copy. */
+  public FixedBitSet(FixedBitSet other) {
+    bits = new long[other.bits.length];
+    System.arraycopy(other.bits, 0, bits, 0, bits.length);
+    numBits = other.numBits;
+  }
+
+  @Override
+  public DocIdSetIterator iterator() {
+    return new OpenBitSetIterator(bits, bits.length);
+  }
+
+  public int length() {
+    return numBits;
+  }
+
+  /** This DocIdSet implementation is cacheable. */
+  @Override
+  public boolean isCacheable() {
+    return true;
+  }
+
+  /** Expert. */
+  public long[] getBits() {
+    return bits;
+  }
+
+  /** Returns number of set bits.  NOTE: this visits every
+   *  long in the backing bits array, and the result is not
+   *  internally cached! */
+  public int cardinality() {
+    return (int) BitUtil.pop_array(bits, 0, bits.length);
+  }
+
+  public boolean get(int index) {
+    assert index >= 0 && index < numBits;
+    int i = index >> 6;               // div 64
+    // signed shift will keep a negative index and force an
+    // array-index-out-of-bounds-exception, removing the need for an explicit check.
+    int bit = index & 0x3f;           // mod 64
+    long bitmask = 1L << bit;
+    return (bits[i] & bitmask) != 0;
+  }
+
+  public void set(int index) {
+    assert index >= 0 && index < numBits;
+    int wordNum = index >> 6;      // div 64
+    int bit = index & 0x3f;     // mod 64
+    long bitmask = 1L << bit;
+    bits[wordNum] |= bitmask;
+  }
+
+  public boolean getAndSet(int index) {
+    assert index >= 0 && index < numBits;
+    int wordNum = index >> 6;      // div 64
+    int bit = index & 0x3f;     // mod 64
+    long bitmask = 1L << bit;
+    boolean val = (bits[wordNum] & bitmask) != 0;
+    bits[wordNum] |= bitmask;
+    return val;
+  }
+
+  public void clear(int index) {
+    assert index >= 0 && index < numBits;
+    int wordNum = index >> 6;
+    int bit = index & 0x03f;
+    long bitmask = 1L << bit;
+    bits[wordNum] &= ~bitmask;
+  }
+
+  public boolean getAndClear(int index) {
+    assert index >= 0 && index < numBits;
+    int wordNum = index >> 6;      // div 64
+    int bit = index & 0x3f;     // mod 64
+    long bitmask = 1L << bit;
+    boolean val = (bits[wordNum] & bitmask) != 0;
+    bits[wordNum] &= ~bitmask;
+    return val;
+  }
+
+  /** Returns the index of the first set bit starting at the index specified.
+   *  -1 is returned if there are no more set bits.
+   */
+  public int nextSetBit(int index) {
+    assert index >= 0 && index < numBits;
+    int i = index >> 6;
+    final int subIndex = index & 0x3f;      // index within the word
+    long word = bits[i] >> subIndex;  // skip all the bits to the right of index
+
+    if (word!=0) {
+      return (i<<6) + subIndex + BitUtil.ntz(word);
+    }
+
+    while(++i < bits.length) {
+      word = bits[i];
+      if (word != 0) {
+        return (i<<6) + BitUtil.ntz(word);
+      }
+    }
+
+    return -1;
+  }
+
+  /** Returns the index of the last set bit before or on the index specified.
+   *  -1 is returned if there are no more set bits.
+   */
+  public int prevSetBit(int index) {
+    assert index >= 0 && index < numBits: "index=" + index + " numBits=" + numBits;
+    int i = index >> 6;
+    final int subIndex = index & 0x3f;  // index within the word
+    long word = (bits[i] << (63-subIndex));  // skip all the bits to the left of index
+
+    if (word != 0) {
+      return (i << 6) + subIndex - Long.numberOfLeadingZeros(word); // See LUCENE-3197
+    }
+
+    while (--i >= 0) {
+      word = bits[i];
+      if (word !=0 ) {
+        return (i << 6) + 63 - Long.numberOfLeadingZeros(word);
+      }
+    }
+
+    return -1;
+  }
+
+  /** Does in-place OR of the bits provided by the
+   *  iterator. */
+  public void or(DocIdSetIterator iter) throws IOException {
+    if (iter instanceof OpenBitSetIterator && iter.docID() == -1) {
+      final OpenBitSetIterator obs = (OpenBitSetIterator) iter;
+      or(obs.arr, obs.words);
+      // advance after last doc that would be accepted if standard
+      // iteration is used (to exhaust it):
+      obs.advance(numBits);
+    } else {
+      int doc;
+      while ((doc = iter.nextDoc()) < numBits) {
+        set(doc);
+      }
+    }
+  }
+
+  /** this = this OR other */
+  public void or(FixedBitSet other) {
+    or(other.bits, other.bits.length);
+  }
+  
+  private void or(final long[] otherArr, final int otherLen) {
+    final long[] thisArr = this.bits;
+    int pos = Math.min(thisArr.length, otherLen);
+    while (--pos >= 0) {
+      thisArr[pos] |= otherArr[pos];
+    }
+  }
+
+  /** Does in-place AND of the bits provided by the
+   *  iterator. */
+  public void and(DocIdSetIterator iter) throws IOException {
+    if (iter instanceof OpenBitSetIterator && iter.docID() == -1) {
+      final OpenBitSetIterator obs = (OpenBitSetIterator) iter;
+      and(obs.arr, obs.words);
+      // advance after last doc that would be accepted if standard
+      // iteration is used (to exhaust it):
+      obs.advance(numBits);
+    } else {
+      if (numBits == 0) return;
+      int disiDoc, bitSetDoc = nextSetBit(0);
+      while (bitSetDoc != -1 && (disiDoc = iter.advance(bitSetDoc)) < numBits) {
+        clear(bitSetDoc, disiDoc);
+        disiDoc++;
+        bitSetDoc = (disiDoc < numBits) ? nextSetBit(disiDoc) : -1;
+      }
+      if (bitSetDoc != -1) {
+        clear(bitSetDoc, numBits);
+      }
+    }
+  }
+
+  /** this = this AND other */
+  public void and(FixedBitSet other) {
+    and(other.bits, other.bits.length);
+  }
+  
+  private void and(final long[] otherArr, final int otherLen) {
+    final long[] thisArr = this.bits;
+    int pos = Math.min(thisArr.length, otherLen);
+    while(--pos >= 0) {
+      thisArr[pos] &= otherArr[pos];
+    }
+    if (thisArr.length > otherLen) {
+      Arrays.fill(thisArr, otherLen, thisArr.length, 0L);
+    }
+  }
+
+  /** Does in-place AND NOT of the bits provided by the
+   *  iterator. */
+  public void andNot(DocIdSetIterator iter) throws IOException {
+    if (iter instanceof OpenBitSetIterator && iter.docID() == -1) {
+      final OpenBitSetIterator obs = (OpenBitSetIterator) iter;
+      andNot(obs.arr, obs.words);
+      // advance after last doc that would be accepted if standard
+      // iteration is used (to exhaust it):
+      obs.advance(numBits);
+    } else {
+      int doc;
+      while ((doc = iter.nextDoc()) < numBits) {
+        clear(doc);
+      }
+    }
+  }
+
+  /** this = this AND NOT other */
+  public void andNot(FixedBitSet other) {
+    andNot(other.bits, other.bits.length);
+  }
+  
+  private void andNot(final long[] otherArr, final int otherLen) {
+    final long[] thisArr = this.bits;
+    int pos = Math.min(thisArr.length, otherLen);
+    while(--pos >= 0) {
+      thisArr[pos] &= ~otherArr[pos];
+    }
+  }
+
+  // NOTE: no .isEmpty() here because that's trappy (ie,
+  // typically isEmpty is low cost, but this one wouldn't
+  // be)
+
+  /** Flips a range of bits
+   *
+   * @param startIndex lower index
+   * @param endIndex one-past the last bit to flip
+   */
+  public void flip(int startIndex, int endIndex) {
+    assert startIndex >= 0 && startIndex < numBits;
+    assert endIndex >= 0 && endIndex <= numBits;
+    if (endIndex <= startIndex) {
+      return;
+    }
+
+    int startWord = startIndex >> 6;
+    int endWord = (endIndex-1) >> 6;
+
+    /*** Grrr, java shifting wraps around so -1L>>>64 == -1
+     * for that reason, make sure not to use endmask if the bits to flip will
+     * be zero in the last word (redefine endWord to be the last changed...)
+    long startmask = -1L << (startIndex & 0x3f);     // example: 11111...111000
+    long endmask = -1L >>> (64-(endIndex & 0x3f));   // example: 00111...111111
+    ***/
+
+    long startmask = -1L << startIndex;
+    long endmask = -1L >>> -endIndex;  // 64-(endIndex&0x3f) is the same as -endIndex due to wrap
+
+    if (startWord == endWord) {
+      bits[startWord] ^= (startmask & endmask);
+      return;
+    }
+
+    bits[startWord] ^= startmask;
+
+    for (int i=startWord+1; i<endWord; i++) {
+      bits[i] = ~bits[i];
+    }
+
+    bits[endWord] ^= endmask;
+  }
+
+  /** Sets a range of bits
+   *
+   * @param startIndex lower index
+   * @param endIndex one-past the last bit to set
+   */
+  public void set(int startIndex, int endIndex) {
+    assert startIndex >= 0 && startIndex < numBits;
+    assert endIndex >= 0 && endIndex <= numBits;
+    if (endIndex <= startIndex) {
+      return;
+    }
+
+    int startWord = startIndex >> 6;
+    int endWord = (endIndex-1) >> 6;
+
+    long startmask = -1L << startIndex;
+    long endmask = -1L >>> -endIndex;  // 64-(endIndex&0x3f) is the same as -endIndex due to wrap
+
+    if (startWord == endWord) {
+      bits[startWord] |= (startmask & endmask);
+      return;
+    }
+
+    bits[startWord] |= startmask;
+    Arrays.fill(bits, startWord+1, endWord, -1L);
+    bits[endWord] |= endmask;
+  }
+
+  /** Clears a range of bits.
+   *
+   * @param startIndex lower index
+   * @param endIndex one-past the last bit to clear
+   */
+  public void clear(int startIndex, int endIndex) {
+    assert startIndex >= 0 && startIndex < numBits;
+    assert endIndex >= 0 && endIndex <= numBits;
+    if (endIndex <= startIndex) {
+      return;
+    }
+
+    int startWord = startIndex >> 6;
+    int endWord = (endIndex-1) >> 6;
+
+    long startmask = -1L << startIndex;
+    long endmask = -1L >>> -endIndex;  // 64-(endIndex&0x3f) is the same as -endIndex due to wrap
+
+    // invert masks since we are clearing
+    startmask = ~startmask;
+    endmask = ~endmask;
+
+    if (startWord == endWord) {
+      bits[startWord] &= (startmask | endmask);
+      return;
+    }
+
+    bits[startWord] &= startmask;
+    Arrays.fill(bits, startWord+1, endWord, 0L);
+    bits[endWord] &= endmask;
+  }
+
+  @Override
+  public Object clone() {
+    return new FixedBitSet(this);
+  }
+
+  /** returns true if both sets have the same bits set */
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (!(o instanceof FixedBitSet)) {
+      return false;
+    }
+    FixedBitSet other = (FixedBitSet) o;
+    if (numBits != other.length()) {
+      return false;
+    }
+    return Arrays.equals(bits, other.bits);
+  }
+
+  @Override
+  public int hashCode() {
+    long h = 0;
+    for (int i = bits.length; --i>=0;) {
+      h ^= bits[i];
+      h = (h << 1) | (h >>> 63); // rotate left
+    }
+    // fold leftmost bits into right and add a constant to prevent
+    // empty sets from returning 0, which is too common.
+    return (int) ((h>>32) ^ h) + 0x98761234;
+  }
+}