/* * 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. */ using System; using Lucene.Net.Support; using NUnit.Framework; using DocIdSetIterator = Lucene.Net.Search.DocIdSetIterator; namespace Lucene.Net.Util { /// $Id$ /// [TestFixture] public class TestOpenBitSet:LuceneTestCase { internal System.Random rand; internal virtual void DoGet(System.Collections.BitArray a, OpenBitSet b) { int max = a.Count; for (int i = 0; i < max; i++) { Assert.AreEqual(a.Get(i) != b.Get(i), "mismatch: BitSet=[" + i + "]=" + a.Get(i)); } } internal virtual void DoNextSetBit(System.Collections.BitArray a, OpenBitSet b) { int aa = - 1, bb = - 1; do { aa = BitSetSupport.NextSetBit(a, aa + 1); bb = b.NextSetBit(bb + 1); Assert.AreEqual(aa, bb); } while (aa >= 0); } // test interleaving different OpenBitSetIterator.next()/skipTo() internal virtual void DoIterate(System.Collections.BitArray a, OpenBitSet b, int mode) { if (mode == 1) DoIterate1(a, b); if (mode == 2) DoIterate2(a, b); } internal virtual void DoIterate1(System.Collections.BitArray a, OpenBitSet b) { int aa = - 1, bb = - 1; OpenBitSetIterator iterator = new OpenBitSetIterator(b); do { aa = BitSetSupport.NextSetBit(a, aa + 1); bb = rand.NextDouble() > 0.5 ? iterator.NextDoc() : iterator.Advance(bb + 1); Assert.AreEqual(aa == - 1?DocIdSetIterator.NO_MORE_DOCS:aa, bb); } while (aa >= 0); } internal virtual void DoIterate2(System.Collections.BitArray a, OpenBitSet b) { int aa = - 1, bb = - 1; OpenBitSetIterator iterator = new OpenBitSetIterator(b); do { aa = BitSetSupport.NextSetBit(a, aa + 1); bb = rand.NextDouble() > 0.5 ? iterator.NextDoc() : iterator.Advance(bb + 1); Assert.AreEqual(aa == - 1?DocIdSetIterator.NO_MORE_DOCS:aa, bb); } while (aa >= 0); } internal virtual void DoRandomSets(int maxSize, int iter, int mode) { System.Collections.BitArray a0 = null; OpenBitSet b0 = null; for (int i = 0; i < iter; i++) { int sz = rand.Next(maxSize); System.Collections.BitArray a = new System.Collections.BitArray(sz); OpenBitSet b = new OpenBitSet(sz); // test the various ways of setting bits if (sz > 0) { int nOper = rand.Next(sz); for (int j = 0; j < nOper; j++) { int idx; idx = rand.Next(sz); a.Set(idx, true); b.FastSet(idx); idx = rand.Next(sz); a.Set(idx, false); b.FastClear(idx); idx = rand.Next(sz); a.Set(idx, !a.Get(idx)); b.FastFlip(idx); bool val = b.FlipAndGet(idx); bool val2 = b.FlipAndGet(idx); Assert.IsTrue(val != val2); val = b.GetAndSet(idx); Assert.IsTrue(val2 == val); Assert.IsTrue(b.Get(idx)); if (!val) b.FastClear(idx); Assert.IsTrue(b.Get(idx) == val); } } // test that the various ways of accessing the bits are equivalent DoGet(a, b); // {{dougsale-2.4.0}} // // Java's java.util.BitSet automatically grows as needed - i.e., when a bit is referenced beyond // the size of the BitSet, an exception isn't thrown - rather, the set grows to the size of the // referenced bit. // // System.Collections.BitArray does not have this feature, and thus I've faked it here by // "growing" the array explicitly when necessary (creating a new instance of the appropriate size // and setting the appropriate bits). // // test ranges, including possible extension int fromIndex, toIndex; fromIndex = rand.Next(sz + 80); toIndex = fromIndex + rand.Next((sz >> 1) + 1); // {{dougsale-2.4.0}}: // The following commented-out, compound statement's 'for loop' implicitly grows the Java BitSets 'a' // and 'aa' to the same cardinality as 'j+1' when 'a.Count < j+1' and 'fromIndex < toIndex': //BitArray aa = (BitArray)a.Clone(); for (int j = fromIndex; j < toIndex; j++) aa.Set(j, !a.Get(j)); // So, if necessary, lets explicitly grow 'a' now; then 'a' and its clone, 'aa', will be of the required size. if (a.Count < toIndex && fromIndex < toIndex) { System.Collections.BitArray tmp = new System.Collections.BitArray(toIndex, false); for (int k = 0; k < a.Count; k++) tmp.Set(k, a.Get(k)); a = tmp; } // {{dougsale-2.4.0}}: now we can invoke this statement without going 'out-of-bounds' System.Collections.BitArray aa = (System.Collections.BitArray)a.Clone(); for (int j = fromIndex; j < toIndex; j++) aa.Set(j, !a.Get(j)); OpenBitSet bb = (OpenBitSet)b.Clone(); bb.Flip(fromIndex, toIndex); DoIterate(aa, bb, mode); // a problem here is from flip or doIterate fromIndex = rand.Next(sz + 80); toIndex = fromIndex + rand.Next((sz >> 1) + 1); // {{dougsale-2.4.0}}: // The following commented-out, compound statement's 'for loop' implicitly grows the Java BitSet 'aa' // when 'a.Count < j+1' and 'fromIndex < toIndex' //aa = (BitArray)a.Clone(); for (int j = fromIndex; j < toIndex; j++) aa.Set(j, false); // So, if necessary, lets explicitly grow 'aa' now if (a.Count < toIndex && fromIndex < toIndex) { aa = new System.Collections.BitArray(toIndex); for (int k = 0; k < a.Count; k++) aa.Set(k, a.Get(k)); } else { aa = (System.Collections.BitArray)a.Clone(); } for (int j = fromIndex; j < toIndex; j++) aa.Set(j, false); bb = (OpenBitSet)b.Clone(); bb.Clear(fromIndex, toIndex); DoNextSetBit(aa, bb); // a problem here is from clear() or nextSetBit fromIndex = rand.Next(sz + 80); toIndex = fromIndex + rand.Next((sz >> 1) + 1); // {{dougsale-2.4.0}}: // The following commented-out, compound statement's 'for loop' implicitly grows the Java BitSet 'aa' // when 'a.Count < j+1' and 'fromIndex < toIndex' //aa = (BitArray)a.Clone(); for (int j = fromIndex; j < toIndex; j++) aa.Set(j, false); // So, if necessary, lets explicitly grow 'aa' now if (a.Count < toIndex && fromIndex < toIndex) { aa = new System.Collections.BitArray(toIndex); for (int k = 0; k < a.Count; k++) aa.Set(k, a.Get(k)); } else { aa = (System.Collections.BitArray)a.Clone(); } for (int j = fromIndex; j < toIndex; j++) aa.Set(j, true); bb = (OpenBitSet)b.Clone(); bb.Set(fromIndex, toIndex); DoNextSetBit(aa, bb); // a problem here is from set() or nextSetBit if (a0 != null) { Assert.AreEqual(a.Equals(a0), b.Equals(b0)); Assert.AreEqual(BitSetSupport.Cardinality(a), b.Cardinality()); // {{dougsale-2.4.0}} // // The Java code used java.util.BitSet, which grows as needed. // When a bit, outside the dimension of the set is referenced, // the set automatically grows to the necessary size. The // new entries default to false. // // BitArray does not grow automatically and is not growable. // Thus when BitArray instances of mismatched cardinality // interact, we must first explicitly "grow" the smaller one. // // This growth is acheived by creating a new instance of the // required size and copying the appropriate values. // //BitArray a_and = (BitArray)a.Clone(); a_and.And(a0); //BitArray a_or = (BitArray)a.Clone(); a_or.Or(a0); //BitArray a_xor = (BitArray)a.Clone(); a_xor.Xor(a0); //BitArray a_andn = (BitArray)a.Clone(); for (int j = 0; j < a_andn.Count; j++) if (a0.Get(j)) a_andn.Set(j, false); System.Collections.BitArray a_and; System.Collections.BitArray a_or; System.Collections.BitArray a_xor; System.Collections.BitArray a_andn; if (a.Count < a0.Count) { // the Java code would have implicitly resized 'a_and', 'a_or', 'a_xor', and 'a_andn' // in this case, so we explicitly create a resized stand-in for 'a' here, allowing for // a to keep its original size while 'a_and', 'a_or', 'a_xor', and 'a_andn' are resized System.Collections.BitArray tmp = new System.Collections.BitArray(a0.Count, false); for (int z = 0; z < a.Count; z++) tmp.Set(z, a.Get(z)); a_and = (System.Collections.BitArray)tmp.Clone(); a_and.And(a0); a_or = (System.Collections.BitArray)tmp.Clone(); a_or.Or(a0); a_xor = (System.Collections.BitArray)tmp.Clone(); a_xor.Xor(a0); a_andn = (System.Collections.BitArray)tmp.Clone(); for (int j = 0; j < a_andn.Count; j++) if (a0.Get(j)) a_andn.Set(j, false); } else if (a.Count > a0.Count) { // the Java code would have implicitly resized 'a0' in this case, so // we explicitly do so here: System.Collections.BitArray tmp = new System.Collections.BitArray(a.Count, false); for (int z = 0; z < a0.Count; z++) tmp.Set(z, a0.Get(z)); a0 = tmp; a_and = (System.Collections.BitArray)a.Clone(); a_and.And(a0); a_or = (System.Collections.BitArray)a.Clone(); a_or.Or(a0); a_xor = (System.Collections.BitArray)a.Clone(); a_xor.Xor(a0); a_andn = (System.Collections.BitArray)a.Clone(); for (int j = 0; j < a_andn.Count; j++) if (a0.Get(j)) a_andn.Set(j, false); } else { // 'a' and 'a0' are the same size, no explicit growing necessary a_and = (System.Collections.BitArray)a.Clone(); a_and.And(a0); a_or = (System.Collections.BitArray)a.Clone(); a_or.Or(a0); a_xor = (System.Collections.BitArray)a.Clone(); a_xor.Xor(a0); a_andn = (System.Collections.BitArray)a.Clone(); for (int j = 0; j < a_andn.Count; j++) if (a0.Get(j)) a_andn.Set(j, false); } OpenBitSet b_and = (OpenBitSet)b.Clone(); Assert.AreEqual(b, b_and); b_and.And(b0); OpenBitSet b_or = (OpenBitSet)b.Clone(); b_or.Or(b0); OpenBitSet b_xor = (OpenBitSet)b.Clone(); b_xor.Xor(b0); OpenBitSet b_andn = (OpenBitSet)b.Clone(); b_andn.AndNot(b0); DoIterate(a_and, b_and, mode); DoIterate(a_or, b_or, mode); DoIterate(a_xor, b_xor, mode); DoIterate(a_andn, b_andn, mode); Assert.AreEqual(BitSetSupport.Cardinality(a_and), b_and.Cardinality()); Assert.AreEqual(BitSetSupport.Cardinality(a_or), b_or.Cardinality()); Assert.AreEqual(BitSetSupport.Cardinality(a_xor), b_xor.Cardinality()); Assert.AreEqual(BitSetSupport.Cardinality(a_andn), b_andn.Cardinality()); // test non-mutating popcounts Assert.AreEqual(b_and.Cardinality(), OpenBitSet.IntersectionCount(b, b0)); Assert.AreEqual(b_or.Cardinality(), OpenBitSet.UnionCount(b, b0)); Assert.AreEqual(b_xor.Cardinality(), OpenBitSet.XorCount(b, b0)); Assert.AreEqual(b_andn.Cardinality(), OpenBitSet.AndNotCount(b, b0)); } a0 = a; b0 = b; } } // large enough to flush obvious bugs, small enough to run in <.5 sec as part of a // larger testsuite. [Test] public virtual void TestSmall() { // TODO: fix for 64 bit tests. if (IntPtr.Size == 4) { rand = NewRandom(); DoRandomSets(1200, 1000, 1); DoRandomSets(1200, 1000, 2); } } [Test] public virtual void TestBig() { // uncomment to run a bigger test (~2 minutes). // rand = newRandom(); // doRandomSets(2000,200000, 1); // doRandomSets(2000,200000, 2); } [Test] public virtual void TestEquals() { rand = NewRandom(); OpenBitSet b1 = new OpenBitSet(1111); OpenBitSet b2 = new OpenBitSet(2222); Assert.IsTrue(b1.Equals(b2)); Assert.IsTrue(b2.Equals(b1)); b1.Set(10); Assert.IsFalse(b1.Equals(b2)); Assert.IsFalse(b2.Equals(b1)); b2.Set(10); Assert.IsTrue(b1.Equals(b2)); Assert.IsTrue(b2.Equals(b1)); b2.Set(2221); Assert.IsFalse(b1.Equals(b2)); Assert.IsFalse(b2.Equals(b1)); b1.Set(2221); Assert.IsTrue(b1.Equals(b2)); Assert.IsTrue(b2.Equals(b1)); // try different type of object Assert.IsFalse(b1.Equals(new System.Object())); } [Test] public virtual void TestBitUtils() { rand = NewRandom(); long num = 100000; Assert.AreEqual(5, BitUtil.Ntz(num)); Assert.AreEqual(5, BitUtil.Ntz2(num)); Assert.AreEqual(5, BitUtil.Ntz3(num)); num = 10; Assert.AreEqual(1, BitUtil.Ntz(num)); Assert.AreEqual(1, BitUtil.Ntz2(num)); Assert.AreEqual(1, BitUtil.Ntz3(num)); for (int i = 0; i < 64; i++) { num = 1L << i; Assert.AreEqual(i, BitUtil.Ntz(num)); Assert.AreEqual(i, BitUtil.Ntz2(num)); Assert.AreEqual(i, BitUtil.Ntz3(num)); } } [Test] public void TestHashCodeEquals() { OpenBitSet bs1 = new OpenBitSet(200); OpenBitSet bs2 = new OpenBitSet(64); bs1.Set(3); bs2.Set(3); Assert.AreEqual(bs1, bs2); Assert.AreEqual(bs1.GetHashCode(), bs2.GetHashCode()); } } }