#include "testing/testing.hpp" #include "coding/compressed_bit_vector.hpp" #include "coding/writer.hpp" #include "std/algorithm.hpp" #include "std/iterator.hpp" #include "std/set.hpp" namespace { void Intersect(vector & setBits1, vector & setBits2, vector & result) { sort(setBits1.begin(), setBits1.end()); sort(setBits2.begin(), setBits2.end()); set_intersection(setBits1.begin(), setBits1.end(), setBits2.begin(), setBits2.end(), back_inserter(result)); } void Subtract(vector & setBits1, vector & setBits2, vector & result) { sort(setBits1.begin(), setBits1.end()); sort(setBits2.begin(), setBits2.end()); set_difference(setBits1.begin(), setBits1.end(), setBits2.begin(), setBits2.end(), back_inserter(result)); } void Union(vector & setBits1, vector & setBits2, vector & result) { sort(setBits1.begin(), setBits1.end()); sort(setBits2.begin(), setBits2.end()); set_union(setBits1.begin(), setBits1.end(), setBits2.begin(), setBits2.end(), back_inserter(result)); } template void CheckBinaryOp(TBinaryOp op, vector & setBits1, vector & setBits2, coding::CompressedBitVector const & cbv) { vector expected; op(setBits1, setBits2, expected); TEST_EQUAL(expected.size(), cbv.PopCount(), ()); for (size_t i = 0; i < expected.size(); ++i) TEST(cbv.GetBit(expected[i]), ()); } void CheckIntersection(vector & setBits1, vector & setBits2, coding::CompressedBitVector const & cbv) { CheckBinaryOp(&Intersect, setBits1, setBits2, cbv); } void CheckSubtraction(vector & setBits1, vector & setBits2, coding::CompressedBitVector const & cbv) { CheckBinaryOp(&Subtract, setBits1, setBits2, cbv); } void CheckUnion(vector & setBits1, vector & setBits2, coding::CompressedBitVector const & cbv) { CheckBinaryOp(&Union, setBits1, setBits2, cbv); } void CheckUnion(vector & setBits1, coding::CompressedBitVector::StorageStrategy strategy1, vector & setBits2, coding::CompressedBitVector::StorageStrategy strategy2, coding::CompressedBitVector::StorageStrategy resultStrategy) { auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); TEST(cbv1.get(), ()); TEST(cbv2.get(), ()); TEST_EQUAL(strategy1, cbv1->GetStorageStrategy(), ()); TEST_EQUAL(strategy2, cbv2->GetStorageStrategy(), ()); auto cbv3 = coding::CompressedBitVector::Union(*cbv1, *cbv2); TEST(cbv3.get(), ()); TEST_EQUAL(resultStrategy, cbv3->GetStorageStrategy(), ()); CheckUnion(setBits1, setBits2, *cbv3); } } // namespace UNIT_TEST(CompressedBitVector_Intersect1) { size_t const kNumBits = 100; vector setBits1; vector setBits2; for (size_t i = 0; i < kNumBits; ++i) { if (i > 0) setBits1.push_back(i); if (i + 1 < kNumBits) setBits2.push_back(i); } auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); TEST(cbv1.get(), ()); TEST(cbv2.get(), ()); auto cbv3 = coding::CompressedBitVector::Intersect(*cbv1, *cbv2); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv3->GetStorageStrategy(), ()); CheckIntersection(setBits1, setBits2, *cbv3); } UNIT_TEST(CompressedBitVector_Intersect2) { size_t const kNumBits = 100; vector setBits1; vector setBits2; for (size_t i = 0; i < kNumBits; ++i) { if (i <= kNumBits / 2) setBits1.push_back(i); if (i >= kNumBits / 2) setBits2.push_back(i); } auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); TEST(cbv1.get(), ()); TEST(cbv2.get(), ()); auto cbv3 = coding::CompressedBitVector::Intersect(*cbv1, *cbv2); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv3->GetStorageStrategy(), ()); CheckIntersection(setBits1, setBits2, *cbv3); } UNIT_TEST(CompressedBitVector_Intersect3) { size_t const kNumBits = 100; vector setBits1; vector setBits2; for (size_t i = 0; i < kNumBits; ++i) { if (i % 2 == 0) setBits1.push_back(i); if (i % 3 == 0) setBits2.push_back(i); } auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); TEST(cbv1.get(), ()); TEST(cbv2.get(), ()); auto cbv3 = coding::CompressedBitVector::Intersect(*cbv1, *cbv2); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv3->GetStorageStrategy(), ()); CheckIntersection(setBits1, setBits2, *cbv3); } UNIT_TEST(CompressedBitVector_Intersect4) { size_t const kNumBits = 1000; vector setBits1; vector setBits2; for (size_t i = 0; i < kNumBits; ++i) { if (i % 100 == 0) setBits1.push_back(i); if (i % 150 == 0) setBits2.push_back(i); } auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); TEST(cbv1.get(), ()); TEST(cbv2.get(), ()); auto cbv3 = coding::CompressedBitVector::Intersect(*cbv1, *cbv2); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv3->GetStorageStrategy(), ()); CheckIntersection(setBits1, setBits2, *cbv3); } UNIT_TEST(CompressedBitVector_Subtract1) { vector setBits1 = {0, 1, 2, 3, 4, 5, 6}; vector setBits2 = {1, 2, 3, 4, 5, 6, 7}; auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); TEST(cbv1.get(), ()); TEST(cbv2.get(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv1->GetStorageStrategy(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv2->GetStorageStrategy(), ()); auto cbv3 = coding::CompressedBitVector::Subtract(*cbv1, *cbv2); TEST(cbv3.get(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv3->GetStorageStrategy(), ()); CheckSubtraction(setBits1, setBits2, *cbv3); } UNIT_TEST(CompressedBitVector_Subtract2) { vector setBits1; for (size_t i = 0; i < 100; ++i) setBits1.push_back(i); vector setBits2 = {9, 14}; auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); TEST(cbv1.get(), ()); TEST(cbv2.get(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv1->GetStorageStrategy(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv2->GetStorageStrategy(), ()); auto cbv3 = coding::CompressedBitVector::Subtract(*cbv1, *cbv2); TEST(cbv3.get(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv3->GetStorageStrategy(), ()); CheckSubtraction(setBits1, setBits2, *cbv3); } UNIT_TEST(CompressedBitVector_Subtract3) { vector setBits1 = {0, 9}; vector setBits2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); TEST(cbv1.get(), ()); TEST(cbv2.get(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv1->GetStorageStrategy(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv2->GetStorageStrategy(), ()); auto cbv3 = coding::CompressedBitVector::Subtract(*cbv1, *cbv2); TEST(cbv3.get(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv3->GetStorageStrategy(), ()); CheckSubtraction(setBits1, setBits2, *cbv3); } UNIT_TEST(CompressedBitVector_Subtract4) { vector setBits1 = {0, 5, 15}; vector setBits2 = {0, 10}; auto cbv1 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits1); auto cbv2 = coding::CompressedBitVectorBuilder::FromBitPositions(setBits2); TEST(cbv1.get(), ()); TEST(cbv2.get(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv1->GetStorageStrategy(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv2->GetStorageStrategy(), ()); auto cbv3 = coding::CompressedBitVector::Subtract(*cbv1, *cbv2); TEST(cbv3.get(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv3->GetStorageStrategy(), ()); CheckSubtraction(setBits1, setBits2, *cbv3); } UNIT_TEST(CompressedBitVector_Union_Smoke) { vector setBits1 = {}; vector setBits2 = {}; CheckUnion(setBits1, coding::CompressedBitVector::StorageStrategy::Sparse /* strategy1 */, setBits2, coding::CompressedBitVector::StorageStrategy::Sparse /* strategy2 */, coding::CompressedBitVector::StorageStrategy::Sparse /* resultStrategy */); } UNIT_TEST(CompressedBitVector_Union1) { vector setBits1 = {}; vector setBits2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; CheckUnion(setBits1, coding::CompressedBitVector::StorageStrategy::Sparse /* strategy1 */, setBits2, coding::CompressedBitVector::StorageStrategy::Dense /* strategy2 */, coding::CompressedBitVector::StorageStrategy::Dense /* resultStrategy */); } UNIT_TEST(CompressedBitVector_Union2) { vector setBits1 = {256, 1024}; vector setBits2 = {0, 32, 64}; CheckUnion(setBits1, coding::CompressedBitVector::StorageStrategy::Sparse /* strategy1 */, setBits2, coding::CompressedBitVector::StorageStrategy::Sparse /* strategy2 */, coding::CompressedBitVector::StorageStrategy::Sparse /* resultStrategy */); } UNIT_TEST(CompressedBitVector_Union3) { vector setBits1 = {0, 1, 2, 3, 4, 5, 6}; vector setBits2; for (int i = 0; i < 256; ++i) setBits2.push_back(i); CheckUnion(setBits1, coding::CompressedBitVector::StorageStrategy::Dense /* strategy1 */, setBits2, coding::CompressedBitVector::StorageStrategy::Dense /* strategy2 */, coding::CompressedBitVector::StorageStrategy::Dense /* resultStrategy */); } UNIT_TEST(CompressedBitVector_Union4) { vector setBits1; for (uint64_t i = 0; i < coding::DenseCBV::kBlockSize; ++i) setBits1.push_back(i); vector setBits2 = {1000000000}; CheckUnion(setBits1, coding::CompressedBitVector::StorageStrategy::Dense /* strategy1 */, setBits2, coding::CompressedBitVector::StorageStrategy::Sparse /* strategy2 */, coding::CompressedBitVector::StorageStrategy::Sparse /* resultStrategy */); } UNIT_TEST(CompressedBitVector_SerializationDense) { int const kNumBits = 100; vector setBits; for (size_t i = 0; i < kNumBits; ++i) setBits.push_back(i); vector buf; { MemWriter> writer(buf); auto cbv = coding::CompressedBitVectorBuilder::FromBitPositions(setBits); TEST_EQUAL(setBits.size(), cbv->PopCount(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv->GetStorageStrategy(), ()); cbv->Serialize(writer); } MemReader reader(buf.data(), buf.size()); auto cbv = coding::CompressedBitVectorBuilder::DeserializeFromReader(reader); TEST(cbv.get(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, cbv->GetStorageStrategy(), ()); TEST_EQUAL(setBits.size(), cbv->PopCount(), ()); for (size_t i = 0; i < setBits.size(); ++i) TEST(cbv->GetBit(setBits[i]), ()); } UNIT_TEST(CompressedBitVector_SerializationSparse) { int const kNumBits = 100; vector setBits; for (size_t i = 0; i < kNumBits; ++i) { if (i % 10 == 0) setBits.push_back(i); } vector buf; { MemWriter> writer(buf); auto cbv = coding::CompressedBitVectorBuilder::FromBitPositions(setBits); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv->GetStorageStrategy(), ()); cbv->Serialize(writer); } MemReader reader(buf.data(), buf.size()); auto cbv = coding::CompressedBitVectorBuilder::DeserializeFromReader(reader); TEST(cbv.get(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, cbv->GetStorageStrategy(), ()); TEST_EQUAL(setBits.size(), cbv->PopCount(), ()); for (size_t i = 0; i < setBits.size(); ++i) TEST(cbv->GetBit(setBits[i]), ()); } UNIT_TEST(CompressedBitVector_ForEach) { int const kNumBits = 150; vector denseBits; vector sparseBits; for (size_t i = 0; i < kNumBits; ++i) { denseBits.push_back(i); if (i % 15 == 0) sparseBits.push_back(i); } auto denseCBV = coding::CompressedBitVectorBuilder::FromBitPositions(denseBits); auto sparseCBV = coding::CompressedBitVectorBuilder::FromBitPositions(sparseBits); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Dense, denseCBV->GetStorageStrategy(), ()); TEST_EQUAL(coding::CompressedBitVector::StorageStrategy::Sparse, sparseCBV->GetStorageStrategy(), ()); set denseSet; uint64_t maxPos = 0; coding::CompressedBitVectorEnumerator::ForEach(*denseCBV, [&](uint64_t pos) { denseSet.insert(pos); maxPos = max(maxPos, pos); }); TEST_EQUAL(denseSet.size(), kNumBits, ()); TEST_EQUAL(maxPos, kNumBits - 1, ()); coding::CompressedBitVectorEnumerator::ForEach(*sparseCBV, [](uint64_t pos) { TEST_EQUAL(pos % 15, 0, ()); }); } UNIT_TEST(CompressedBitVector_DenseOneBit) { vector setBits = {0}; unique_ptr cbv(new coding::DenseCBV(setBits)); TEST_EQUAL(cbv->PopCount(), 1, ()); coding::CompressedBitVectorEnumerator::ForEach(*cbv, [&](uint64_t pos) { TEST_EQUAL(pos, 0, ()); }); } UNIT_TEST(CompressedBitVector_LeaveFirstNBitsSmoke) { auto cbv = coding::CompressedBitVectorBuilder::FromBitPositions(vector{}); TEST_EQUAL(cbv->PopCount(), 0, ()); cbv = cbv->LeaveFirstSetNBits(0); TEST_EQUAL(cbv->PopCount(), 0, ()); cbv = cbv->LeaveFirstSetNBits(200); TEST_EQUAL(cbv->PopCount(), 0, ()); } UNIT_TEST(CompressedBitVector_DenseLeaveFirstNBits) { { vector setBits; setBits.assign(coding::DenseCBV::kBlockSize * 4, 1); auto cbv = coding::CompressedBitVectorBuilder::FromBitPositions(setBits); TEST_EQUAL(cbv->PopCount(), coding::DenseCBV::kBlockSize * 4, ()); TEST_EQUAL(cbv->GetStorageStrategy(), coding::CompressedBitVector::StorageStrategy::Dense, ()); cbv = cbv->LeaveFirstSetNBits(0); TEST_EQUAL(cbv->PopCount(), 0, ()); } { vector setBits; for (uint64_t i = 0; i < 100; ++i) setBits.push_back(2 * i); auto cbv = coding::CompressedBitVectorBuilder::FromBitPositions(setBits); TEST_EQUAL(cbv->PopCount(), 100, ()); TEST_EQUAL(cbv->GetStorageStrategy(), coding::CompressedBitVector::StorageStrategy::Dense, ()); cbv = cbv->LeaveFirstSetNBits(50); TEST_EQUAL(cbv->PopCount(), 50, ()); TEST_EQUAL(cbv->GetStorageStrategy(), coding::CompressedBitVector::StorageStrategy::Dense, ()); for (uint64_t i = 0; i < 50; ++i) { TEST(cbv->GetBit(2 * i), ()); TEST(!cbv->GetBit(2 * i + 1), ()); } } } UNIT_TEST(CompressedBitVector_SparseLeaveFirstNBits) { vector setBits; for (int p = 0; p < 10; ++p) setBits.push_back(static_cast(1) << p); auto cbv = coding::CompressedBitVectorBuilder::FromBitPositions(setBits); TEST_EQUAL(cbv->PopCount(), 10, ()); TEST_EQUAL(cbv->GetStorageStrategy(), coding::CompressedBitVector::StorageStrategy::Sparse, ()); cbv = cbv->LeaveFirstSetNBits(100); TEST_EQUAL(cbv->PopCount(), 10, ()); for (uint64_t bit = 0; bit < (1 << 10); ++bit) { if (bit != 0 && (bit & (bit - 1)) == 0) TEST(cbv->GetBit(bit), (bit)); else TEST(!cbv->GetBit(bit), (bit)); } cbv = cbv->LeaveFirstSetNBits(8); TEST_EQUAL(cbv->PopCount(), 8, ()); for (uint64_t bit = 0; bit < (1 << 10); ++bit) { if (bit != 0 && (bit & (bit - 1)) == 0 && bit < (1 << 8)) TEST(cbv->GetBit(bit), (bit)); else TEST(!cbv->GetBit(bit), (bit)); } cbv = cbv->LeaveFirstSetNBits(0); TEST_EQUAL(cbv->PopCount(), 0, ()); for (uint64_t bit = 0; bit < (1 << 10); ++bit) TEST(!cbv->GetBit(bit), (bit)); }