411 lines
13 KiB
C++
411 lines
13 KiB
C++
|
|
#include <gtest/gtest.h>
|
||
|
|
|
||
|
|
#include <algorithm>
|
||
|
|
#include <cstdint>
|
||
|
|
#include <type_traits>
|
||
|
|
#include <utility>
|
||
|
|
|
||
|
|
#include "dpf/bit_array.hpp"
|
||
|
|
#include "dpf/bitstring.hpp"
|
||
|
|
|
||
|
|
using namespace dpf::literals;
|
||
|
|
|
||
|
|
namespace
|
||
|
|
{
|
||
|
|
|
||
|
|
template <typename Array>
|
||
|
|
std::size_t ref_count(const Array &a, std::size_t lo, std::size_t hi)
|
||
|
|
{
|
||
|
|
std::size_t n = 0;
|
||
|
|
for (std::size_t i = lo; i < hi; ++i)
|
||
|
|
n += static_cast<bool>(a[i]) ? 1u : 0u;
|
||
|
|
return n;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename Array>
|
||
|
|
int ref_parity(const Array &a, std::size_t lo, std::size_t hi)
|
||
|
|
{
|
||
|
|
int p = 0;
|
||
|
|
for (std::size_t i = lo; i < hi; ++i)
|
||
|
|
p ^= static_cast<bool>(a[i]) ? 1 : 0;
|
||
|
|
return p;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename Array>
|
||
|
|
bool ref_all(const Array &a, std::size_t lo, std::size_t hi)
|
||
|
|
{
|
||
|
|
for (std::size_t i = lo; i < hi; ++i)
|
||
|
|
if (!static_cast<bool>(a[i])) return false;
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename Array>
|
||
|
|
bool ref_any(const Array &a, std::size_t lo, std::size_t hi)
|
||
|
|
{
|
||
|
|
for (std::size_t i = lo; i < hi; ++i)
|
||
|
|
if (static_cast<bool>(a[i])) return true;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename Array>
|
||
|
|
void expect_reductions(Array &a)
|
||
|
|
{
|
||
|
|
const std::size_t n = a.size();
|
||
|
|
EXPECT_EQ(a.count(), ref_count(a, 0, n));
|
||
|
|
EXPECT_EQ(static_cast<int>(a.any()), static_cast<int>(ref_any(a, 0, n)));
|
||
|
|
EXPECT_EQ(static_cast<int>(a.none()), static_cast<int>(!ref_any(a, 0, n)));
|
||
|
|
EXPECT_EQ(static_cast<int>(a.all()), static_cast<int>(ref_all(a, 0, n)));
|
||
|
|
EXPECT_EQ(a.parity(), static_cast<std::size_t>(ref_parity(a, 0, n)));
|
||
|
|
|
||
|
|
const std::size_t cuts[][2] = {
|
||
|
|
{0, 0},
|
||
|
|
{0, n},
|
||
|
|
{0, std::min<std::size_t>(n, 1)},
|
||
|
|
{0, std::min<std::size_t>(n, 3)},
|
||
|
|
{std::min<std::size_t>(n, 2), std::min<std::size_t>(n, 10)},
|
||
|
|
{n > 60 ? n - 60 : 0, n},
|
||
|
|
{n > 70 ? std::size_t{60} : std::size_t{0}, std::min<std::size_t>(n, 70)},
|
||
|
|
{n / 2, std::min(n, n / 2 + 1)},
|
||
|
|
};
|
||
|
|
for (const auto &c : cuts)
|
||
|
|
{
|
||
|
|
if (c[0] > c[1] || c[1] > n) continue;
|
||
|
|
auto first = a.begin() + static_cast<std::ptrdiff_t>(c[0]);
|
||
|
|
auto last = a.begin() + static_cast<std::ptrdiff_t>(c[1]);
|
||
|
|
EXPECT_EQ(a.count(first, last), ref_count(a, c[0], c[1]));
|
||
|
|
EXPECT_EQ(static_cast<int>(a.any(first, last)), static_cast<int>(ref_any(a, c[0], c[1])));
|
||
|
|
EXPECT_EQ(static_cast<int>(a.all(first, last)), static_cast<int>(ref_all(a, c[0], c[1])));
|
||
|
|
EXPECT_EQ(static_cast<int>(a.none(first, last)), static_cast<int>(!ref_any(a, c[0], c[1])));
|
||
|
|
EXPECT_EQ(a.parity(first, last), static_cast<std::size_t>(ref_parity(a, c[0], c[1])));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
template <typename Array>
|
||
|
|
void exercise(Array a)
|
||
|
|
{
|
||
|
|
const std::size_t n = a.size();
|
||
|
|
a.unset();
|
||
|
|
expect_reductions(a);
|
||
|
|
|
||
|
|
if (n > 0)
|
||
|
|
{
|
||
|
|
a.set(n / 2, true);
|
||
|
|
if (n > 2) a.set(0, true);
|
||
|
|
expect_reductions(a);
|
||
|
|
}
|
||
|
|
|
||
|
|
a.unset();
|
||
|
|
for (std::size_t i = 0; i < n; ++i) a.set(i, true);
|
||
|
|
expect_reductions(a);
|
||
|
|
EXPECT_EQ(a.count(), n);
|
||
|
|
if (n > 0) EXPECT_TRUE(a.all());
|
||
|
|
|
||
|
|
a.set();
|
||
|
|
expect_reductions(a);
|
||
|
|
EXPECT_EQ(a.count(), n);
|
||
|
|
if (n > 0) EXPECT_TRUE(a.all());
|
||
|
|
EXPECT_EQ(a.parity(), n % 2);
|
||
|
|
|
||
|
|
a.unset();
|
||
|
|
for (std::size_t i = 0; i < n; ++i) a.set(i, (i % 3) != 0);
|
||
|
|
expect_reductions(a);
|
||
|
|
|
||
|
|
if (n > 1)
|
||
|
|
{
|
||
|
|
EXPECT_EQ(a.end() - static_cast<std::ptrdiff_t>(n), a.begin());
|
||
|
|
EXPECT_EQ(a.begin() + static_cast<std::ptrdiff_t>(n), a.end());
|
||
|
|
EXPECT_EQ(5 + a.begin(), a.begin() + 5);
|
||
|
|
auto back = a.begin() + static_cast<std::ptrdiff_t>(std::min<std::size_t>(n, 64));
|
||
|
|
const auto dest = static_cast<std::ptrdiff_t>(std::min<std::size_t>(n, 64)) - 3;
|
||
|
|
if (dest >= 0)
|
||
|
|
{
|
||
|
|
back += -3;
|
||
|
|
EXPECT_EQ(back, a.begin() + dest);
|
||
|
|
EXPECT_EQ(static_cast<bool>(*back), static_cast<bool>(a[static_cast<std::size_t>(dest)]));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
constexpr auto kLiteral = 10101001_bitstring;
|
||
|
|
static_assert(kLiteral.size() == 8);
|
||
|
|
static_assert(kLiteral == dpf::bitstring<8>(0b10101001));
|
||
|
|
static_assert(dpf::utils::quotient_ceiling<std::size_t>(0, 64) == 0);
|
||
|
|
static_assert(dpf::utils::quotient_ceiling<std::size_t>(1, 64) == 1);
|
||
|
|
static_assert(dpf::utils::quotient_ceiling<std::size_t>(64, 64) == 1);
|
||
|
|
static_assert(dpf::utils::quotient_ceiling<std::size_t>(65, 64) == 2);
|
||
|
|
|
||
|
|
TEST(BitArray, QuotientCeilingZero)
|
||
|
|
{
|
||
|
|
EXPECT_EQ(dpf::utils::quotient_ceiling<std::size_t>(0, 8), 0u);
|
||
|
|
EXPECT_EQ(dpf::utils::quotient_ceiling<std::size_t>(9, 8), 2u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitArray, StaticPayloadIsZero)
|
||
|
|
{
|
||
|
|
dpf::static_bit_array<80> a;
|
||
|
|
EXPECT_EQ(a.data()[0], 0u);
|
||
|
|
EXPECT_EQ(a.data()[1], 0u);
|
||
|
|
EXPECT_EQ(a.count(), 0u);
|
||
|
|
EXPECT_FALSE(a.any());
|
||
|
|
EXPECT_TRUE(a.none());
|
||
|
|
EXPECT_FALSE(a.all());
|
||
|
|
|
||
|
|
dpf::static_bit_array<80> seeded(0x11);
|
||
|
|
EXPECT_EQ(seeded.data()[0], 0x11u);
|
||
|
|
EXPECT_EQ(seeded.data()[1], 0u);
|
||
|
|
EXPECT_EQ(seeded.count(), 2u);
|
||
|
|
|
||
|
|
dpf::static_bit_array<80> wide_val(0x1FFFF);
|
||
|
|
EXPECT_EQ(wide_val.data()[0], 0x1FFFFu);
|
||
|
|
EXPECT_EQ(wide_val.data()[1], 0u);
|
||
|
|
EXPECT_EQ(wide_val.count(), 17u);
|
||
|
|
|
||
|
|
dpf::static_bit_array<4> narrow(0xFF);
|
||
|
|
EXPECT_EQ(narrow.count(), 4u);
|
||
|
|
EXPECT_TRUE(narrow.all());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitArray, DynamicPayloadIsZeroAndEmptyIsEmpty)
|
||
|
|
{
|
||
|
|
dpf::dynamic_bit_array<> a(130);
|
||
|
|
ASSERT_NE(a.data(), nullptr);
|
||
|
|
for (std::size_t i = 0; i < a.data_length(); ++i)
|
||
|
|
EXPECT_EQ(a.data(i), 0u);
|
||
|
|
|
||
|
|
dpf::dynamic_bit_array<> empty(0);
|
||
|
|
EXPECT_EQ(empty.size(), 0u);
|
||
|
|
EXPECT_EQ(empty.data_length(), 0u);
|
||
|
|
EXPECT_TRUE(empty.all());
|
||
|
|
EXPECT_FALSE(empty.any());
|
||
|
|
EXPECT_EQ(empty.count(), 0u);
|
||
|
|
EXPECT_EQ(empty.parity(), 0u);
|
||
|
|
EXPECT_EQ(empty.begin(), empty.end());
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitArray, DynamicCopyAndMove)
|
||
|
|
{
|
||
|
|
dpf::dynamic_bit_array<> a(70);
|
||
|
|
a.set(3, true);
|
||
|
|
a.set(69, true);
|
||
|
|
|
||
|
|
dpf::dynamic_bit_array<> b = a;
|
||
|
|
b.set(3, false);
|
||
|
|
EXPECT_TRUE(static_cast<bool>(a[3]));
|
||
|
|
EXPECT_FALSE(static_cast<bool>(b[3]));
|
||
|
|
EXPECT_TRUE(static_cast<bool>(b[69]));
|
||
|
|
|
||
|
|
dpf::dynamic_bit_array<> c(8);
|
||
|
|
c = a;
|
||
|
|
EXPECT_TRUE(static_cast<bool>(c[69]));
|
||
|
|
EXPECT_EQ(c.size(), 70u);
|
||
|
|
|
||
|
|
auto moved = std::move(a);
|
||
|
|
EXPECT_EQ(a.size(), 0u);
|
||
|
|
EXPECT_EQ(a.data(), nullptr);
|
||
|
|
EXPECT_TRUE(static_cast<bool>(moved[3]));
|
||
|
|
EXPECT_TRUE(static_cast<bool>(moved[69]));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitArray, TailBitsAreIgnored)
|
||
|
|
{
|
||
|
|
exercise(dpf::dynamic_bit_array<>(1));
|
||
|
|
exercise(dpf::dynamic_bit_array<>(7));
|
||
|
|
exercise(dpf::dynamic_bit_array<>(8));
|
||
|
|
exercise(dpf::dynamic_bit_array<>(9));
|
||
|
|
exercise(dpf::dynamic_bit_array<>(63));
|
||
|
|
exercise(dpf::dynamic_bit_array<>(64));
|
||
|
|
exercise(dpf::dynamic_bit_array<>(65));
|
||
|
|
exercise(dpf::dynamic_bit_array<>(130));
|
||
|
|
exercise(dpf::static_bit_array<1>{});
|
||
|
|
exercise(dpf::static_bit_array<10>{});
|
||
|
|
exercise(dpf::static_bit_array<64>{});
|
||
|
|
exercise(dpf::static_bit_array<70>{});
|
||
|
|
exercise(dpf::static_bit_array<40, std::uint8_t>{});
|
||
|
|
exercise(dpf::static_bit_array<400, std::uint8_t>{});
|
||
|
|
exercise(dpf::static_bit_array<20, std::uint16_t>{});
|
||
|
|
exercise(dpf::bitstring<1>{});
|
||
|
|
exercise(dpf::bitstring<9>{});
|
||
|
|
exercise(dpf::bitstring<10>{});
|
||
|
|
exercise(dpf::bitstring<64>{});
|
||
|
|
exercise(dpf::bitstring<65>{});
|
||
|
|
exercise(dpf::bitstring<70, std::uint8_t>{});
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitArray, SameWordRangeDoesNotRunOff)
|
||
|
|
{
|
||
|
|
dpf::dynamic_bit_array<> a(32);
|
||
|
|
a.unset();
|
||
|
|
a.set(0, true);
|
||
|
|
a.set(10, true);
|
||
|
|
EXPECT_FALSE(a.any(a.begin() + 1, a.begin() + 10));
|
||
|
|
EXPECT_TRUE(a.any(a.begin(), a.begin() + 1));
|
||
|
|
EXPECT_EQ(a.count(a.begin(), a.begin() + 11), 2u);
|
||
|
|
EXPECT_FALSE(a.all(a.begin(), a.begin() + 11));
|
||
|
|
EXPECT_TRUE(a.all(a.begin() + 3, a.begin() + 3));
|
||
|
|
EXPECT_FALSE(a.any(a.begin() + 3, a.begin() + 3));
|
||
|
|
EXPECT_EQ(a.count(a.begin() + 3, a.begin() + 3), 0u);
|
||
|
|
EXPECT_EQ(a.parity(a.begin() + 3, a.begin() + 3), 0u);
|
||
|
|
|
||
|
|
a.set();
|
||
|
|
EXPECT_TRUE(a.all(a.begin() + 2, a.begin() + 9));
|
||
|
|
EXPECT_EQ(a.count(a.begin() + 2, a.begin() + 9), 7u);
|
||
|
|
EXPECT_EQ(a.parity(a.begin() + 2, a.begin() + 9), 1u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitArray, CrossWordRangeAndFullBitstring)
|
||
|
|
{
|
||
|
|
dpf::dynamic_bit_array<> a(130);
|
||
|
|
a.unset();
|
||
|
|
a.set(63, true);
|
||
|
|
a.set(64, true);
|
||
|
|
a.set(100, true);
|
||
|
|
EXPECT_EQ(a.count(a.begin() + 60, a.begin() + 70), 2u);
|
||
|
|
EXPECT_TRUE(a.any(a.begin() + 60, a.begin() + 70));
|
||
|
|
EXPECT_FALSE(a.all(a.begin() + 60, a.begin() + 70));
|
||
|
|
EXPECT_EQ(a.parity(a.begin() + 60, a.begin() + 70), 0u);
|
||
|
|
|
||
|
|
dpf::bitstring<64> aligned;
|
||
|
|
aligned.set(1, true);
|
||
|
|
EXPECT_TRUE(aligned.any(aligned.begin(), aligned.end()));
|
||
|
|
EXPECT_EQ(aligned.count(aligned.begin(), aligned.end()), 1u);
|
||
|
|
EXPECT_FALSE(aligned.all(aligned.begin(), aligned.end()));
|
||
|
|
aligned.set();
|
||
|
|
EXPECT_TRUE(aligned.all(aligned.begin(), aligned.end()));
|
||
|
|
EXPECT_EQ(aligned.count(aligned.begin(), aligned.end()), 64u);
|
||
|
|
|
||
|
|
dpf::bitstring<128> wide;
|
||
|
|
wide.set();
|
||
|
|
EXPECT_TRUE(wide.all(wide.begin(), wide.end()));
|
||
|
|
EXPECT_EQ(wide.count(wide.begin(), wide.end()), 128u);
|
||
|
|
EXPECT_EQ(wide.parity(wide.begin(), wide.end()), 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitArray, NarrowWordCountCompilesAndFits)
|
||
|
|
{
|
||
|
|
dpf::static_bit_array<400, std::uint8_t> wide;
|
||
|
|
wide.set();
|
||
|
|
EXPECT_EQ(wide.count(), 400u);
|
||
|
|
EXPECT_TRUE(wide.all());
|
||
|
|
EXPECT_EQ(wide.parity(), 0u);
|
||
|
|
|
||
|
|
dpf::bitstring<10> partial;
|
||
|
|
partial.set(0, true);
|
||
|
|
partial.set(3, true);
|
||
|
|
EXPECT_EQ(partial.count(partial.begin(), partial.begin() + 4), 2u);
|
||
|
|
EXPECT_EQ(partial.count(partial.begin(), partial.end()), 2u);
|
||
|
|
partial.set();
|
||
|
|
EXPECT_EQ(partial.count(), 10u);
|
||
|
|
EXPECT_EQ(partial.count(partial.begin(), partial.end()), 10u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitString, SetMatchesFlipAndIgnoresPadding)
|
||
|
|
{
|
||
|
|
dpf::bitstring<65> set_bits;
|
||
|
|
set_bits.set();
|
||
|
|
dpf::bitstring<65> flipped = ~dpf::bitstring<65>{};
|
||
|
|
EXPECT_EQ(set_bits, flipped);
|
||
|
|
EXPECT_EQ(set_bits.count(), 65u);
|
||
|
|
EXPECT_EQ(flipped.count(), 65u);
|
||
|
|
EXPECT_EQ(set_bits.parity(), 1u);
|
||
|
|
|
||
|
|
dpf::bitstring<10> dirty;
|
||
|
|
using word = std::remove_reference_t<decltype(dirty.data(0))>;
|
||
|
|
dirty.data(0) = static_cast<word>(word{1} << 10);
|
||
|
|
dpf::bitstring<10> clean;
|
||
|
|
EXPECT_EQ(dirty, clean);
|
||
|
|
EXPECT_FALSE(dirty.any());
|
||
|
|
EXPECT_TRUE(dirty.none());
|
||
|
|
EXPECT_EQ(dirty.count(), 0u);
|
||
|
|
EXPECT_FALSE(dirty.all());
|
||
|
|
|
||
|
|
const auto clz = dpf::utils::countl_zero_symmetric_difference<dpf::bitstring<10>>{};
|
||
|
|
EXPECT_EQ(clz(dirty, clean), 10u);
|
||
|
|
dpf::bitstring<10> ones = ~dpf::bitstring<10>{};
|
||
|
|
dpf::bitstring<10> also_ones;
|
||
|
|
also_ones.set();
|
||
|
|
EXPECT_EQ(clz(also_ones, ones), 10u);
|
||
|
|
|
||
|
|
dpf::bitstring<1> one_set;
|
||
|
|
one_set.set();
|
||
|
|
EXPECT_EQ(one_set.count(), 1u);
|
||
|
|
EXPECT_EQ(dpf::utils::countl_zero_symmetric_difference<dpf::bitstring<1>>{}(
|
||
|
|
one_set, ~dpf::bitstring<1>{}), 1u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitString, OrderingXorAndIncrement)
|
||
|
|
{
|
||
|
|
dpf::bitstring<12> lo(1);
|
||
|
|
dpf::bitstring<12> hi;
|
||
|
|
hi.set(11, true);
|
||
|
|
EXPECT_LT(lo, hi);
|
||
|
|
EXPECT_GT(hi, lo);
|
||
|
|
EXPECT_NE(lo, hi);
|
||
|
|
EXPECT_EQ(lo + dpf::bitstring<12>(1), dpf::bitstring<12>{});
|
||
|
|
auto inc = lo;
|
||
|
|
++inc;
|
||
|
|
EXPECT_EQ(inc, dpf::bitstring<12>(2));
|
||
|
|
|
||
|
|
dpf::bitstring<80> wrapped = ~dpf::bitstring<80>{};
|
||
|
|
++wrapped;
|
||
|
|
EXPECT_EQ(wrapped, dpf::bitstring<80>{});
|
||
|
|
--wrapped;
|
||
|
|
EXPECT_EQ(wrapped, ~dpf::bitstring<80>{});
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitString, MaskShiftPastZeroDoesNotIndex)
|
||
|
|
{
|
||
|
|
dpf::bitstring<8> b(1);
|
||
|
|
auto mask = dpf::utils::msb_of_v<dpf::bitstring<8>>;
|
||
|
|
mask >>= 8;
|
||
|
|
EXPECT_FALSE(static_cast<bool>(mask));
|
||
|
|
EXPECT_FALSE(mask & b);
|
||
|
|
EXPECT_FALSE(b & mask);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitString, ModPow2WideShiftIsDefined)
|
||
|
|
{
|
||
|
|
dpf::bitstring<80> b;
|
||
|
|
b.set(3, true);
|
||
|
|
b.set(70, true);
|
||
|
|
const auto mod = dpf::utils::mod_pow_2<dpf::bitstring<80>>{};
|
||
|
|
EXPECT_EQ(mod(b, 4), 8u);
|
||
|
|
EXPECT_EQ(mod(b, 70), 8u);
|
||
|
|
EXPECT_EQ(mod(b, 0), 0u);
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitArray, IteratorArithmeticAndAlgorithms)
|
||
|
|
{
|
||
|
|
using iter = dpf::dynamic_bit_array<>::iterator;
|
||
|
|
EXPECT_TRUE(std::is_default_constructible_v<iter>);
|
||
|
|
iter singular_a, singular_b;
|
||
|
|
EXPECT_EQ(singular_a, singular_b);
|
||
|
|
|
||
|
|
dpf::static_bit_array<40, std::uint8_t> narrow;
|
||
|
|
narrow.unset();
|
||
|
|
narrow.set(7, true);
|
||
|
|
auto it = narrow.begin() + 8;
|
||
|
|
it += -1;
|
||
|
|
EXPECT_EQ(it, narrow.begin() + 7);
|
||
|
|
EXPECT_TRUE(static_cast<bool>(*it));
|
||
|
|
EXPECT_EQ(narrow.end() - static_cast<std::ptrdiff_t>(narrow.size()), narrow.begin());
|
||
|
|
|
||
|
|
dpf::static_bit_array<16> bits;
|
||
|
|
bits.unset();
|
||
|
|
for (std::size_t i = 0; i < 16; ++i) bits.set(i, i < 4);
|
||
|
|
using std::swap;
|
||
|
|
swap(bits[0], bits[15]);
|
||
|
|
EXPECT_FALSE(static_cast<bool>(bits[0]));
|
||
|
|
EXPECT_TRUE(static_cast<bool>(bits[15]));
|
||
|
|
std::reverse(bits.begin(), bits.end());
|
||
|
|
EXPECT_TRUE(static_cast<bool>(bits[0]));
|
||
|
|
EXPECT_FALSE(static_cast<bool>(bits[15]));
|
||
|
|
}
|
||
|
|
|
||
|
|
TEST(BitString, LiteralMatchesIntegerCtor)
|
||
|
|
{
|
||
|
|
EXPECT_EQ(kLiteral, dpf::bitstring<8>(0b10101001));
|
||
|
|
EXPECT_EQ(kLiteral.to_string(), "10101001");
|
||
|
|
}
|