Index: lucene/CHANGES.txt =================================================================== --- lucene/CHANGES.txt (revision 1136910) +++ lucene/CHANGES.txt (working copy) @@ -543,6 +543,8 @@ * 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. Index: lucene/src/test/org/apache/lucene/util/TestOpenBitSet.java =================================================================== --- lucene/src/test/org/apache/lucene/util/TestOpenBitSet.java (revision 1136910) +++ lucene/src/test/org/apache/lucene/util/TestOpenBitSet.java (working copy) @@ -41,6 +41,20 @@ } 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,6 +137,7 @@ 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); @@ -130,6 +145,7 @@ 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,7 +184,7 @@ b0=b; } } - + // large enough to flush obvious bugs, small enough to run in <.5 sec as part of a // larger testsuite. public void testSmall() { @@ -176,12 +192,13 @@ 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 @@ 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 @@ 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}); + } } Index: lucene/src/test/org/apache/lucene/util/TestBitUtil.java =================================================================== --- lucene/src/test/org/apache/lucene/util/TestBitUtil.java (revision 0) +++ lucene/src/test/org/apache/lucene/util/TestBitUtil.java (revision 0) @@ -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); + } +} Property changes on: lucene/src/test/org/apache/lucene/util/TestBitUtil.java ___________________________________________________________________ Added: svn:eol-style + native Index: lucene/src/java/org/apache/lucene/util/BitUtil.java =================================================================== --- lucene/src/java/org/apache/lucene/util/BitUtil.java (revision 1136910) +++ lucene/src/java/org/apache/lucene/util/BitUtil.java (working copy) @@ -778,7 +778,29 @@ 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) { return ((v & (v-1)) == 0); Index: lucene/src/java/org/apache/lucene/util/OpenBitSet.java =================================================================== --- lucene/src/java/org/apache/lucene/util/OpenBitSet.java (revision 1136910) +++ lucene/src/java/org/apache/lucene/util/OpenBitSet.java (working copy) @@ -659,8 +659,35 @@ } + /** 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() { try {