blob: abfc039788adf9943c37ef8bfbb1f167504ec102 [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.Attributes;
using Lucene.Net.Randomized.Generators;
using Lucene.Net.Support;
using NUnit.Framework;
using System.Collections;
namespace Lucene.Net.Util
{
using DocIdSetIterator = Lucene.Net.Search.DocIdSetIterator;
[TestFixture]
public class TestOpenBitSet : BaseDocIdSetTestCase<OpenBitSet>
{
public override OpenBitSet CopyOf(BitArray bs, int length)
{
OpenBitSet set = new OpenBitSet(length);
for (int doc = bs.NextSetBit(0); doc != -1; doc = bs.NextSetBit(doc + 1))
{
set.Set(doc);
}
return set;
}
internal virtual void DoGet(BitArray a, OpenBitSet b)
{
int max = a.Length;
for (int i = 0; i < max; i++)
{
if (a.SafeGet(i) != b.Get(i))
{
Assert.Fail("mismatch: BitSet=[" + i + "]=" + a.SafeGet(i));
}
if (a.SafeGet(i) != b.Get((long)i))
{
Assert.Fail("mismatch: BitSet=[" + i + "]=" + a.SafeGet(i));
}
}
}
internal virtual void DoGetFast(BitArray a, OpenBitSet b, int max)
{
for (int i = 0; i < max; i++)
{
if (a.SafeGet(i) != b.FastGet(i))
{
Assert.Fail("mismatch: BitSet=[" + i + "]=" + a.SafeGet(i));
}
if (a.SafeGet(i) != b.FastGet((long)i))
{
Assert.Fail("mismatch: BitSet=[" + i + "]=" + a.SafeGet(i));
}
}
}
internal virtual void DoNextSetBit(BitArray a, OpenBitSet b)
{
int aa = -1, bb = -1;
do
{
aa = a.NextSetBit(aa + 1);
bb = b.NextSetBit(bb + 1);
Assert.AreEqual(aa, bb);
} while (aa >= 0);
}
internal virtual void DoNextSetBitLong(BitArray a, OpenBitSet b)
{
int aa = -1, bb = -1;
do
{
aa = a.NextSetBit(aa + 1);
bb = (int)b.NextSetBit((long)(bb + 1));
Assert.AreEqual(aa, bb);
} while (aa >= 0);
}
internal virtual void DoPrevSetBit(BitArray a, OpenBitSet b)
{
int aa = a.Length + Random.Next(100);
int bb = aa;
do
{
// aa = a.PrevSetBit(aa-1);
aa--;
while ((aa >= 0) && (!a.SafeGet(aa)))
{
aa--;
}
bb = b.PrevSetBit(bb - 1);
Assert.AreEqual(aa, bb);
} while (aa >= 0);
}
internal virtual void DoPrevSetBitLong(BitArray a, OpenBitSet b)
{
int aa = a.Length + Random.Next(100);
int bb = aa;
do
{
// aa = a.PrevSetBit(aa-1);
aa--;
while ((aa >= 0) && (!a.SafeGet(aa)))
{
aa--;
}
bb = (int)b.PrevSetBit((long)(bb - 1));
Assert.AreEqual(aa, bb);
} while (aa >= 0);
}
// test interleaving different OpenBitSetIterator.Next()/skipTo()
internal virtual void DoIterate(BitArray a, OpenBitSet b, int mode)
{
if (mode == 1)
{
DoIterate1(a, b);
}
if (mode == 2)
{
DoIterate2(a, b);
}
}
internal virtual void DoIterate1(BitArray a, OpenBitSet b)
{
int aa = -1, bb = -1;
OpenBitSetIterator iterator = new OpenBitSetIterator(b);
do
{
aa = a.NextSetBit(aa + 1);
bb = Random.NextBoolean() ? iterator.NextDoc() : iterator.Advance(bb + 1);
Assert.AreEqual(aa == -1 ? DocIdSetIterator.NO_MORE_DOCS : aa, bb);
} while (aa >= 0);
}
internal virtual void DoIterate2(BitArray a, OpenBitSet b)
{
int aa = -1, bb = -1;
OpenBitSetIterator iterator = new OpenBitSetIterator(b);
do
{
aa = a.NextSetBit(aa + 1);
bb = Random.NextBoolean() ? 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)
{
BitArray a0 = null;
OpenBitSet b0 = null;
for (int i = 0; i < iter; i++)
{
int sz = Random.Next(maxSize);
BitArray a = new BitArray(sz);
OpenBitSet b = new OpenBitSet(sz);
// test the various ways of setting bits
if (sz > 0)
{
int nOper = Random.Next(sz);
for (int j = 0; j < nOper; j++)
{
int idx;
idx = Random.Next(sz);
a.SafeSet(idx, true);
b.FastSet(idx);
idx = Random.Next(sz);
a.SafeSet(idx, true);
b.FastSet((long)idx);
idx = Random.Next(sz);
a.SafeSet(idx, false);
b.FastClear(idx);
idx = Random.Next(sz);
a.SafeSet(idx, false);
b.FastClear((long)idx);
idx = Random.Next(sz);
a.SafeSet(idx, !a.SafeGet(idx));
b.FastFlip(idx);
bool val = b.FlipAndGet(idx);
bool val2 = b.FlipAndGet(idx);
Assert.IsTrue(val != val2);
idx = Random.Next(sz);
a.SafeSet(idx, !a.SafeGet(idx));
b.FastFlip((long)idx);
val = b.FlipAndGet((long)idx);
val2 = b.FlipAndGet((long)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);
DoGetFast(a, b, sz);
// test ranges, including possible extension
int fromIndex, toIndex;
fromIndex = Random.Next(sz + 80);
toIndex = fromIndex + Random.Next((sz >> 1) + 1);
BitArray aa = new BitArray(a);
// C# BitArray class does not support dynamic sizing.
// We have to explicitly change its size using the Length attribute.
if (toIndex > aa.Length)
{
aa.Length = toIndex;
}
aa.Flip(fromIndex, toIndex);
OpenBitSet bb = (OpenBitSet)b.Clone();
bb.Flip(fromIndex, toIndex);
DoIterate(aa, bb, mode); // a problem here is from flip or doIterate
fromIndex = Random.Next(sz + 80);
toIndex = fromIndex + Random.Next((sz >> 1) + 1);
aa = new BitArray(a);
if (toIndex > aa.Length)
{
aa.Length = toIndex;
}
aa.Clear(fromIndex, toIndex);
bb = (OpenBitSet)b.Clone();
bb.Clear(fromIndex, toIndex);
DoNextSetBit(aa, bb); // a problem here is from clear() or nextSetBit
DoNextSetBitLong(aa, bb);
DoPrevSetBit(aa, bb);
DoPrevSetBitLong(aa, bb);
fromIndex = Random.Next(sz + 80);
toIndex = fromIndex + Random.Next((sz >> 1) + 1);
aa = new BitArray(a);
if (toIndex > aa.Length)
{
aa.Length = toIndex;
}
aa.Set(fromIndex, toIndex);
bb = (OpenBitSet)b.Clone();
bb.Set(fromIndex, toIndex);
DoNextSetBit(aa, bb); // a problem here is from set() or nextSetBit
DoNextSetBitLong(aa, bb);
DoPrevSetBit(aa, bb);
DoPrevSetBitLong(aa, bb);
if (a0 != null)
{
aa = new BitArray(a);
BitArray aa0 = new BitArray(a0);
int largest = Math.Max(a.Length, a0.Length);
aa.Length = aa0.Length = largest;
// BitWiseEquals needs both arrays to be the same size for succeeding.
// We could enlarge the smallest of a and a0, but then the tests below
// won't test "UnequalLengths" operations.
Assert.AreEqual(aa.BitWiseEquals(aa0), b.Equals(b0));
Assert.AreEqual(a.Cardinality(), b.Cardinality());
BitArray a_and = new BitArray(a);
a_and = a_and.And_UnequalLengths(a0);
BitArray a_or = new BitArray(a);
a_or = a_or.Or_UnequalLengths(a0);
BitArray a_xor = new BitArray(a);
a_xor = a_xor.Xor_UnequalLengths(a0);
BitArray a_andn = new BitArray(a);
a_andn.AndNot(a0);
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(a_and.Cardinality(), b_and.Cardinality());
Assert.AreEqual(a_or.Cardinality(), b_or.Cardinality());
Assert.AreEqual(a_xor.Cardinality(), b_xor.Cardinality());
Assert.AreEqual(a_andn.Cardinality(), 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, LongRunningTest]
public virtual void TestSmall()
{
DoRandomSets(AtLeast(1200), AtLeast(1000), 1);
DoRandomSets(AtLeast(1200), AtLeast(1000), 2);
}
[Test, LuceneNetSpecific]
public void TestClearSmall()
{
OpenBitSet a = new OpenBitSet(30); // 0110010111001000101101001001110...0
int[] onesA = { 1, 2, 5, 7, 8, 9, 12, 16, 18, 19, 21, 24, 27, 28, 29 };
for (int i = 0; i < onesA.size(); i++)
{
a.Set(onesA[i]);
}
OpenBitSet b = new OpenBitSet(30); // 0110000001001000101101001001110...0
int[] onesB = { 1, 2, 9, 12, 16, 18, 19, 21, 24, 27, 28, 29 };
for (int i = 0; i < onesB.size(); i++)
{
b.Set(onesB[i]);
}
a.Clear(5, 9);
Assert.True(a.Equals(b));
a.Clear(9, 10);
Assert.False(a.Equals(b));
a.Set(9);
Assert.True(a.Equals(b));
}
[Test, LuceneNetSpecific]
public void TestClearLarge()
{
int iters = AtLeast(1000);
for (int it = 0; it < iters; it++)
{
Random random = new Random();
int sz = AtLeast(1200);
OpenBitSet a = new OpenBitSet(sz);
OpenBitSet b = new OpenBitSet(sz);
int from = random.Next(sz - 1);
int to = random.Next(from, sz);
for (int i = 0; i < sz / 2; i++)
{
int index = random.Next(sz - 1);
a.Set(index);
if (index < from || index >= to)
{
b.Set(index);
}
}
a.Clear(from, to);
Assert.True(a.Equals(b));
}
}
// uncomment to run a bigger test (~2 minutes).
/*
public void TestBig() {
doRandomSets(2000,200000, 1);
doRandomSets(2000,200000, 2);
}
*/
[Test]
public virtual void TestEquals()
{
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 object()));
}
[Test]
public virtual 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());
}
private OpenBitSet MakeOpenBitSet(int[] a)
{
OpenBitSet bs = new OpenBitSet();
foreach (int e in a)
{
bs.Set(e);
}
return bs;
}
private BitArray MakeBitSet(int[] a)
{
BitArray bs = new BitArray(a.Length);
foreach (int e in a)
{
bs.SafeSet(e, true);
}
return bs;
}
private void CheckPrevSetBitArray(int[] a)
{
OpenBitSet obs = MakeOpenBitSet(a);
BitArray bs = MakeBitSet(a);
DoPrevSetBit(bs, obs);
}
[Test]
public virtual void TestPrevSetBit()
{
CheckPrevSetBitArray(new int[] { });
CheckPrevSetBitArray(new int[] { 0 });
CheckPrevSetBitArray(new int[] { 0, 2 });
}
[Test]
public virtual void TestEnsureCapacity()
{
OpenBitSet bits = new OpenBitSet(1);
int bit = Random.Next(100) + 10;
bits.EnsureCapacity(bit); // make room for more bits
bits.FastSet(bit - 1);
Assert.IsTrue(bits.FastGet(bit - 1));
bits.EnsureCapacity(bit + 1);
bits.FastSet(bit);
Assert.IsTrue(bits.FastGet(bit));
bits.EnsureCapacity(3); // should not change numBits nor grow the array
bits.FastSet(3);
Assert.IsTrue(bits.FastGet(3));
bits.FastSet(bit - 1);
Assert.IsTrue(bits.FastGet(bit - 1));
// test ensureCapacityWords
int numWords = Random.Next(10) + 2; // make sure we grow the array (at least 128 bits)
bits.EnsureCapacityWords(numWords);
bit = TestUtil.NextInt32(Random, 127, (numWords << 6) - 1); // pick a bit >= to 128, but still within range
bits.FastSet(bit);
Assert.IsTrue(bits.FastGet(bit));
bits.FastClear(bit);
Assert.IsFalse(bits.FastGet(bit));
bits.FastFlip(bit);
Assert.IsTrue(bits.FastGet(bit));
bits.EnsureCapacityWords(2); // should not change numBits nor grow the array
bits.FastSet(3);
Assert.IsTrue(bits.FastGet(3));
bits.FastSet(bit - 1);
Assert.IsTrue(bits.FastGet(bit - 1));
}
[Test, LuceneNetSpecific] // https://github.com/apache/lucenenet/pull/154
public virtual void TestXorWithDifferentCapacity()
{
OpenBitSet smaller = new OpenBitSet(2);
OpenBitSet larger = new OpenBitSet(64 * 10000);
larger.Set(64 * 10000 - 1);
larger.Set(65);
larger.Set(3);
smaller.Set(3);
smaller.Set(66);
smaller.Xor(larger);
Assert.IsTrue(smaller.Get(64 * 10000 - 1));
Assert.IsTrue(smaller.Get(65));
Assert.IsFalse(smaller.Get(3));
Assert.IsTrue(smaller.Get(66));
}
}
}