#include #include #include #include #include #include "dpf/bit_array.hpp" #include "dpf/bitstring.hpp" using namespace dpf::literals; namespace { template 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(a[i]) ? 1u : 0u; return n; } template 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(a[i]) ? 1 : 0; return p; } template 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(a[i])) return false; return true; } template 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(a[i])) return true; return false; } template 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(a.any()), static_cast(ref_any(a, 0, n))); EXPECT_EQ(static_cast(a.none()), static_cast(!ref_any(a, 0, n))); EXPECT_EQ(static_cast(a.all()), static_cast(ref_all(a, 0, n))); EXPECT_EQ(a.parity(), static_cast(ref_parity(a, 0, n))); const std::size_t cuts[][2] = { {0, 0}, {0, n}, {0, std::min(n, 1)}, {0, std::min(n, 3)}, {std::min(n, 2), std::min(n, 10)}, {n > 60 ? n - 60 : 0, n}, {n > 70 ? std::size_t{60} : std::size_t{0}, std::min(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(c[0]); auto last = a.begin() + static_cast(c[1]); EXPECT_EQ(a.count(first, last), ref_count(a, c[0], c[1])); EXPECT_EQ(static_cast(a.any(first, last)), static_cast(ref_any(a, c[0], c[1]))); EXPECT_EQ(static_cast(a.all(first, last)), static_cast(ref_all(a, c[0], c[1]))); EXPECT_EQ(static_cast(a.none(first, last)), static_cast(!ref_any(a, c[0], c[1]))); EXPECT_EQ(a.parity(first, last), static_cast(ref_parity(a, c[0], c[1]))); } } template 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(n), a.begin()); EXPECT_EQ(a.begin() + static_cast(n), a.end()); EXPECT_EQ(5 + a.begin(), a.begin() + 5); auto back = a.begin() + static_cast(std::min(n, 64)); const auto dest = static_cast(std::min(n, 64)) - 3; if (dest >= 0) { back += -3; EXPECT_EQ(back, a.begin() + dest); EXPECT_EQ(static_cast(*back), static_cast(a[static_cast(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(0, 64) == 0); static_assert(dpf::utils::quotient_ceiling(1, 64) == 1); static_assert(dpf::utils::quotient_ceiling(64, 64) == 1); static_assert(dpf::utils::quotient_ceiling(65, 64) == 2); TEST(BitArray, QuotientCeilingZero) { EXPECT_EQ(dpf::utils::quotient_ceiling(0, 8), 0u); EXPECT_EQ(dpf::utils::quotient_ceiling(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(a[3])); EXPECT_FALSE(static_cast(b[3])); EXPECT_TRUE(static_cast(b[69])); dpf::dynamic_bit_array<> c(8); c = a; EXPECT_TRUE(static_cast(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(moved[3])); EXPECT_TRUE(static_cast(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; dirty.data(0) = static_cast(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>{}; 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>{}( 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>; mask >>= 8; EXPECT_FALSE(static_cast(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>{}; 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 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(*it)); EXPECT_EQ(narrow.end() - static_cast(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(bits[0])); EXPECT_TRUE(static_cast(bits[15])); std::reverse(bits.begin(), bits.end()); EXPECT_TRUE(static_cast(bits[0])); EXPECT_FALSE(static_cast(bits[15])); } TEST(BitString, LiteralMatchesIntegerCtor) { EXPECT_EQ(kLiteral, dpf::bitstring<8>(0b10101001)); EXPECT_EQ(kLiteral.to_string(), "10101001"); }