Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions Equativ.RoaringBitmaps.Benchmarks/ContainerOperatorBenchmark.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
using System;
using System.Linq;
using BenchmarkDotNet.Attributes;

namespace Equativ.RoaringBitmaps.Benchmark;

[MemoryDiagnoser(false)]
public class ContainerOperatorBenchmark
{
private Container[] _lhs = Array.Empty<Container>();
private Container[] _rhs = Array.Empty<Container>();

[Params(1000)]
public int Size { get; set; }

[GlobalSetup]
public void Setup()
{
var rnd = new Random(42);
_lhs = new Container[Size];
_rhs = new Container[Size];
for (int i = 0; i < Size; i++)
{
var start1 = rnd.Next(0, ushort.MaxValue - 200);
var start2 = rnd.Next(0, ushort.MaxValue - 200);
_lhs[i] = ArrayContainer.Create(Enumerable.Range(start1, 100).Select(x => (ushort)x).ToArray());
_rhs[i] = ArrayContainer.Create(Enumerable.Range(start2, 100).Select(x => (ushort)x).ToArray());
}
}

[Benchmark]
public int Or()
{
int total = 0;
for (int i = 0; i < _lhs.Length; i++)
{
total += (_lhs[i] | _rhs[i]).Cardinality;
}
return total;
}

[Benchmark]
public int Xor()
{
int total = 0;
for (int i = 0; i < _lhs.Length; i++)
{
total += (_lhs[i] ^ _rhs[i]).Cardinality;
}
return total;
}

[Benchmark]
public int And()
{
int total = 0;
for (int i = 0; i < _lhs.Length; i++)
{
total += (_lhs[i] & _rhs[i]).Cardinality;
}
return total;
}

[Benchmark]
public int AndNot()
{
int total = 0;
for (int i = 0; i < _lhs.Length; i++)
{
total += Container.AndNot(_lhs[i], _rhs[i]).Cardinality;
}
return total;
}
}
104 changes: 104 additions & 0 deletions Equativ.RoaringBitmaps.Tests/ArrayContainerArrayOpsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using Xunit;

namespace Equativ.RoaringBitmaps.Tests;

public class ArrayContainerArrayOpsTests
{
private static bool BitSet(ulong[] bitmap, int value)
{
int index = value >> 6;
ulong mask = 1UL << value;
return (bitmap[index] & mask) != 0;
}

[Fact]
public void OrArray_EmptyBitmap_SetsAllBits()
{
var ac = ArrayContainer.Create(new ushort[] {1, 63, 64, 500});
var bitmap = new ulong[1024];

int added = ac.OrArray(bitmap);

Assert.Equal(ac.Cardinality, added);
Assert.True(BitSet(bitmap, 1));
Assert.True(BitSet(bitmap, 63));
Assert.True(BitSet(bitmap, 64));
Assert.True(BitSet(bitmap, 500));
}

[Fact]
public void OrArray_WithExistingBits_AddsOnlyMissing()
{
var ac = ArrayContainer.Create(new ushort[] {1, 200, 500});
var bitmap = new ulong[1024];
// pre-set bit 1 and 200
bitmap[1 >> 6] |= 1UL << 1;
bitmap[200 >> 6] |= 1UL << 200;

int added = ac.OrArray(bitmap);

Assert.Equal(1, added); // only value 500 was added
Assert.True(BitSet(bitmap, 1));
Assert.True(BitSet(bitmap, 200));
Assert.True(BitSet(bitmap, 500));
}

[Fact]
public void XorArray_EmptyBitmap_TogglesBits()
{
var ac = ArrayContainer.Create(new ushort[] {2, 100});
var bitmap = new ulong[1024];

int delta = ac.XorArray(bitmap);

Assert.Equal(ac.Cardinality, delta);
Assert.True(BitSet(bitmap, 2));
Assert.True(BitSet(bitmap, 100));
}

[Fact]
public void XorArray_WithExistingBits_TogglesOff()
{
var ac = ArrayContainer.Create(new ushort[] {2, 100});
var bitmap = new ulong[1024];
bitmap[2 >> 6] |= 1UL << 2; // set bit 2

int delta = ac.XorArray(bitmap);

Assert.Equal(0, delta); // one added, one removed
Assert.False(BitSet(bitmap, 2));
Assert.True(BitSet(bitmap, 100));
}

[Fact]
public void AndNotArray_NoBitsSet_NoChange()
{
var ac = ArrayContainer.Create(new ushort[] {3, 30});
var bitmap = new ulong[1024];

int delta = ac.AndNotArray(bitmap);

Assert.Equal(0, delta);
Assert.False(BitSet(bitmap, 3));
Assert.False(BitSet(bitmap, 30));
}

[Fact]
public void AndNotArray_RemovesExistingBits()
{
var ac = ArrayContainer.Create(new ushort[] {3, 30});
var bitmap = new ulong[1024];
bitmap[3 >> 6] |= 1UL << 3;
bitmap[30 >> 6] |= 1UL << 30;
bitmap[40 >> 6] |= 1UL << 40;

int delta = ac.AndNotArray(bitmap);

Assert.Equal(-2, delta);
Assert.False(BitSet(bitmap, 3));
Assert.False(BitSet(bitmap, 30));
Assert.True(BitSet(bitmap, 40));
}
}
59 changes: 59 additions & 0 deletions Equativ.RoaringBitmaps.Tests/ContainerOperatorTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.Linq;
using Xunit;

namespace Equativ.RoaringBitmaps.Tests;

public class ContainerOperatorTests
{
private static List<int> ToList(Container c)
{
var list = new List<int>();
c.EnumerateFill(list, 0);
return list;
}

[Fact]
public void Or_ArrayContainers_ThroughBaseOperator()
{
Container a = ArrayContainer.Create(new ushort[] {1, 3});
Container b = ArrayContainer.Create(new ushort[] {3, 5});

Container result = a | b;

Assert.Equal(new[] {1,3,5}, ToList(result));
}

[Fact]
public void Xor_MixedContainers_ThroughBaseOperator()
{
Container a = ArrayContainer.Create(new ushort[] {1, 2});
Container b = BitmapContainer.Create(new ushort[] {2, 4});

Container result = a ^ b;

Assert.Equal(new[] {1,4}, ToList(result));
}

[Fact]
public void And_MixedContainers_ThroughBaseOperator()
{
Container a = ArrayContainer.Create(new ushort[] {10, 11, 12});
Container b = BitmapContainer.Create(new ushort[] {11, 13});

Container result = a & b;

Assert.Equal(new[] {11}, ToList(result));
}

[Fact]
public void AndNot_MixedContainers()
{
Container a = ArrayContainer.Create(new ushort[] {8, 9, 10});
Container b = BitmapContainer.Create(new ushort[] {9});

Container result = Container.AndNot(a, b);

Assert.Equal(new[] {8,10}, ToList(result));
}
}
Loading