#include #include "dpf.hpp" #include "grotto/fixedpoint.hpp" #include "grotto/fixedpoint_mul.hpp" #include "grotto/principal_lut.hpp" #include #include #include #include #include #include namespace { template void expect_same(const T & got, const T & point) { EXPECT_EQ(std::memcmp(&got, &point, sizeof(T)), 0); } template void expect_wrapping_interval(In from, In to, In alpha, Out y, std::size_t leaves) { auto [k0, k1] = dpf::make_dpf(alpha, y); using key_t = std::decay_t; EXPECT_EQ((dpf::utils::get_nodes_in_interval(from, to)), leaves); auto [buf0, it0] = dpf::eval_interval(k0, from, to); auto [buf1, it1] = dpf::eval_interval(k1, from, to); auto a = std::begin(it0); auto b = std::begin(it1); const auto a_end = std::end(it0); const auto b_end = std::end(it1); std::size_t n = 0; In cur = from; for (;;) { ASSERT_NE(a, a_end); ASSERT_NE(b, b_end); auto p0 = *dpf::eval_point(k0, cur); auto p1 = *dpf::eval_point(k1, cur); expect_same(*a, p0); expect_same(*b, p1); ++a; ++b; ++n; if (cur == to) break; cur = static_cast(static_cast(cur) + 1u); ASSERT_LT(n, std::size_t{1} << 20); } EXPECT_EQ(a, a_end); EXPECT_EQ(b, b_end); const std::uint64_t width = std::uint64_t{1} << dpf::utils::bitlength_of_v; const std::uint64_t masked = (static_cast(to) - static_cast(from)) & (width - 1); EXPECT_EQ(n, masked + 1); } } // namespace TEST(CornerGaps, SameLeafWrapUint8Uint32) { expect_wrapping_interval(10, 9, 40, 0x11111111u, 65); } TEST(CornerGaps, AdjacentLeafWrapUint8Uint32) { expect_wrapping_interval(8, 7, 40, 0x22222222u, 64); } TEST(CornerGaps, LongWrapStillMatchesPoint) { expect_wrapping_interval(200, 10, 3, 0x33333333u, 17); auto [k0, k1] = dpf::make_dpf(uint8_t{3}, uint32_t{0x33333333u}); using key_t = std::decay_t; auto memo0 = dpf::make_full_tree_interval_memoizer(uint8_t{200}, uint8_t{10}); auto memo1 = dpf::make_full_tree_interval_memoizer(uint8_t{200}, uint8_t{10}); auto [buf0, it0] = dpf::eval_interval(k0, uint8_t{200}, uint8_t{10}, std::move(memo0)); auto [buf1, it1] = dpf::eval_interval(k1, uint8_t{200}, uint8_t{10}, std::move(memo1)); auto p0 = *dpf::eval_point(k0, uint8_t{200}); auto p1 = *dpf::eval_point(k1, uint8_t{200}); expect_same(*std::begin(it0), p0); expect_same(*std::begin(it1), p1); EXPECT_EQ(dpf::reconstruct(*std::begin(it0), *std::begin(it1)), 0u); auto back0 = std::begin(it0); auto back1 = std::begin(it1); for (int i = 0; i < 66; ++i, ++back0, ++back1) {} auto q0 = *dpf::eval_point(k0, uint8_t{10}); auto q1 = *dpf::eval_point(k1, uint8_t{10}); expect_same(*back0, q0); expect_same(*back1, q1); } TEST(CornerGaps, OneOutputPerLeafWrap) { expect_wrapping_interval(5, 4, 9, simde_uint128{7}, 65536); } TEST(CornerGaps, SaturatedUint64LeafCount) { using in_t = uint64_t; using out_t = simde_uint128; using key_t = dpf::utils::dpf_type_t; EXPECT_EQ((dpf::utils::get_nodes_in_interval(in_t{1}, ~in_t{0})), std::numeric_limits::max()); EXPECT_THROW((dpf::utils::get_nodes_in_interval(in_t{0}, ~in_t{0})), std::length_error); EXPECT_THROW((dpf::utils::get_nodes_in_interval(in_t{5}, in_t{4})), std::length_error); } TEST(CornerGaps, MemoizerRejectsALargerInterval) { auto [k0, k1] = dpf::make_dpf(uint8_t{4}, uint32_t{1}); using key_t = std::decay_t; auto memo = dpf::make_basic_interval_memoizer(uint8_t{0}, uint8_t{10}); auto buf = dpf::make_output_buffer_for_interval(uint8_t{0}, uint8_t{100}); EXPECT_THROW(dpf::eval_interval(k0, uint8_t{0}, uint8_t{100}, buf, memo), std::length_error); (void)k1; } TEST(CornerGaps, OddStartInteriorTail) { auto [k0, k1] = dpf::make_dpf(uint16_t{3}, simde_uint128{11}); for (uint16_t to : {uint16_t{9}, uint16_t{10}}) { auto [b0, it0] = dpf::eval_interval(k0, uint16_t{1}, to); auto [b1, it1] = dpf::eval_interval(k1, uint16_t{1}, to); auto a = std::begin(it0); auto b = std::begin(it1); for (uint16_t q = 1; q <= to; ++q, ++a, ++b) { auto p0 = *dpf::eval_point(k0, q); auto p1 = *dpf::eval_point(k1, q); expect_same(*a, p0); expect_same(*b, p1); } EXPECT_EQ(a, std::end(it0)); EXPECT_EQ(b, std::end(it1)); } } TEST(CornerGaps, PathMemoizerExtremes) { auto [k0, k1] = dpf::make_dpf(uint16_t{0x0102}, uint16_t{9}); using key_t = std::decay_t; dpf::basic_path_memoizer m0; dpf::basic_path_memoizer m1; const uint16_t queries[] = {0, 0x8000, 1, 0}; for (uint16_t q : queries) { auto a = *dpf::eval_point(k0, q, m0); auto b = *dpf::eval_point(k1, q, m1); auto fa = *dpf::eval_point(k0, q); auto fb = *dpf::eval_point(k1, q); EXPECT_EQ(a, fa) << q; EXPECT_EQ(b, fb) << q; } } TEST(CornerGaps, DepthOneBitInterval) { for (uint8_t alpha : {uint8_t{0}, uint8_t{127}, uint8_t{128}, uint8_t{255}}) { auto [k0, k1] = dpf::make_dpf(alpha, dpf::bit::one); using key_t = std::decay_t; EXPECT_EQ(key_t::depth, 1u); auto check = [&](uint8_t from, uint8_t to) { auto [b0, it0] = dpf::eval_interval(k0, from, to); auto [b1, it1] = dpf::eval_interval(k1, from, to); auto a = std::begin(it0); auto b = std::begin(it1); for (uint8_t q = from; ; ) { const bool on = q == alpha; const bool bit = static_cast(*a) != static_cast(*b); EXPECT_EQ(bit, on) << int(q); ++a; ++b; if (q == to) break; ++q; } EXPECT_EQ(a, std::end(it0)); EXPECT_EQ(std::end(it1), b); }; check(alpha, alpha); check(127, 128); } } TEST(CornerGaps, EmptyAndDuplicateSequence) { auto [k0, k1] = dpf::make_dpf(uint8_t{40}, uint8_t{7}); std::vector empty; auto [eb0, eit0] = dpf::eval_sequence(k0, empty.begin(), empty.end()); auto [eb1, eit1] = dpf::eval_sequence(k1, empty.begin(), empty.end()); EXPECT_EQ(std::begin(eit0), std::end(eit0)); EXPECT_EQ(std::begin(eit1), std::end(eit1)); const std::vector seq{40, 40, 41}; auto [b0, it0] = dpf::eval_sequence(k0, seq.begin(), seq.end()); auto [b1, it1] = dpf::eval_sequence(k1, seq.begin(), seq.end()); auto a = std::begin(it0); auto b = std::begin(it1); const uint8_t want[] = {7, 7, 0}; for (int i = 0; i < 3; ++i, ++a, ++b) EXPECT_EQ(dpf::reconstruct(*a, *b), want[i]) << i; EXPECT_EQ(a, std::end(it0)); } TEST(CornerGaps, IncrementalAdjacentLaneWrap) { const uint16_t alpha = 0x00ab; auto [k0, k1] = dpf::make_dpf(alpha, dpf::at<8>(uint32_t{0xabcdu})); auto [b0, it0] = dpf::eval_interval(dpf::out<0, 8>, k0, uint8_t{8}, uint8_t{7}); auto [b1, it1] = dpf::eval_interval(dpf::out<0, 8>, k1, uint8_t{8}, uint8_t{7}); auto a = std::begin(it0); auto b = std::begin(it1); std::size_t n = 0; for (int lane = 8; ; ) { const uint16_t query = static_cast(static_cast(lane) << 8); auto p0 = *dpf::eval_point(dpf::out<0, 8>, k0, query); auto p1 = *dpf::eval_point(dpf::out<0, 8>, k1, query); EXPECT_EQ(*a, p0) << lane; EXPECT_EQ(*b, p1) << lane; ++a; ++b; ++n; if (lane == 7) break; lane = (lane + 1) & 255; } EXPECT_EQ(n, 256u); EXPECT_EQ(a, std::end(it0)); } TEST(CornerGaps, ModintWideLiteralAndLimbShift) { using namespace dpf::literals; EXPECT_EQ(1_u129, dpf::modint<129>{1}); const uint256_t bit128{1, 0}; const auto wide = 340282366920938463463374607431768211456_u129; EXPECT_EQ(wide, (dpf::modint<129>{bit128})); const bool shift10 = (dpf::modint<10>{1} << 10) == dpf::modint<10>{0}; const bool shift16 = (dpf::modint<10>{1} << 16) == dpf::modint<10>{0}; const bool shift64 = (dpf::modint<64>{1} << 64) == dpf::modint<64>{0}; const bool shift128 = (dpf::modint<65>{1} << 128) == dpf::modint<65>{0}; const bool rshift10 = (dpf::modint<10>{5} >> 10) == dpf::modint<10>{0}; const bool rshift64 = (dpf::modint<64>{1} >> 64) == dpf::modint<64>{0}; EXPECT_TRUE(shift10 && shift16 && shift64 && shift128 && rshift10 && rshift64); dpf::modint<10> assigned{1}; assigned <<= 16; EXPECT_TRUE(assigned == dpf::modint<10>{0}); assigned = dpf::modint<10>{7}; assigned >>= 10; EXPECT_TRUE(assigned == dpf::modint<10>{0}); } TEST(CornerGaps, SetbitEmptyAndSingleAndNarrowLeaf) { dpf::dynamic_bit_array<> zeros(128); using iter_t = decltype(zeros.begin()); dpf::subinterval_iterable all(zeros.begin(), zeros.size(), 0, zeros.size() - 1, 0, 0); auto none = dpf::indices_set_in(all); EXPECT_EQ(none.begin(), none.end()); zeros[0] = true; dpf::subinterval_iterable one(zeros.begin(), zeros.size(), 0, zeros.size() - 1, 0, 0); auto set = dpf::indices_set_in(one); auto it = set.begin(); ASSERT_NE(it, set.end()); EXPECT_EQ(*it, 0u); ++it; EXPECT_EQ(it, set.end()); dpf::dynamic_bit_array<> narrow(128); narrow[0] = true; dpf::subinterval_iterable clipped(narrow.begin(), narrow.size(), 0, 1, 0, 2); auto clipped_set = dpf::indices_set_in(clipped); auto cit = clipped_set.begin(); ASSERT_NE(cit, clipped_set.end()); EXPECT_EQ(*cit, 0u); ++cit; EXPECT_EQ(cit, clipped_set.end()); } TEST(CornerGaps, EmptyRotationIsEmptyAndZeroIsIdentity) { std::vector empty; dpf::rotation_iterable::iterator> none( empty.begin(), empty.end(), 1); EXPECT_EQ(none.begin(), none.end()); std::vector values{1, 2, 3}; dpf::rotation_iterable::iterator> id( values.begin(), values.end(), 0); std::vector got; for (auto it = id.begin(); it != id.end(); ++it) got.push_back(*it); EXPECT_EQ(got, values); dpf::rotation_iterable::iterator> rot( values.begin(), values.end(), 1); got.clear(); for (auto it = rot.begin(); it != rot.end(); ++it) got.push_back(*it); EXPECT_EQ(got, (std::vector{2, 3, 1})); } TEST(CornerGaps, Party1NegatesSignedMinimum) { using sub = dpf::subtractive_share; using add0 = dpf::additive_share; const auto raw = std::numeric_limits::min(); const auto party1 = sub::from_raw(raw).as_additive(); EXPECT_EQ(party1.raw(), raw); EXPECT_EQ(dpf::reconstruct(add0::from_raw(0), party1), raw); EXPECT_EQ((-sub::from_raw(raw)).raw(), raw); } TEST(CornerGaps, FixedMulFloorsAndPrecisionCastDiffersFromLogicalShift) { using q4 = grotto::fixedpoint<4, std::int32_t>; const auto prod = grotto::fixed_mul<8, 4>(q4::from_raw(-3), q4::from_raw(1)); EXPECT_EQ(prod.integral_representation(), -1); const auto neg_pair = grotto::fixed_mul<8, 4>(q4::from_raw(-3), q4::from_raw(-2)); EXPECT_EQ(neg_pair.integral_representation(), 0); auto quarter = q4::from_raw(-12); const auto casted = grotto::precision_cast<0>(quarter); EXPECT_EQ(casted.integral_representation(), -1); quarter >>= 4; EXPECT_EQ(quarter.integral_representation(), 268435455); } TEST(CornerGaps, Int64MinFactorIsDefined) { using grotto::principal_detail::w_from_i128; using grotto::principal_detail::w_mul_i64; using grotto::principal_detail::w_mul_u64; using grotto::principal_detail::w_neg; const auto value = w_from_i128(3); const auto got = w_mul_i64(value, std::numeric_limits::min()); const auto want = w_neg(w_mul_u64(value, std::uint64_t{1} << 63)); EXPECT_EQ(got.lo, want.lo); EXPECT_EQ(got.hi, want.hi); } TEST(CornerGaps, PrgRejectsUint32Seam) { alignas(64) simde__m128i seed = simde_mm_set_epi64x(1, 2); alignas(64) simde__m128i out[4]; const auto pos = static_cast(UINT32_MAX - 1u); EXPECT_THROW(dpf::prg::aes128::eval(seed, out, 4, pos), std::invalid_argument); EXPECT_THROW(dpf::prg::lowmc128::eval(seed, out, 4, pos), std::invalid_argument); const auto ok = static_cast(UINT32_MAX - 3u); dpf::prg::aes128::eval(seed, out, 4, ok); for (psnip_uint32_t i = 0; i < 4; ++i) { const auto one = dpf::prg::aes128::eval(seed, ok + i); EXPECT_EQ(std::memcmp(&out[i], &one, sizeof(one)), 0) << i; } dpf::prg::lowmc128::eval(seed, out, 4, ok); for (psnip_uint32_t i = 0; i < 4; ++i) { const auto one = dpf::prg::lowmc128::eval(seed, ok + i); EXPECT_EQ(std::memcmp(&out[i], &one, sizeof(one)), 0) << i; } HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") EXPECT_THROW((dpf::randomness::detail::lane_codec::fill( seed, static_cast(UINT32_MAX) - 1u, out, 4)), std::invalid_argument); HEDLEY_PRAGMA(GCC diagnostic pop) }