blob: 70d0ea03b0f57da2bab9b93a36a96cbc53c29486 [file] [log] [blame]
/*
* 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
{
/// <version> $Id$
/// </version>
[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());
}
}
}