Permalink
Browse files

LUCENE-3179: add OpenBitSet.prevSetBit

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1137054 13f79535-47bb-0310-9956-ffa450edef68
  • Loading branch information...
1 parent 4c4019f commit c55be3bcd37b5a9d3e23ae3f9caf3c92448942b7 @mikemccand mikemccand committed Jun 17, 2011
View
@@ -543,6 +543,8 @@ New Features
* LUCENE-3191: Added TopDocs.merge, to facilitate merging results from
different shards (Uwe Schindler, Mike McCandless)
+* LUCENE-3179: Added OpenBitSet.prevSetBit (Paul Elschot via Mike McCandless)
+
Build
* LUCENE-1344: Create OSGi bundle using dev-tools/maven.
@@ -778,6 +778,28 @@ public static int ntz3(long x) {
return n - (y & 1);
}
+ /** table of number of leading zeros in a byte */
+ public static final byte[] nlzTable = {8,7,6,6,5,5,5,5,4,4,4,4,4,4,4,4,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
+
+ /** Returns the number of leading zero bits.
+ */
+ public static int nlz(long x) {
+ int n = 0;
+ // do the first step as a long
+ int y = (int)(x>>>32);
+ if (y==0) {n+=32; y = (int)(x); }
+ if ((y & 0xFFFF0000) == 0) { n+=16; y<<=16; }
+ if ((y & 0xFF000000) == 0) { n+=8; y<<=8; }
+ return n + nlzTable[y >>> 24];
+ /* implementation without table:
+ if ((y & 0xF0000000) == 0) { n+=4; y<<=4; }
+ if ((y & 0xC0000000) == 0) { n+=2; y<<=2; }
+ if ((y & 0x80000000) == 0) { n+=1; y<<=1; }
+ if ((y & 0x80000000) == 0) { n+=1;}
+ return n;
+ */
+ }
+
/** returns true if v is a power of two or zero*/
public static boolean isPowerOfTwo(int v) {
@@ -659,7 +659,34 @@ public long nextSetBit(long index) {
}
+ /** Returns the index of the first set bit starting downwards at
+ * the index specified.
+ * -1 is returned if there are no more set bits.
+ */
+ public int prevSetBit(int index) {
+ if (index < 0) {
+ return -1;
+ }
+ int i = index>>6;
+ if (i >= wlen) {
+ i = wlen - 1;
+ }
+ 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;
+ }
@Override
public Object clone() {
@@ -0,0 +1,133 @@
+/**
+ * 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;
+
+public class TestBitUtil extends LuceneTestCase {
+
+ private static int slowNlz(long x) {
+ if (x == 0L) return 64;
+ int nlz = 0;
+ while ( ((x << nlz) & (1L << 63)) == 0) {
+ nlz++;
+ }
+ return nlz;
+ }
+
+ private void checkNlz(long x) {
+ assertEquals(slowNlz(x), BitUtil.nlz(x));
+ assertEquals(Long.numberOfLeadingZeros(x), BitUtil.nlz(x));
+ }
+
+ public void testNlz() {
+ checkNlz(0L);
+ checkNlz(1L);
+ checkNlz(-1L);
+ for (int i = 1; i <= 63; i++) {
+ checkNlz(1L << i);
+ checkNlz((1L << i) + (1L << (i>>1)));
+ }
+ }
+
+ public void testBitUtils() {
+ long num = 100000;
+ assertEquals( 5, BitUtil.ntz(num) );
+ assertEquals( 5, BitUtil.ntz2(num) );
+ assertEquals( 5, BitUtil.ntz3(num) );
+
+ num = 10;
+ assertEquals( 1, BitUtil.ntz(num) );
+ assertEquals( 1, BitUtil.ntz2(num) );
+ assertEquals( 1, BitUtil.ntz3(num) );
+
+ for (int i=0; i<64; i++) {
+ num = 1L << i;
+ assertEquals( i, BitUtil.ntz(num) );
+ assertEquals( i, BitUtil.ntz2(num) );
+ assertEquals( i, BitUtil.ntz3(num) );
+ }
+ }
+
+
+ private long testArg(int shift) {
+ return (1L << shift) + (1L << (shift>>1));
+ }
+
+ private long nlzBitUtilBasicLoop(int iters) {
+ long sumRes = 0;
+ while (iters-- >= 0) {
+ for (int i = 1; i <= 63; i++) {
+ long a = testArg(i);
+ sumRes += BitUtil.nlz(a);
+ sumRes += BitUtil.nlz(a+1);
+ sumRes += BitUtil.nlz(a-1);
+ sumRes += BitUtil.nlz(a+10);
+ sumRes += BitUtil.nlz(a-10);
+ }
+ }
+ return sumRes;
+ }
+
+ private long nlzLongBasicLoop(int iters) {
+ long sumRes = 0;
+ while (iters-- >= 0) {
+ for (int i = 1; i <= 63; i++) {
+ long a = testArg(i);
+ sumRes += Long.numberOfLeadingZeros(a);
+ sumRes += Long.numberOfLeadingZeros(a+1);
+ sumRes += Long.numberOfLeadingZeros(a-1);
+ sumRes += Long.numberOfLeadingZeros(a+10);
+ sumRes += Long.numberOfLeadingZeros(a-10);
+ }
+ }
+ return sumRes;
+ }
+
+ public void tstPerfNlz() { // See LUCENE-3197, prefer to use Long.numberOfLeadingZeros() over BitUtil.nlz().
+ final long measureMilliSecs = 2000;
+ final int basicIters = 100000;
+ long startTime;
+ long endTime;
+ long curTime;
+ long dummy = 0; // avoid optimizing away
+
+ dummy = 0;
+ int bitUtilLoops = 0;
+ startTime = System.currentTimeMillis();
+ endTime = startTime + measureMilliSecs;
+ do {
+ dummy += nlzBitUtilBasicLoop(basicIters);
+ bitUtilLoops++;
+ curTime = System.currentTimeMillis();
+ } while (curTime < endTime);
+ int bitUtilPsTime = (int) (1000000000 * (curTime - startTime) / (basicIters * 5 * 63 * (float) bitUtilLoops));
+ System.out.println("BitUtil nlz time: " + (bitUtilPsTime/1) + " picosec/call, dummy: " + dummy);
+
+
+ dummy = 0;
+ int longLoops = 0;
+ startTime = System.currentTimeMillis();
+ endTime = startTime + measureMilliSecs;
+ do {
+ dummy += nlzLongBasicLoop(basicIters);
+ longLoops++;
+ curTime = System.currentTimeMillis();
+ } while (curTime < endTime);
+ int longPsTime = (int) (1000000000 * (curTime - startTime) / (basicIters * 5 * 63 * (float) longLoops));
+ System.out.println("Long nlz time: " + longPsTime + " picosec/call, dummy: " + dummy);
+ }
+}
@@ -41,6 +41,20 @@ void doNextSetBit(BitSet a, OpenBitSet b) {
} while (aa>=0);
}
+ void doPrevSetBit(BitSet a, OpenBitSet b) {
+ int aa=a.length();
+ int bb=aa;
+ do {
+ // aa = a.prevSetBit(aa-1);
+ aa--;
+ while ((aa >= 0) && (! a.get(aa))) {
+ aa--;
+ }
+ bb = b.prevSetBit(bb-1);
+ assertEquals(aa,bb);
+ } while (aa>=0);
+ }
+
// test interleaving different OpenBitSetIterator.next()/skipTo()
void doIterate(BitSet a, OpenBitSet b, int mode) {
if (mode==1) doIterate1(a, b);
@@ -123,13 +137,15 @@ void doRandomSets(int maxSize, int iter, int mode) {
bb = (OpenBitSet)b.clone(); bb.clear(fromIndex,toIndex);
doNextSetBit(aa,bb); // a problem here is from clear() or nextSetBit
+ doPrevSetBit(aa,bb);
fromIndex = random.nextInt(sz+80);
toIndex = fromIndex + random.nextInt((sz>>1)+1);
aa = (BitSet)a.clone(); aa.set(fromIndex,toIndex);
bb = (OpenBitSet)b.clone(); bb.set(fromIndex,toIndex);
doNextSetBit(aa,bb); // a problem here is from set() or nextSetBit
+ doPrevSetBit(aa,bb);
if (a0 != null) {
@@ -168,20 +184,21 @@ void doRandomSets(int maxSize, int iter, int mode) {
b0=b;
}
}
-
+
// large enough to flush obvious bugs, small enough to run in <.5 sec as part of a
// larger testsuite.
public void testSmall() {
doRandomSets(atLeast(1200), atLeast(1000), 1);
doRandomSets(atLeast(1200), atLeast(1000), 2);
}
+ // uncomment to run a bigger test (~2 minutes).
+ /*
public void testBig() {
- // uncomment to run a bigger test (~2 minutes).
- // rand = newRandom();
- // doRandomSets(2000,200000, 1);
- // doRandomSets(2000,200000, 2);
+ doRandomSets(2000,200000, 1);
+ doRandomSets(2000,200000, 2);
}
+ */
public void testEquals() {
OpenBitSet b1 = new OpenBitSet(1111);
@@ -205,26 +222,6 @@ public void testEquals() {
assertFalse(b1.equals(new Object()));
}
- public void testBitUtils()
- {
- long num = 100000;
- assertEquals( 5, BitUtil.ntz(num) );
- assertEquals( 5, BitUtil.ntz2(num) );
- assertEquals( 5, BitUtil.ntz3(num) );
-
- num = 10;
- assertEquals( 1, BitUtil.ntz(num) );
- assertEquals( 1, BitUtil.ntz2(num) );
- assertEquals( 1, BitUtil.ntz3(num) );
-
- for (int i=0; i<64; i++) {
- num = 1L << i;
- assertEquals( i, BitUtil.ntz(num) );
- assertEquals( i, BitUtil.ntz2(num) );
- assertEquals( i, BitUtil.ntz3(num) );
- }
- }
-
public void testHashCodeEquals() {
OpenBitSet bs1 = new OpenBitSet(200);
OpenBitSet bs2 = new OpenBitSet(64);
@@ -233,6 +230,35 @@ public void testHashCodeEquals() {
assertEquals(bs1, bs2);
assertEquals(bs1.hashCode(), bs2.hashCode());
}
+
+
+ private OpenBitSet makeOpenBitSet(int[] a) {
+ OpenBitSet bs = new OpenBitSet();
+ for (int e: a) {
+ bs.set(e);
+ }
+ return bs;
+ }
+
+ private BitSet makeBitSet(int[] a) {
+ BitSet bs = new BitSet();
+ for (int e: a) {
+ bs.set(e);
+ }
+ return bs;
+ }
+
+ private void checkPrevSetBitArray(int [] a) {
+ OpenBitSet obs = makeOpenBitSet(a);
+ BitSet bs = makeBitSet(a);
+ doPrevSetBit(bs, obs);
+ }
+
+ public void testPrevSetBit() {
+ checkPrevSetBitArray(new int[] {});
+ checkPrevSetBitArray(new int[] {0});
+ checkPrevSetBitArray(new int[] {0,2});
+ }
}

0 comments on commit c55be3b

Please sign in to comment.