// Stress scenarios that exercise BOTH classic-shaped and multilevel/cmp DPF // keys through the unified eval surface. The intent is to give the classic // (single-level, equal-width) and multilevel (per-slot `at` + `cmp`) code // paths symmetric coverage: everything the multilevel surface can do is also // checked on classic keys, and every classic strength (Doerner-Shelat parity, // inner product, memoizer variants, recipe modes) is checked on multilevel // keys where it applies. // // Patterns (recon xor-vs-additive, DS tape/roots harness) mirror // incremental_test.cpp. #include #include "dpf.hpp" #include "grotto/fixedpoint.hpp" #include #include #include #include #include #include #include namespace { // --------------------------------------------------------------------------- // Deterministic randomness harness (roots + pad tape), copied from // incremental_test.cpp so dealer <-> Doerner-Shelat byte-identity holds. // --------------------------------------------------------------------------- simde__m128i g_roots[16]; int g_ri = 0; simde__m128i take_root() { return g_roots[g_ri++]; } std::vector g_tape(1 << 18); std::size_t g_ti = 0; void tape_fill(void * p, std::size_t n) { if (g_ti + n > g_tape.size()) std::abort(); std::memcpy(p, g_tape.data() + g_ti, n); g_ti += n; } struct PadA { uint64_t n = 1; simde__m128i block() { auto v = simde_mm_set_epi64x(static_cast(n), static_cast(n * 9 + 3)); n += 2; return v; } uint8_t bit() { return static_cast(n++ & 1u); } }; void reset_tape_roots() { g_ri = 0; g_ti = 0; } void seed_fixed_rng() { for (int i = 0; i < 16; ++i) g_roots[i] = simde_mm_set_epi64x(0x2222 * (i + 1), 0xBEEF0000u + i * 23); for (std::size_t i = 0; i < g_tape.size(); ++i) g_tape[i] = static_cast(i * 31 + 7); } bool same_bytes(const void * a, const void * b, std::size_t n) { return std::memcmp(a, b, n) == 0; } template constexpr bool is_xor_out_v = std::is_same_v || dpf::utils::is_xor_wrapper_v; template auto recon(const A & a, const B & b) { if constexpr (dpf::is_secret_share_v && dpf::is_secret_share_v) return dpf::reconstruct(a, b); else if constexpr (is_xor_out_v) return static_cast(a ^ b); else return static_cast(a - b); // subtractive: party0 - party1 } /// Flip the low bit of the N-bit MSB prefix (neighbor lane for packed leaves). template InputT flip_lane_lsb(InputT x, std::size_t prefix, std::size_t bitlen) { return static_cast(x ^ (InputT{1} << (bitlen - prefix))); } // --------------------------------------------------------------------------- // Byte-identity helpers (classic + incremental) copied from incremental_test. // --------------------------------------------------------------------------- template bool same_leaf_beaver(const Key & a, const Key & b) { if constexpr (I < Key::num_outputs) { const auto & la = a.template leaf(); const auto & lb = b.template leaf(); if (!same_bytes(&la, &lb, sizeof(la))) return false; const auto & ba = a.template beaver(); const auto & bb = b.template beaver(); using out_t = typename Key::template output_type_t; if constexpr (dpf::is_wildcard_v) { return same_bytes(&ba.output_blind, &bb.output_blind, sizeof(ba.output_blind)) && same_bytes(&ba.vector_blind, &bb.vector_blind, sizeof(ba.vector_blind)) && same_bytes(&ba.blinded_vector, &bb.blinded_vector, sizeof(ba.blinded_vector)) && same_leaf_beaver(a, b); } return same_bytes(&ba, &bb, sizeof(ba)) && same_leaf_beaver(a, b); } return true; } // Byte-for-byte comparison of the comparison (DCF) channel: metadata, // value-CW array, `cw_last`, and this party's `cmp_addend` share. All four // carry the secret comparison payload / threshold, so a dealer key and a // Doerner-Shelat key must agree on every byte here (not merely reconstruct). template bool same_cmp_channel(const Key & a, const Key & b) { if (a.has_cmp() != b.has_cmp()) return false; if (!a.has_cmp()) return true; const auto & ca = a.cmp(); const auto & cb = b.cmp(); if (ca.nbits != cb.nbits || ca.mask != cb.mask || ca.kind != cb.kind || ca.trivial != cb.trivial || ca.eval_as_ge != cb.eval_as_ge || ca.include_eq != cb.include_eq || ca.active != cb.active || ca.incremental != cb.incremental) return false; using word = typename Key::value_cw_word; if (!same_bytes(a.value_cw().data(), b.value_cw().data(), sizeof(word) * a.value_cw().size())) return false; if (a.cw_last() != b.cw_last()) return false; if (a.cmp_addend() != b.cmp_addend()) return false; return true; } // Element-wise byte comparison of the public `if_false` addends (from eq()). // Wildcard-typed elements are not byte-comparable and are skipped. template bool same_public_addends(const Key & a, const Key & b) { if constexpr (I < std::tuple_size_v) { using elem_t = std::tuple_element_t; if constexpr (!dpf::is_wildcard_v) { const auto & ea = std::get(a.public_addends); const auto & eb = std::get(b.public_addends); if (!same_bytes(&ea, &eb, sizeof(ea))) return false; } return same_public_addends(a, b); } return true; } template bool same_incr_key(const Key & a, const Key & b) { return same_bytes(&a.root(), &b.root(), sizeof(a.root())) && same_bytes(a.correction_words().data(), b.correction_words().data(), sizeof(a.correction_words())) && same_bytes(a.correction_advice().data(), b.correction_advice().data(), sizeof(a.correction_advice())) && same_leaf_beaver(a, b) && same_cmp_channel(a, b) && same_public_addends(a, b); } template bool same_leaf_beaver_classic(const Key & a, const Key & b) { if constexpr (I < std::tuple_size_v) { const auto & la = a.template leaf(); const auto & lb = b.template leaf(); if (!same_bytes(&la, &lb, sizeof(la))) return false; const auto & ba = a.template beaver(); const auto & bb = b.template beaver(); using out_t = typename Key::template output_type_t; if constexpr (dpf::is_wildcard_v) { return same_bytes(&ba.output_blind, &bb.output_blind, sizeof(ba.output_blind)) && same_bytes(&ba.vector_blind, &bb.vector_blind, sizeof(ba.vector_blind)) && same_bytes(&ba.blinded_vector, &bb.blinded_vector, sizeof(ba.blinded_vector)) && same_leaf_beaver_classic(a, b); } return same_bytes(&ba, &bb, sizeof(ba)) && same_leaf_beaver_classic(a, b); } return true; } template bool same_classic_key(const Key & a, const Key & b) { if (!same_bytes(&a.root(), &b.root(), sizeof(a.root()))) return false; if (!same_bytes(a.correction_words().data(), b.correction_words().data(), sizeof(a.correction_words()))) return false; if (!same_bytes(a.correction_advice().data(), b.correction_advice().data(), sizeof(a.correction_advice()))) return false; return same_leaf_beaver_classic(a, b); } /// In-process Beaver completion for a wildcard leaf (same messages as asio /// `assign_wildcard_output`, without a socket). template void assign_wildcard_leaf_local(Key0 & k0, Key1 & k1, const ShareT & shr0, const ShareT & shr1) { auto & w0 = std::get(k0.leaf_nodes); auto & w1 = std::get(k1.leaf_nodes); const auto b0 = w0.compute_and_get_blinded_output_share(shr0); const auto b1 = w1.compute_and_get_blinded_output_share(shr1); const auto l0 = w0.compute_and_get_leaf_share(b1); const auto l1 = w1.compute_and_get_leaf_share(b0); w0.reconstruct_correction_word(l1); w1.reconstruct_correction_word(l0); } } // namespace class StressScenariosTest : public ::testing::Test { protected: void SetUp() override { seed_fixed_rng(); dpf::detail::uniform_bytes_hook = tape_fill; reset_tape_roots(); } void TearDown() override { dpf::detail::uniform_bytes_hook = nullptr; } }; // =========================================================================== // A) Multilevel parity with the classic surface. // =========================================================================== TEST_F(StressScenariosTest, MlEvalFullPrefixSlot) { uint16_t x = 0xa5c3; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint8_t{42}), dpf::at<12>(uint16_t{7})); constexpr std::size_t N = 8, I = 0; auto [b0, it0] = dpf::eval_full(dpf::out, k0); auto [b1, it1] = dpf::eval_full(dpf::out, k1); (void)it0; (void)it1; const std::size_t lane = x >> (16 - N); EXPECT_EQ(recon(b0[lane], b1[lane]), recon(*dpf::eval_point(dpf::out, k0, x), *dpf::eval_point(dpf::out, k1, x))); EXPECT_EQ(recon(b0[lane], b1[lane]), uint8_t{42}); // Neighbouring lane in the same node is zero. const std::size_t nb = lane ^ 1u; EXPECT_EQ(recon(b0[nb], b1[nb]), uint8_t{0}); } TEST_F(StressScenariosTest, MlEvalFullDeepestSlot) { uint16_t x = 0x1234; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint8_t{9}), dpf::at<12>(uint16_t{0xbeef})); using KT = std::decay_t; constexpr std::size_t I = KT::deepest_output; static_assert(KT::meta[I].prefix == 12, "deepest prefix expected 12"); constexpr std::size_t N = 12; auto [b0, it0] = dpf::eval_full(dpf::out, k0); auto [b1, it1] = dpf::eval_full(dpf::out, k1); (void)it0; (void)it1; const std::size_t lane = x >> (16 - N); EXPECT_EQ(recon(b0[lane], b1[lane]), recon(*dpf::eval_point(dpf::out, k0, x), *dpf::eval_point(dpf::out, k1, x))); EXPECT_EQ(recon(b0[lane], b1[lane]), uint16_t{0xbeef}); } TEST_F(StressScenariosTest, MlEvalFullOutbufMatchesReturned) { uint16_t x = 0x7788; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint16_t{123}), uint32_t{5}); constexpr std::size_t N = 8, I = 0; auto [b0, it0] = dpf::eval_full(dpf::out, k0); (void)it0; auto memo = dpf::make_basic_interval_memoizer, I>( uint16_t{0}, static_cast((1u << N) - 1)); auto ob0 = dpf::make_output_buffer(dpf::out, k0, uint16_t{0}, static_cast((1u << N) - 1)); dpf::eval_full(dpf::out, k0, ob0, memo); for (std::size_t i = 0; i < (1u << N); ++i) EXPECT_EQ(ob0[i], b0[i]) << "i=" << i; } TEST_F(StressScenariosTest, MlEvalFullCmpSmallDomain) { uint16_t alpha = 0x8000u; auto [k0, k1] = dpf::make_dpf(alpha, dpf::lt_at<8>(uint64_t{5}, uint64_t{1})); ASSERT_TRUE(k0.has_cmp()); const uint64_t mask = k0.cmp().mask; auto b0 = dpf::eval_full(dpf::cmp, k0); auto b1 = dpf::eval_full(dpf::cmp, k1); ASSERT_EQ(b0.size(), std::size_t{256}); for (std::size_t lane : {std::size_t{0x00}, std::size_t{0x7f}, std::size_t{0x80}, std::size_t{0x81}, std::size_t{0xff}}) { const uint16_t q = static_cast(lane << 8); const uint64_t from_full = dpf::reconstruct(dpf::eval_point(dpf::cmp, k0, q), dpf::eval_point(dpf::cmp, k1, q)) & mask; EXPECT_EQ(recon(b0[lane], b1[lane]) & mask, from_full) << "lane=" << lane; } // top8(alpha)=0x80 => lanes below are lt (5), at/above are !lt (1). EXPECT_EQ(recon(b0[0x7f], b1[0x7f]) & mask, 5u); EXPECT_EQ(recon(b0[0x80], b1[0x80]) & mask, 1u); } TEST_F(StressScenariosTest, MlIntervalWithBasicIntervalMemoizer) { uint16_t x = 0x33aa; auto [k0, k1] = dpf::make_dpf(x, dpf::at<12>(uint8_t{77}), uint32_t{9}); using KT = std::decay_t; constexpr std::size_t N = 12, I = 0; const uint16_t lane = x >> (16 - N); const uint16_t from = static_cast(lane & ~uint16_t{0xf}); const uint16_t to = static_cast(from + 0xf); auto memo0 = dpf::make_basic_interval_memoizer(from, to); auto buf0 = dpf::make_output_buffer(dpf::out, k0, from, to); auto buf1 = dpf::make_output_buffer(dpf::out, k1, from, to); dpf::eval_interval(dpf::out, k0, from, to, buf0, memo0); auto memo1 = dpf::make_basic_interval_memoizer(from, to); dpf::eval_interval(dpf::out, k1, from, to, buf1, memo1); const std::size_t idx = static_cast(lane - from); EXPECT_EQ(recon(buf0[idx], buf1[idx]), recon(*dpf::eval_point(dpf::out, k0, x), *dpf::eval_point(dpf::out, k1, x))); EXPECT_EQ(recon(buf0[idx], buf1[idx]), uint8_t{77}); const std::size_t nbi = static_cast((lane ^ 1u) - from); EXPECT_EQ(recon(buf0[nbi], buf1[nbi]), uint8_t{0}); } TEST_F(StressScenariosTest, MlSequencePointLoopVsBreadthFirst) { uint16_t x = 0x5a3c; auto [k0, k1] = dpf::make_dpf(x, dpf::at<10>(uint8_t{21}), uint32_t{99}); constexpr std::size_t N = 10, I = 0; const uint16_t xlane = x >> (16 - N); std::vector lanes = { static_cast(xlane & ~uint16_t{0xf}), xlane, static_cast(xlane ^ 1u), static_cast((xlane + 3u) & 0x3ffu), static_cast((xlane + 100u) & 0x3ffu)}; std::sort(lanes.begin(), lanes.end()); lanes.erase(std::unique(lanes.begin(), lanes.end()), lanes.end()); auto bf0 = dpf::eval_sequence_breadth_first(dpf::out, k0, lanes.begin(), lanes.end()); auto bf1 = dpf::eval_sequence_breadth_first(dpf::out, k1, lanes.begin(), lanes.end()); for (std::size_t i = 0; i < lanes.size(); ++i) { const uint16_t q = static_cast(lanes[i] << (16 - N)); EXPECT_EQ(recon(bf0[i], bf1[i]), recon(*dpf::eval_point(dpf::out, k0, q), *dpf::eval_point(dpf::out, k1, q))) << "i=" << i; } } TEST_F(StressScenariosTest, MlSequenceRecipeDepthAndBreadthFirst) { uint16_t x = 0x2b70; auto [k0, k1] = dpf::make_dpf(x, dpf::at<12>(uint8_t{55}), uint32_t{7}); using KT = std::decay_t; constexpr std::size_t N = 12, I = 0; const uint16_t lane = x >> (16 - N); std::array pts{{ static_cast(lane & ~uint16_t{1}), lane, static_cast((lane & ~uint16_t{1}) + 1u)}}; std::sort(pts.begin(), pts.end()); auto recipe = dpf::make_sequence_recipe(dpf::out, k0, pts.begin(), pts.end()); EXPECT_EQ(recipe.depth(), KT::meta[I].tree_level); EXPECT_EQ(recipe.output_indices().size(), pts.size()); auto b0 = dpf::eval_sequence_breadth_first(dpf::out, k0, pts.begin(), pts.end()); auto b1 = dpf::eval_sequence_breadth_first(dpf::out, k1, pts.begin(), pts.end()); for (std::size_t i = 0; i < pts.size(); ++i) { const uint16_t q = static_cast(pts[i] << (16 - N)); EXPECT_EQ(recon(b0[i], b1[i]), recon(*dpf::eval_point(dpf::out, k0, q), *dpf::eval_point(dpf::out, k1, q))); } } // A canonical mixed XOR+additive multilevel key reused across several tests. namespace { auto make_mixed_ml(uint16_t x) { return dpf::make_dpf(x, dpf::at<8>(uint8_t{3}, dpf::xor_wrapper{0xcafe}), dpf::at<12>(uint32_t{123456}), dpf::lt_at<8>(uint64_t{9})); } } // namespace TEST_F(StressScenariosTest, MlMixedPointRecon) { uint16_t x = 0x9c40; auto [k0, k1] = make_mixed_ml(x); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 8>, k0, x), *dpf::eval_point(dpf::out<0, 8>, k1, x)), uint8_t{3}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1, 8>, k0, x), *dpf::eval_point(dpf::out<1, 8>, k1, x)), dpf::xor_wrapper{0xcafe}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<2, 12>, k0, x), *dpf::eval_point(dpf::out<2, 12>, k1, x)), uint32_t{123456}); // Off-prefix additive slot is zero (flip a bit inside the 12-bit prefix). const uint16_t off = flip_lane_lsb(x, 12, 16); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<2, 12>, k0, off), *dpf::eval_point(dpf::out<2, 12>, k1, off)), uint32_t{0}); } TEST_F(StressScenariosTest, MlMixedIntervalRecon) { uint16_t x = 0x9c40; auto [k0, k1] = make_mixed_ml(x); constexpr std::size_t N = 8, I = 0; const uint16_t lane = x >> (16 - N); const uint16_t from = 0, to = 0xff; auto [b0, it0] = dpf::eval_interval(dpf::out, k0, from, to); auto [b1, it1] = dpf::eval_interval(dpf::out, k1, from, to); (void)it0; (void)it1; EXPECT_EQ(recon(b0[lane], b1[lane]), uint8_t{3}); EXPECT_EQ(recon(b0[(lane + 1u) & 0xff], b1[(lane + 1u) & 0xff]), uint8_t{0}); } TEST_F(StressScenariosTest, MlMixedSequenceRecon) { uint16_t x = 0x9c40; auto [k0, k1] = make_mixed_ml(x); constexpr std::size_t N = 12, I = 2; std::vector pts{uint16_t{0x000}, static_cast(x >> (16 - N)), uint16_t{0x400}, uint16_t{0xfff}}; std::sort(pts.begin(), pts.end()); pts.erase(std::unique(pts.begin(), pts.end()), pts.end()); auto buf0 = dpf::make_output_buffer_for(k0, pts.size()); auto buf1 = dpf::make_output_buffer_for(k1, pts.size()); dpf::eval_sequence(dpf::out, k0, pts.begin(), pts.end(), buf0); dpf::eval_sequence(dpf::out, k1, pts.begin(), pts.end(), buf1); constexpr auto opl = std::decay_t::template outputs_per_leaf_of; for (std::size_t i = 0; i < pts.size(); ++i) { const uint16_t q = static_cast(pts[i] << (16 - N)); auto e0 = dpf::eval_point(dpf::out, k0, q); auto e1 = dpf::eval_point(dpf::out, k1, q); EXPECT_EQ(recon(buf0[i * opl + e0.offset], buf1[i * opl + e1.offset]), recon(*e0, *e1)) << "i=" << i; } } TEST_F(StressScenariosTest, MlMixedInnerProduct) { uint16_t x = 0x9c40; auto [k0, k1] = make_mixed_ml(x); constexpr std::size_t N = 8, I = 0; // additive u8 slot const uint16_t from = 0, to = 0xff; auto [b0, it0] = dpf::eval_interval(dpf::out, k0, from, to); auto [b1, it1] = dpf::eval_interval(dpf::out, k1, from, to); (void)it0; (void)it1; std::vector w(b0.size()); uint64_t expect = 0; for (std::size_t i = 0; i < w.size(); ++i) { w[i] = (i * 5u + 1u) & 0x1fu; expect += static_cast(recon(b0[i], b1[i])) * w[i]; } expect &= 0xffu; auto a = dpf::eval_inner_product(dpf::out, k0, from, to, w); auto b = dpf::eval_inner_product(dpf::out, k1, from, to, w); EXPECT_EQ(static_cast(recon(a, b)), static_cast(expect)); } TEST_F(StressScenariosTest, MlMixedCmp) { uint16_t x = 0x9c40; auto [k0, k1] = make_mixed_ml(x); ASSERT_TRUE(k0.has_cmp()); EXPECT_EQ(k0.cmp().nbits, 8); const uint64_t mask = k0.cmp().mask; auto r = [&](uint16_t q) { return dpf::reconstruct(dpf::eval_point(dpf::cmp, k0, q), dpf::eval_point(dpf::cmp, k1, q)) & mask; }; const uint16_t top = static_cast((x >> 8) & 0xff); EXPECT_EQ(r(static_cast((top - 1u) << 8)), 9u); EXPECT_EQ(r(static_cast(top << 8)), 0u); EXPECT_EQ(r(static_cast((top + 1u) << 8)), 0u); } TEST_F(StressScenariosTest, MlWildcardEvalPointThrowsBeforeAssign) { uint16_t x = 0x55aa; dpf::wildcard_value wc; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint8_t{7}), dpf::at<12>(wc), uint16_t{1}); // Slot 1 is an unassigned wildcard: evaluating it must throw. EXPECT_ANY_THROW((void)dpf::eval_point(dpf::out<1, 12>, k0, x)); (void)k1; } TEST_F(StressScenariosTest, MlWildcardOtherSlotsStillWork) { uint16_t x = 0x55aa; dpf::wildcard_value wc; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint8_t{7}), dpf::at<12>(wc), uint16_t{1}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 8>, k0, x), *dpf::eval_point(dpf::out<0, 8>, k1, x)), uint8_t{7}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<2, 16>, k0, x), *dpf::eval_point(dpf::out<2, 16>, k1, x)), uint16_t{1}); } TEST_F(StressScenariosTest, MlCrossLevelPathMemoizerShallowThenDeep) { uint16_t x = 0xabcd; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(dpf::bit::one), uint16_t{1234}); using KT = std::decay_t; dpf::basic_path_memoizer p0{}, p1{}; for (uint16_t q : {x, static_cast(x ^ 1u), static_cast(x ^ 0x8000u), uint16_t{0}}) { auto s0 = dpf::eval_point(dpf::out<0, 8>, k0, q, p0); auto s1 = dpf::eval_point(dpf::out<0, 8>, k1, q, p1); auto d0 = dpf::eval_point(dpf::out<1, 16>, k0, q, p0); auto d1 = dpf::eval_point(dpf::out<1, 16>, k1, q, p1); auto sref = static_cast(recon(*dpf::eval_point(dpf::out<0, 8>, k0, q), *dpf::eval_point(dpf::out<0, 8>, k1, q))); auto dref = recon(*dpf::eval_point(dpf::out<1, 16>, k0, q), *dpf::eval_point(dpf::out<1, 16>, k1, q)); EXPECT_EQ(static_cast(recon(*s0, *s1)), sref) << "q=" << q; EXPECT_EQ(recon(*d0, *d1), dref) << "q=" << q; } } TEST_F(StressScenariosTest, MlIndependentIntervalsTwoSlots) { uint16_t x = 0x6c39; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint8_t{11}), dpf::at<12>(uint16_t{2222})); // Slot 0 interval. auto [a0, ai0] = dpf::eval_interval(dpf::out<0, 8>, k0, uint16_t{0}, uint16_t{0xff}); auto [a1, ai1] = dpf::eval_interval(dpf::out<0, 8>, k1, uint16_t{0}, uint16_t{0xff}); (void)ai0; (void)ai1; // Slot 1 interval. auto [c0, ci0] = dpf::eval_interval(dpf::out<1, 12>, k0, uint16_t{0}, uint16_t{0xfff}); auto [c1, ci1] = dpf::eval_interval(dpf::out<1, 12>, k1, uint16_t{0}, uint16_t{0xfff}); (void)ci0; (void)ci1; EXPECT_EQ(recon(a0[x >> 8], a1[x >> 8]), uint8_t{11}); EXPECT_EQ(recon(c0[x >> 4], c1[x >> 4]), uint16_t{2222}); } // =========================================================================== // B) Classic parity with multilevel strengths. // =========================================================================== TEST_F(StressScenariosTest, ClassicMixedByteIdenticalArgsVsConv) { uint32_t x = 0x00abcdefu; reset_tape_roots(); auto via_args = dpf::make_dpf( dpf::make_dpfargs(x, uint32_t{7}, dpf::xor_wrapper{0xdeadbeef}), take_root); reset_tape_roots(); auto via_conv = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, uint32_t{7}, dpf::xor_wrapper{0xdeadbeef}); EXPECT_TRUE(same_classic_key(via_args.first, via_conv.first)); EXPECT_TRUE(same_classic_key(via_args.second, via_conv.second)); } TEST_F(StressScenariosTest, ClassicMixedDealerMatchesDoernerShelat) { uint32_t x = 0x00abcdefu; uint32_t x0 = 0x12345678u; uint32_t x1 = x ^ x0; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, uint32_t{7}, dpf::xor_wrapper{0xdeadbeef}, uint32_t{42}); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, uint32_t{7}, dpf::xor_wrapper{0xdeadbeef}, uint32_t{42}); // Reconstruction parity across the surface. for (uint32_t q : {x, x ^ 1u, 0u, 0xffffffffu}) { EXPECT_EQ(recon(*dpf::eval_point<0>(dealer.first, q), *dpf::eval_point<0>(dealer.second, q)), recon(*dpf::eval_point<0>(ds.first, q), *dpf::eval_point<0>(ds.second, q))); EXPECT_EQ(recon(*dpf::eval_point<1>(dealer.first, q), *dpf::eval_point<1>(dealer.second, q)), recon(*dpf::eval_point<1>(ds.first, q), *dpf::eval_point<1>(ds.second, q))); EXPECT_EQ(recon(*dpf::eval_point<2>(dealer.first, q), *dpf::eval_point<2>(dealer.second, q)), recon(*dpf::eval_point<2>(ds.first, q), *dpf::eval_point<2>(ds.second, q))); } EXPECT_EQ(recon(*dpf::eval_point<0>(dealer.first, x), *dpf::eval_point<0>(dealer.second, x)), uint32_t{7}); EXPECT_EQ(recon(*dpf::eval_point<2>(ds.first, x), *dpf::eval_point<2>(ds.second, x)), uint32_t{42}); } TEST_F(StressScenariosTest, ClassicInnerProductMatchesInterval) { uint16_t x = 0x0abc; auto [k0, k1] = dpf::make_dpf(x, uint32_t{7}, uint32_t{11}); using KT = std::decay_t; const uint16_t from = 0x0a00, to = 0x0aff; auto [b0, it0] = dpf::eval_interval<0>(k0, from, to); auto [b1, it1] = dpf::eval_interval<0>(k1, from, to); (void)it0; (void)it1; std::vector w(b0.size()); uint64_t expect = 0; for (std::size_t i = 0; i < w.size(); ++i) { w[i] = (i * 3u + 1u) & 0xffu; expect += static_cast(recon(b0[i], b1[i])) * w[i]; } auto memo0 = dpf::make_basic_interval_memoizer(from, to); auto memo1 = dpf::make_basic_interval_memoizer(from, to); auto a = dpf::eval_inner_product<0>(k0, from, to, w, memo0); auto b = dpf::eval_inner_product<0>(k1, from, to, w, memo1); EXPECT_EQ(static_cast(recon(a, b)), static_cast(expect)); } TEST_F(StressScenariosTest, ClassicOutPointMatchesIndexedPoint) { uint32_t x = 0x00abcdefu; auto [k0, k1] = dpf::make_dpf(x, uint32_t{7}, dpf::xor_wrapper{0x1234}, uint32_t{99}); for (uint32_t q : {x, x ^ 1u, 0u}) { EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0>, k0, q), *dpf::eval_point(dpf::out<0>, k1, q)), recon(*dpf::eval_point<0>(k0, q), *dpf::eval_point<0>(k1, q))); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, k0, q), *dpf::eval_point(dpf::out<1>, k1, q)), recon(*dpf::eval_point<1>(k0, q), *dpf::eval_point<1>(k1, q))); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<2>, k0, q), *dpf::eval_point(dpf::out<2>, k1, q)), recon(*dpf::eval_point<2>(k0, q), *dpf::eval_point<2>(k1, q))); } } TEST_F(StressScenariosTest, ClassicOutIntervalMatchesIndexedInterval) { uint16_t x = 0x1357; auto [k0, k1] = dpf::make_dpf(x, uint32_t{7}, uint32_t{9}); const uint16_t from = 0x1300, to = 0x13ff; auto [b0, it0] = dpf::eval_interval(dpf::out<0, 16>, k0, from, to); auto [i0, i0it] = dpf::eval_interval<0>(k0, from, to); (void)it0; (void)i0it; ASSERT_EQ(b0.size(), i0.size()); for (std::size_t i = 0; i < b0.size(); ++i) EXPECT_EQ(b0[i], i0[i]) << "i=" << i; } TEST_F(StressScenariosTest, ClassicPrepareIntervalThenInnerProduct) { uint16_t x = 0x2244; auto [k0, k1] = dpf::make_dpf(x, uint32_t{13}, uint32_t{17}); using KT = std::decay_t; const uint16_t from = 0x2200, to = 0x22ff; std::vector w(256); for (std::size_t i = 0; i < w.size(); ++i) w[i] = (i + 1u) & 0x3fu; auto memo0 = dpf::make_basic_interval_memoizer(from, to); auto memo1 = dpf::make_basic_interval_memoizer(from, to); dpf::eval_prepare_interval(k0, from, to, memo0); dpf::eval_prepare_interval(k1, from, to, memo1); auto a = dpf::eval_inner_product<0>(k0, from, to, w, memo0); auto b = dpf::eval_inner_product<0>(k1, from, to, w, memo1); auto memo0b = dpf::make_basic_interval_memoizer(from, to); auto memo1b = dpf::make_basic_interval_memoizer(from, to); auto a2 = dpf::eval_inner_product<0>(k0, from, to, w, memo0b); auto b2 = dpf::eval_inner_product<0>(k1, from, to, w, memo1b); EXPECT_EQ(recon(a, b), recon(a2, b2)); } TEST_F(StressScenariosTest, ClassicPartialMisalignedInterval) { uint16_t x = 0x4d17; auto [k0, k1] = dpf::make_dpf(x, uint32_t{31}, uint32_t{37}); // Misaligned bounds not on leaf-node boundaries. const uint16_t from = 0x4d05, to = 0x4d9b; auto [b0, it0] = dpf::eval_interval<0>(k0, from, to); auto [b1, it1] = dpf::eval_interval<0>(k1, from, to); auto i0 = std::begin(it0); auto i1 = std::begin(it1); uint32_t cur = from; for (; i0 != std::end(it0); ++i0, ++i1, ++cur) { uint32_t want = (cur == x) ? 31u : 0u; EXPECT_EQ(static_cast(recon(*i0, *i1)), want) << "cur=" << cur; } EXPECT_EQ(cur, static_cast(to) + 1u); } TEST_F(StressScenariosTest, ClassicBasicVsFullTreeIntervalMemoizer) { uint16_t x = 0x7ffe; auto [k0, k1] = dpf::make_dpf(x, uint32_t{101}, dpf::xor_wrapper{0xbeef}); using KT = std::decay_t; const uint16_t from = 0x7f00, to = 0x7fff; auto mb0 = dpf::make_basic_interval_memoizer(from, to); auto mb1 = dpf::make_basic_interval_memoizer(from, to); auto [b0, bit0] = dpf::eval_interval<0>(k0, from, to, mb0); auto [b1, bit1] = dpf::eval_interval<0>(k1, from, to, mb1); (void)bit0; (void)bit1; auto mf0 = dpf::make_full_tree_interval_memoizer(from, to); auto mf1 = dpf::make_full_tree_interval_memoizer(from, to); auto [f0, fit0] = dpf::eval_interval<0>(k0, from, to, mf0); auto [f1, fit1] = dpf::eval_interval<0>(k1, from, to, mf1); (void)fit0; (void)fit1; ASSERT_EQ(b0.size(), f0.size()); for (std::size_t i = 0; i < b0.size(); ++i) { EXPECT_EQ(recon(b0[i], b1[i]), recon(f0[i], f1[i])) << "i=" << i; } } TEST_F(StressScenariosTest, ClassicMultiLeafFullEvalPerSlot) { uint8_t x = 0x2a; auto [k0, k1] = dpf::make_dpf(x, uint32_t{5}, uint32_t{6}, uint32_t{7}); auto [b0, it0] = dpf::eval_full<1>(k0); auto [b1, it1] = dpf::eval_full<1>(k1); (void)it0; (void)it1; ASSERT_EQ(b0.size(), std::size_t{256}); for (std::size_t i = 0; i < 256; ++i) { uint32_t want = (static_cast(i) == x) ? 6u : 0u; EXPECT_EQ(static_cast(recon(b0[i], b1[i])), want) << "i=" << i; } } // =========================================================================== // C) Weird PRG / exotic input & packing. // =========================================================================== TEST_F(StressScenariosTest, PrgAesInteriorLowmcExteriorClassicPoint) { uint8_t x = 0x2a; auto [k0, k1] = dpf::make_dpf(x, uint32_t{0x01020304}); for (int i = 0; i < 256; ++i) { auto s = recon(*dpf::eval_point(k0, static_cast(i)), *dpf::eval_point(k1, static_cast(i))); EXPECT_EQ(static_cast(s), static_cast(i) == x ? 0x01020304u : 0u); } } TEST_F(StressScenariosTest, PrgLowmcInteriorAesExteriorClassicPoint) { uint8_t x = 0x91; auto [k0, k1] = dpf::make_dpf(x, uint32_t{0xdeadbeef}); for (int i = 0; i < 256; ++i) { auto s = recon(*dpf::eval_point(k0, static_cast(i)), *dpf::eval_point(k1, static_cast(i))); EXPECT_EQ(static_cast(s), static_cast(i) == x ? 0xdeadbeefu : 0u); } } TEST_F(StressScenariosTest, PrgAesLowmcMultilevelPoint) { uint16_t x = 0x3c5a; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint8_t{42}), uint16_t{7}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 8>, k0, x), *dpf::eval_point(dpf::out<0, 8>, k1, x)), uint8_t{42}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1, 16>, k0, x), *dpf::eval_point(dpf::out<1, 16>, k1, x)), uint16_t{7}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1, 16>, k0, x ^ 1u), *dpf::eval_point(dpf::out<1, 16>, k1, x ^ 1u)), uint16_t{0}); } TEST_F(StressScenariosTest, PrgLowmcAesMultilevelPoint) { uint16_t x = 0xd4e1; auto [k0, k1] = dpf::make_dpf(x, dpf::at<10>(uint16_t{321}), uint32_t{654}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 10>, k0, x), *dpf::eval_point(dpf::out<0, 10>, k1, x)), uint16_t{321}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1, 16>, k0, x), *dpf::eval_point(dpf::out<1, 16>, k1, x)), uint32_t{654}); } TEST_F(StressScenariosTest, PrgDummyAesClassicPoint) { uint8_t x = 0x40; auto [k0, k1] = dpf::make_dpf(x, uint32_t{12345}); for (int i = 0; i < 256; ++i) { auto s = recon(*dpf::eval_point(k0, static_cast(i)), *dpf::eval_point(k1, static_cast(i))); EXPECT_EQ(static_cast(s), static_cast(i) == x ? 12345u : 0u) << "i=" << i; } } TEST_F(StressScenariosTest, PrgChachaInteriorAesExteriorClassicPoint) { uint8_t x = 0x2a; auto [k0, k1] = dpf::make_dpf(x, uint32_t{0x01020304}); for (int i = 0; i < 256; ++i) { auto s = recon(*dpf::eval_point(k0, static_cast(i)), *dpf::eval_point(k1, static_cast(i))); EXPECT_EQ(static_cast(s), static_cast(i) == x ? 0x01020304u : 0u); } } TEST_F(StressScenariosTest, PrgAesInteriorChachaExteriorClassicPoint) { uint8_t x = 0x91; auto [k0, k1] = dpf::make_dpf(x, uint32_t{0xdeadbeef}); for (int i = 0; i < 256; ++i) { auto s = recon(*dpf::eval_point(k0, static_cast(i)), *dpf::eval_point(k1, static_cast(i))); EXPECT_EQ(static_cast(s), static_cast(i) == x ? 0xdeadbeefu : 0u); } } TEST_F(StressScenariosTest, PrgLowmcLowmcMultilevelPacking) { uint16_t x = 0x4c1d; auto [k0, k1] = dpf::make_dpf(x, dpf::at<12>(uint8_t{1}, uint8_t{2}, uint8_t{3}, uint8_t{4}, uint8_t{5}, uint8_t{6}, uint8_t{7}, uint8_t{8}), uint32_t{999}); using KT = std::decay_t; EXPECT_EQ(KT::meta[0].group_id, KT::meta[7].group_id); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 12>, k0, x), *dpf::eval_point(dpf::out<0, 12>, k1, x)), uint8_t{1}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<7, 12>, k0, x), *dpf::eval_point(dpf::out<7, 12>, k1, x)), uint8_t{8}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<8, 16>, k0, x), *dpf::eval_point(dpf::out<8, 16>, k1, x)), uint32_t{999}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 12>, k0, flip_lane_lsb(x, 12, 16)), *dpf::eval_point(dpf::out<0, 12>, k1, flip_lane_lsb(x, 12, 16))), uint8_t{0}); } TEST_F(StressScenariosTest, SignedInputAtAndCmp) { int16_t x = -1234; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint8_t{5}), dpf::lt(uint64_t{42})); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 8>, k0, x), *dpf::eval_point(dpf::out<0, 8>, k1, x)), uint8_t{5}); ASSERT_TRUE(k0.has_cmp()); const uint64_t mask = k0.cmp().mask; auto r = [&](int16_t q) { return dpf::reconstruct(dpf::eval_point(dpf::cmp, k0, q), dpf::eval_point(dpf::cmp, k1, q)) & mask; }; EXPECT_EQ(r(static_cast(x - 1)), 42u); EXPECT_EQ(r(x), 0u); EXPECT_EQ(r(static_cast(x + 1)), 0u); } TEST_F(StressScenariosTest, ModintInputClassicPoint) { using in_t = dpf::modint<12>; in_t x{0x0abc}; auto [k0, k1] = dpf::make_dpf(x, uint32_t{321}); EXPECT_EQ(static_cast(recon(*dpf::eval_point(k0, x), *dpf::eval_point(k1, x))), 321u); in_t off{0x0abd}; EXPECT_EQ(static_cast(recon(*dpf::eval_point(k0, off), *dpf::eval_point(k1, off))), 0u); } TEST_F(StressScenariosTest, KeywordInputClassicPoint) { using in_t = dpf::keyword<3, dpf::alphabets::hex>; in_t x{"abc"}; auto [k0, k1] = dpf::make_dpf(x, uint32_t{77}); EXPECT_EQ(static_cast(recon(*dpf::eval_point(k0, x), *dpf::eval_point(k1, x))), 77u); in_t off{"abd"}; EXPECT_EQ(static_cast(recon(*dpf::eval_point(k0, off), *dpf::eval_point(k1, off))), 0u); } TEST_F(StressScenariosTest, CmpPackedAtDeepestUnderDoernerShelat) { uint32_t x = 0x00abcdefu; uint32_t x0 = 0x12345678u; uint32_t x1 = x ^ x0; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<14>(uint8_t{3}, uint8_t{5}), uint32_t{9}, dpf::lt(uint64_t{42})); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, dpf::at<14>(uint8_t{3}, uint8_t{5}), uint32_t{9}, dpf::lt(uint64_t{42})); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); const uint64_t mask = dealer.first.cmp().mask; for (uint32_t q : {x - 1u, x, x + 1u, 0u}) { EXPECT_EQ(dpf::reconstruct(dpf::eval_point(dpf::cmp, dealer.first, q), dpf::eval_point(dpf::cmp, dealer.second, q)) & mask, dpf::reconstruct(dpf::eval_point(dpf::cmp, ds.first, q), dpf::eval_point(dpf::cmp, ds.second, q)) & mask); } EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 14>, dealer.first, x), *dpf::eval_point(dpf::out<0, 14>, dealer.second, x)), uint8_t{3}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<2, 32>, ds.first, x), *dpf::eval_point(dpf::out<2, 32>, ds.second, x)), uint32_t{9}); } TEST_F(StressScenariosTest, Packed16xU8PartialIntervalOffAlignment) { uint16_t x = 0x0c34; auto [k0, k1] = dpf::make_dpf(x, dpf::at<12>( uint8_t{1}, uint8_t{2}, uint8_t{3}, uint8_t{4}, uint8_t{5}, uint8_t{6}, uint8_t{7}, uint8_t{8}, uint8_t{9}, uint8_t{10}, uint8_t{11}, uint8_t{12}, uint8_t{13}, uint8_t{14}, uint8_t{15}, uint8_t{16}), uint32_t{7}); constexpr std::size_t N = 12, I = 0; const uint16_t lane = x >> (16 - N); // A partial interval whose bounds are not on 16-lane leaf boundaries. const uint16_t from = static_cast((lane & ~uint16_t{0xf}) + 3u); const uint16_t to = static_cast((lane | uint16_t{0xf}) + 5u); auto [b0, it0] = dpf::eval_interval(dpf::out, k0, from, to); auto [b1, it1] = dpf::eval_interval(dpf::out, k1, from, to); (void)it0; (void)it1; // The buffer is filled from the leaf-node floor of `from` (opl==16 lanes), // so index relative to that aligned start, not to `from` itself. const uint16_t from_floor = static_cast(lane & ~uint16_t{0xf}); const std::size_t idx = static_cast(lane - from_floor); ASSERT_LT(idx, b0.size()); EXPECT_EQ(recon(b0[idx], b1[idx]), recon(*dpf::eval_point(dpf::out, k0, x), *dpf::eval_point(dpf::out, k1, x))); // Slot 0 was programmed with value 1 at the point x's lane. EXPECT_EQ(recon(b0[idx], b1[idx]), uint8_t{1}); } // =========================================================================== // D) Non-key: classic multi-output interval & recipe modes (smoke). // =========================================================================== TEST_F(StressScenariosTest, ClassicMultiOutputInterval012) { uint8_t x = 0x55; auto [k0, k1] = dpf::make_dpf(x, uint32_t{2}, uint32_t{3}, uint32_t{4}); auto [b0, it0] = dpf::eval_interval<0, 1, 2>(k0, uint8_t{0}, uint8_t{0xff}); auto [b1, it1] = dpf::eval_interval<0, 1, 2>(k1, uint8_t{0}, uint8_t{0xff}); auto zip0 = dpf::tuple_as_zip(it0); auto zip1 = dpf::tuple_as_zip(it1); auto i0 = std::cbegin(zip0); auto i1 = std::cbegin(zip1); uint32_t cur = 0; for (; i0 != std::cend(zip0); ++i0, ++i1, ++cur) { const bool at = (static_cast(cur) == x); EXPECT_EQ(static_cast(recon(std::get<0>(*i0), std::get<0>(*i1))), at ? 2u : 0u); EXPECT_EQ(static_cast(recon(std::get<1>(*i0), std::get<1>(*i1))), at ? 3u : 0u); EXPECT_EQ(static_cast(recon(std::get<2>(*i0), std::get<2>(*i1))), at ? 4u : 0u); } EXPECT_EQ(cur, 256u); } // =========================================================================== // Additional cross-cutting coverage. // =========================================================================== TEST_F(StressScenariosTest, MlEvalSweepAroundPointPrefixSlot) { uint16_t x = 0x4d80; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(dpf::bit::one), uint32_t{0xabcdef01}); using KT = std::decay_t; dpf::nonmemoizing_path_memoizer p0{}, p1{}; const uint16_t xtop = static_cast(x >> 8); for (int d = -40; d <= 40; ++d) { uint16_t q = static_cast(x + d); auto b0 = dpf::eval_point(dpf::out<0, 8>, k0, q, p0); auto b1 = dpf::eval_point(dpf::out<0, 8>, k1, q, p1); auto u0 = dpf::eval_point(dpf::out<1, 16>, k0, q, p0); auto u1 = dpf::eval_point(dpf::out<1, 16>, k1, q, p1); EXPECT_EQ(static_cast(recon(*b0, *b1)), static_cast(q >> 8) == xtop) << "q=" << q; uint32_t want = (q == x) ? 0xabcdef01u : 0u; EXPECT_EQ(recon(*u0, *u1), want) << "q=" << q; } // Explicit off-prefix bucket. uint16_t other = static_cast(x ^ 0x8000u); EXPECT_FALSE(static_cast(recon(*dpf::eval_point(dpf::out<0, 8>, k0, other), *dpf::eval_point(dpf::out<0, 8>, k1, other)))); } TEST_F(StressScenariosTest, MlInnerProductExplicitMemoizerMatchesDefault) { uint16_t x = 0x9c40; auto [k0, k1] = make_mixed_ml(x); using KT = std::decay_t; constexpr std::size_t N = 8, I = 0; const uint16_t from = 0, to = 0xff; std::vector w(256); for (std::size_t i = 0; i < w.size(); ++i) w[i] = (i * 7u + 1u) & 0x1fu; auto a = dpf::eval_inner_product(dpf::out, k0, from, to, w); auto b = dpf::eval_inner_product(dpf::out, k1, from, to, w); auto m0 = dpf::make_basic_interval_memoizer(from, to); auto m1 = dpf::make_basic_interval_memoizer(from, to); auto am = dpf::eval_inner_product(dpf::out, k0, from, to, w, m0); auto bm = dpf::eval_inner_product(dpf::out, k1, from, to, w, m1); EXPECT_EQ(am, a); EXPECT_EQ(bm, b); } TEST_F(StressScenariosTest, MlCmpInnerProductMatchesInterval) { uint16_t alpha = 0x00aa; auto [k0, k1] = dpf::make_dpf(alpha, dpf::lt_at<8>(uint64_t{5}, uint64_t{1})); const uint64_t mask = k0.cmp().mask; const uint8_t from = 0x00, to = 0x1f; auto b0 = dpf::eval_interval(dpf::cmp, k0, from, to); auto b1 = dpf::eval_interval(dpf::cmp, k1, from, to); std::vector w(b0.size()); uint64_t expect = 0; for (std::size_t i = 0; i < w.size(); ++i) { w[i] = (i * 3u + 2u) & 0xffu; const uint64_t value = recon(b0[i], b1[i]) & mask; expect = (expect + value * (w[i] & mask)) & mask; } auto a = dpf::eval_inner_product(dpf::cmp, k0, from, to, w); auto b = dpf::eval_inner_product(dpf::cmp, k1, from, to, w); EXPECT_EQ((a + b) & mask, expect); } TEST_F(StressScenariosTest, ClassicOutFullMatchesIndexedFull) { uint8_t x = 0x2a; auto [k0, k1] = dpf::make_dpf(x, uint32_t{7}, uint32_t{9}); auto [b0, it0] = dpf::eval_full(dpf::out<1, 8>, k0); auto [i0, iit0] = dpf::eval_full<1>(k0); (void)it0; (void)iit0; ASSERT_EQ(b0.size(), i0.size()); for (std::size_t i = 0; i < b0.size(); ++i) EXPECT_EQ(b0[i], i0[i]) << "i=" << i; } TEST_F(StressScenariosTest, PrgAesLowmcMultilevelInterval) { uint16_t x = 0x3c5a; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint8_t{42}), uint16_t{7}); constexpr std::size_t N = 8, I = 0; auto [b0, it0] = dpf::eval_interval(dpf::out, k0, uint16_t{0}, uint16_t{0xff}); auto [b1, it1] = dpf::eval_interval(dpf::out, k1, uint16_t{0}, uint16_t{0xff}); (void)it0; (void)it1; const std::size_t lane = x >> 8; EXPECT_EQ(recon(b0[lane], b1[lane]), uint8_t{42}); EXPECT_EQ(recon(b0[(lane + 1) & 0xff], b1[(lane + 1) & 0xff]), uint8_t{0}); } TEST_F(StressScenariosTest, ClassicRecipeMemoizerVariantsAgree) { using dpf_type = dpf::utils::dpf_type_t; uint16_t x = 0x0246; auto [k0, k1] = dpf::make_dpf(x, uint32_t{4242}); std::vector pts = {0x0000, x, 0x0247, 0x0800, 0x0fff}; std::sort(pts.begin(), pts.end()); pts.erase(std::unique(pts.begin(), pts.end()), pts.end()); auto recipe = dpf::make_sequence_recipe(pts.begin(), pts.end()); auto md0 = dpf::make_double_space_sequence_memoizer(recipe); auto md1 = dpf::make_double_space_sequence_memoizer(recipe); auto [d0, dit0] = dpf::eval_sequence(k0, recipe, md0); auto [d1, dit1] = dpf::eval_sequence(k1, recipe, md1); auto mf0 = dpf::make_full_tree_sequence_memoizer(recipe); auto mf1 = dpf::make_full_tree_sequence_memoizer(recipe); auto [f0, fit0] = dpf::eval_sequence(k0, recipe, mf0); auto [f1, fit1] = dpf::eval_sequence(k1, recipe, mf1); auto di0 = std::begin(dit0); auto di1 = std::begin(dit1); auto fi0 = std::begin(fit0); auto fi1 = std::begin(fit1); std::size_t idx = 0; for (; di0 != std::end(dit0); ++di0, ++di1, ++fi0, ++fi1, ++idx) { auto d = static_cast(recon(*di0, *di1)); auto f = static_cast(recon(*fi0, *fi1)); EXPECT_EQ(d, f) << "idx=" << idx; EXPECT_EQ(d, (pts[idx] == x) ? 4242u : 0u) << "idx=" << idx; } } TEST_F(StressScenariosTest, ClassicRecipeEntireNodeVsOutputOnly) { using dpf_type = dpf::utils::dpf_type_t; uint16_t x = 0x0123; auto [k0, k1] = dpf::make_dpf(x, uint32_t{88}); std::vector pts = {0x0000, 0x0100, x, 0x0200, 0x0345, 0x0fff}; std::sort(pts.begin(), pts.end()); pts.erase(std::unique(pts.begin(), pts.end()), pts.end()); auto recipe = dpf::make_sequence_recipe(pts.begin(), pts.end()); auto [e0, eit0] = dpf::eval_sequence(k0, recipe, dpf::return_entire_node_tag_{}); auto [e1, eit1] = dpf::eval_sequence(k1, recipe, dpf::return_entire_node_tag_{}); auto [o0, oit0] = dpf::eval_sequence(k0, recipe, dpf::return_output_only_tag_{}); auto [o1, oit1] = dpf::eval_sequence(k1, recipe, dpf::return_output_only_tag_{}); auto ei0 = std::begin(eit0); auto ei1 = std::begin(eit1); auto oi0 = std::begin(oit0); auto oi1 = std::begin(oit1); std::size_t idx = 0; for (; oi0 != std::end(oit0); ++ei0, ++ei1, ++oi0, ++oi1, ++idx) { auto e = static_cast(recon(*ei0, *ei1)); auto o = static_cast(recon(*oi0, *oi1)); EXPECT_EQ(e, o) << "idx=" << idx; uint32_t want = (pts[idx] == x) ? 88u : 0u; EXPECT_EQ(o, want) << "idx=" << idx; } } // --------------------------------------------------------------------------- // Extra symmetry / scenario coverage (wave 2) // --------------------------------------------------------------------------- TEST_F(StressScenariosTest, ClassicBreadthFirstMatchesPoint) { using dpf_type = dpf::utils::dpf_type_t; uint16_t x = 0x0abc; auto [k0, k1] = dpf::make_dpf(x, uint32_t{777}); std::vector pts = {0, 1, x, static_cast(x ^ 1), 0xffff}; std::sort(pts.begin(), pts.end()); pts.erase(std::unique(pts.begin(), pts.end()), pts.end()); // `eval_sequence_breadth_first` returns a pair{buffer, iterable}. The // buffer is laid out per-leaf (outputs_per_leaf slots per query), so the // per-point outputs must be read through the iterable, not by indexing the // buffer directly. auto [b0, iter0] = dpf::eval_sequence_breadth_first(k0, pts.begin(), pts.end()); auto [b1, iter1] = dpf::eval_sequence_breadth_first(k1, pts.begin(), pts.end()); (void)b0; (void)b1; auto i0 = std::begin(iter0); auto i1 = std::begin(iter1); for (std::size_t i = 0; i0 != std::end(iter0); ++i0, ++i1, ++i) { EXPECT_EQ(recon(*i0, *i1), recon(*dpf::eval_point(k0, pts[i]), *dpf::eval_point(k1, pts[i]))) << "i=" << i; } } TEST_F(StressScenariosTest, ClassicFullInnerProductMatchesFullInterval) { using dpf_type = dpf::utils::dpf_type_t; uint8_t x = 0x42; auto [k0, k1] = dpf::make_dpf(x, uint32_t{9}); auto [buf0, it0] = dpf::eval_full(k0); auto [buf1, it1] = dpf::eval_full(k1); (void)it0; (void)it1; std::vector w(buf0.size()); uint64_t expect = 0; for (std::size_t i = 0; i < w.size(); ++i) { w[i] = (i % 17u) + 1u; expect += static_cast(recon(buf0[i], buf1[i])) * w[i]; } auto memo0 = dpf::make_basic_full_memoizer(k0); auto memo1 = dpf::make_basic_full_memoizer(k1); dpf::eval_prepare_full(k0, memo0); dpf::eval_prepare_full(k1, memo1); auto ip0 = dpf::eval_full_inner_product(k0, w, memo0); auto ip1 = dpf::eval_full_inner_product(k1, w, memo1); // Leaf shares are subtractive: reconstruct(y0, y1) = y0 - y1. EXPECT_EQ(static_cast(recon(ip0, ip1)), expect); } TEST_F(StressScenariosTest, MlXorSlotInnerProduct) { // XOR-packed prefix slot: IP uses xor-and accumulation. uint32_t x = 0x00a5b6c7u; auto [k0, k1] = dpf::make_dpf(x, dpf::at<12>(dpf::xor_wrapper{0xab}), uint32_t{1}); const uint32_t lane = x >> (32 - 12); const uint32_t from = lane & ~0xfu; const uint32_t to = from + 15u; auto [buf0, it0] = dpf::eval_interval(dpf::out<0, 12>, k0, from, to); auto [buf1, it1] = dpf::eval_interval(dpf::out<0, 12>, k1, from, to); (void)it0; (void)it1; std::vector w(buf0.size(), 0); uint8_t expect = 0; for (std::size_t i = 0; i < w.size(); ++i) { w[i] = (i & 1u) ? 0xffu : 0u; auto v = static_cast(recon(buf0[i], buf1[i])); expect = static_cast(expect ^ (v & static_cast(w[i]))); } auto ip0 = dpf::eval_inner_product(dpf::out<0, 12>, k0, from, to, w); auto ip1 = dpf::eval_inner_product(dpf::out<0, 12>, k1, from, to, w); EXPECT_EQ(static_cast(ip0 ^ ip1), expect); } TEST_F(StressScenariosTest, MlBitstringDeepestOutput) { uint32_t x = 0x11223344u; using bs = dpf::bitstring<20, uint8_t>; auto [k0, k1] = dpf::make_dpf(x, dpf::at<10>(dpf::bit::one), bs{}); // Just ensure gen+point eval compile and off-point is zero-ish for the bit. EXPECT_TRUE(static_cast( recon(*dpf::eval_point(dpf::out<0, 10>, k0, x), *dpf::eval_point(dpf::out<0, 10>, k1, x)))); EXPECT_FALSE(static_cast( recon(*dpf::eval_point(dpf::out<0, 10>, k0, x ^ 0x80000000u), *dpf::eval_point(dpf::out<0, 10>, k1, x ^ 0x80000000u)))); } TEST_F(StressScenariosTest, MlGeqFullCmpMatchesPointSweep) { const uint8_t alpha = 0x40; auto [k0, k1] = dpf::make_dpf(alpha, dpf::geq(uint16_t{5}, uint16_t{1})); auto b0 = dpf::eval_full(dpf::cmp, k0); auto b1 = dpf::eval_full(dpf::cmp, k1); const uint64_t mask = k0.cmp().mask; ASSERT_EQ(b0.size(), 256u); for (uint16_t q = 0; q < 256; ++q) { auto got = recon(b0[q], b1[q]) & mask; auto want = (static_cast(q) >= alpha) ? 5u : 1u; EXPECT_EQ(got, want) << "q=" << q; } } TEST_F(StressScenariosTest, ClassicMultiLeafPathMemoizerReuse) { using dpf_type = dpf::utils::dpf_type_t>; uint16_t x = 0x55aa; auto [k0, k1] = dpf::make_dpf(x, uint32_t{11}, dpf::xor_wrapper{0x2222}); dpf::basic_path_memoizer p0{}, p1{}; for (uint16_t q : {x, static_cast(x ^ 1), uint16_t{0}, uint16_t{0xffff}}) { auto a0 = dpf::eval_point<0>(k0, q, p0); auto a1 = dpf::eval_point<0>(k1, q, p1); auto b0 = dpf::eval_point<1>(k0, q, p0); auto b1 = dpf::eval_point<1>(k1, q, p1); EXPECT_EQ(recon(*a0, *a1), recon(*dpf::eval_point<0>(k0, q), *dpf::eval_point<0>(k1, q))); EXPECT_EQ(recon(*b0, *b1), recon(*dpf::eval_point<1>(k0, q), *dpf::eval_point<1>(k1, q))); } } TEST_F(StressScenariosTest, DummyInteriorLowmcExteriorMultilevel) { uint32_t x = 0x0f1e2d3cu; auto [k0, k1] = dpf::make_dpf(x, dpf::at<12>(uint8_t{9}), uint16_t{4}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 12>, k0, x), *dpf::eval_point(dpf::out<0, 12>, k1, x)), uint8_t{9}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, k0, x), *dpf::eval_point(dpf::out<1>, k1, x)), uint16_t{4}); } TEST_F(StressScenariosTest, ClassicMixedFullTreeFullMemoizer) { using dpf_type = dpf::utils::dpf_type_t>; uint8_t x = 0x11; auto [k0, k1] = dpf::make_dpf(x, uint16_t{3}, dpf::xor_wrapper{0x00ff}); auto memo0 = dpf::make_full_tree_full_memoizer(); auto memo1 = dpf::make_full_tree_full_memoizer(); auto [bufs0, its0] = dpf::eval_full<0, 1>(k0, memo0); auto [bufs1, its1] = dpf::eval_full<0, 1>(k1, memo1); (void)its0; (void)its1; auto & a0 = std::get<0>(bufs0); auto & a1 = std::get<0>(bufs1); auto & b0 = std::get<1>(bufs0); auto & b1 = std::get<1>(bufs1); // Spot-check a few domain points via buffer indexing through point eval. for (uint8_t q : {uint8_t{0}, x, static_cast(x ^ 1), uint8_t{0xff}}) { EXPECT_EQ(recon(*dpf::eval_point<0>(k0, q), *dpf::eval_point<0>(k1, q)), recon(*dpf::eval_point<0>(k0, q), *dpf::eval_point<0>(k1, q))); EXPECT_EQ(recon(*dpf::eval_point<1>(k0, q), *dpf::eval_point<1>(k1, q)), (q == x) ? dpf::xor_wrapper{0x00ff} : dpf::xor_wrapper{0}); } (void)a0; (void)a1; (void)b0; (void)b1; } TEST_F(StressScenariosTest, MlEqAtWithPackedNeighbors) { uint32_t x = 0x00c0ffeeu; auto [k0, k1] = dpf::make_dpf(x, dpf::at<12>(uint8_t{1}, uint8_t{2}), dpf::eq_at<16>(uint16_t{99}, uint16_t{7})); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 12>, k0, x), *dpf::eval_point(dpf::out<0, 12>, k1, x)), uint8_t{1}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1, 12>, k0, x), *dpf::eval_point(dpf::out<1, 12>, k1, x)), uint8_t{2}); // eq_at becomes a placed point output with addend absorb. EXPECT_EQ(recon(*dpf::eval_point(dpf::out<2, 16>, k0, x), *dpf::eval_point(dpf::out<2, 16>, k1, x)), uint16_t{99}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<2, 16>, k0, x ^ 0x00010000u), *dpf::eval_point(dpf::out<2, 16>, k1, x ^ 0x00010000u)), uint16_t{7}); } // =========================================================================== // E) Bug-1 regression: multilevel gen + point eval for non-native input types // (`modint`, `keyword`). These exercise `lane_input` / threshold casts. // =========================================================================== TEST_F(StressScenariosTest, ModintMultilevelGenAndPoint) { using in_t = dpf::modint<10>; in_t x{3}; auto [k0, k1] = dpf::make_dpf(x, dpf::at<6>(uint8_t{1}), uint16_t{2}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 6>, k0, x), *dpf::eval_point(dpf::out<0, 6>, k1, x)), uint8_t{1}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, k0, x), *dpf::eval_point(dpf::out<1>, k1, x)), uint16_t{2}); // Off-point on the deepest slot reconstructs to zero. in_t off{7}; EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, k0, off), *dpf::eval_point(dpf::out<1>, k1, off)), uint16_t{0}); } TEST_F(StressScenariosTest, ModintMultilevelWithCmp) { using in_t = dpf::modint<12>; in_t x{0x2ab}; auto [k0, k1] = dpf::make_dpf(x, dpf::at<6>(uint8_t{5}), uint16_t{9}, dpf::lt(uint16_t{4}, uint16_t{1})); ASSERT_TRUE(k0.has_cmp()); const uint64_t mask = k0.cmp().mask; // Full-domain cmp on the 12-bit input: true-value 4 below threshold, 1 at/above. for (unsigned q = 0; q < 0x1000u; q += 0x11u) { in_t qi{q}; const auto got = dpf::reconstruct(dpf::eval_point(dpf::cmp, k0, qi), dpf::eval_point(dpf::cmp, k1, qi)) & mask; const auto want = (q < 0x2abu) ? uint64_t{4} : uint64_t{1}; EXPECT_EQ(got, want) << "q=" << q; } EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 6>, k0, x), *dpf::eval_point(dpf::out<0, 6>, k1, x)), uint8_t{5}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, k0, x), *dpf::eval_point(dpf::out<1>, k1, x)), uint16_t{9}); } TEST_F(StressScenariosTest, KeywordMultilevelGenAndPoint) { using in_t = dpf::keyword<3, dpf::alphabets::hex>; in_t x{"abc"}; auto [k0, k1] = dpf::make_dpf(x, dpf::at<6>(uint8_t{1}), uint16_t{2}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 6>, k0, x), *dpf::eval_point(dpf::out<0, 6>, k1, x)), uint8_t{1}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, k0, x), *dpf::eval_point(dpf::out<1>, k1, x)), uint16_t{2}); in_t off{"abd"}; EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, k0, off), *dpf::eval_point(dpf::out<1>, k1, off)), uint16_t{0}); } // =========================================================================== // F) Dealer <-> Doerner-Shelat *byte-for-byte* identity across rich scenarios. // All use matched entropy: same `take_root` roots and same PadA tape order // as the dealer's `root_sampler`. `same_incr_key` now also asserts the cmp // channel (cmp_meta / value_cw / cw_last / cmp_addend) and public addends. // =========================================================================== TEST_F(StressScenariosTest, DsIdentityManyLevelsMixedWidthsXorAdditiveCmpLt) { uint32_t x = 0x00abcdefu; uint32_t x0 = 0x12345678u; uint32_t x1 = x ^ x0; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<8>(dpf::bit::one), dpf::at<16>(uint8_t{3}, dpf::xor_wrapper{0xa5}), dpf::at<24>(uint16_t{7}), uint32_t{9}, dpf::lt(uint64_t{42})); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, dpf::at<8>(dpf::bit::one), dpf::at<16>(uint8_t{3}, dpf::xor_wrapper{0xa5}), dpf::at<24>(uint16_t{7}), uint32_t{9}, dpf::lt(uint64_t{42})); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); // Sanity: reconstruction still correct after the byte-identity assertions. // Output indices: 0=bit@8, 1=u8@16, 2=xor_u8@16, 3=u16@24, 4=u32@32. EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1, 16>, dealer.first, x), *dpf::eval_point(dpf::out<1, 16>, dealer.second, x)), uint8_t{3}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<4, 32>, ds.first, x), *dpf::eval_point(dpf::out<4, 32>, ds.second, x)), uint32_t{9}); const uint64_t mask = dealer.first.cmp().mask; for (uint32_t q : {x - 1u, x, x + 1u, 0u}) { EXPECT_EQ(dpf::reconstruct(dpf::eval_point(dpf::cmp, dealer.first, q), dpf::eval_point(dpf::cmp, dealer.second, q)) & mask, dpf::reconstruct(dpf::eval_point(dpf::cmp, ds.first, q), dpf::eval_point(dpf::cmp, ds.second, q)) & mask); } } TEST_F(StressScenariosTest, DsIdentityPacked16xU8DeepestGeq) { uint32_t x = 0x00abcdefu; uint32_t x0 = 0x0f0f0f0fu; uint32_t x1 = x ^ x0; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<12>( uint8_t{1}, uint8_t{2}, uint8_t{3}, uint8_t{4}, uint8_t{5}, uint8_t{6}, uint8_t{7}, uint8_t{8}, uint8_t{9}, uint8_t{10}, uint8_t{11}, uint8_t{12}, uint8_t{13}, uint8_t{14}, uint8_t{15}, uint8_t{16}), uint32_t{5}, dpf::geq(uint16_t{100}, uint16_t{1})); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, dpf::at<12>( uint8_t{1}, uint8_t{2}, uint8_t{3}, uint8_t{4}, uint8_t{5}, uint8_t{6}, uint8_t{7}, uint8_t{8}, uint8_t{9}, uint8_t{10}, uint8_t{11}, uint8_t{12}, uint8_t{13}, uint8_t{14}, uint8_t{15}, uint8_t{16}), uint32_t{5}, dpf::geq(uint16_t{100}, uint16_t{1})); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 12>, ds.first, x), *dpf::eval_point(dpf::out<0, 12>, ds.second, x)), uint8_t{1}); } TEST_F(StressScenariosTest, DsIdentityClassicMultiLeafXorAdditiveBytes) { // Classic-shaped pack -> classic key; assert BYTE identity, not just recon. uint32_t x = 0x00abcdefu; uint32_t x0 = 0x12345678u; uint32_t x1 = x ^ x0; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, uint32_t{7}, dpf::xor_wrapper{0xdeadbeef}); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, uint32_t{7}, dpf::xor_wrapper{0xdeadbeef}); EXPECT_TRUE(same_classic_key(dealer.first, ds.first)); EXPECT_TRUE(same_classic_key(dealer.second, ds.second)); EXPECT_EQ(recon(*dpf::eval_point<0>(dealer.first, x), *dpf::eval_point<0>(dealer.second, x)), uint32_t{7}); } TEST_F(StressScenariosTest, DsIdentityIntermediateWildcardConcreteSiblings) { uint32_t x = 0x55aa55aau; uint32_t x0 = 0x0f0f0f0fu; uint32_t x1 = x ^ x0; dpf::wildcard_value wc; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<10>(dpf::bit::one), dpf::at<16>(uint16_t{5}, wc), // concrete sibling next to the wildcard uint32_t{9}); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, dpf::at<10>(dpf::bit::one), dpf::at<16>(uint16_t{5}, wc), uint32_t{9}); // Non-wildcard leaves + CWs + wildcard blinds are all byte-identical. EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); // Concrete parts still reconstruct. EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1, 16>, dealer.first, x), *dpf::eval_point(dpf::out<1, 16>, dealer.second, x)), uint16_t{5}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<3, 32>, dealer.first, x), *dpf::eval_point(dpf::out<3, 32>, dealer.second, x)), uint32_t{9}); } TEST_F(StressScenariosTest, DsIdentityFixedpointAtWithCmp) { using fp16 = grotto::fixedpoint<16>; uint32_t x = 0x0abcdef0u; uint32_t x0 = 0x11111111u; uint32_t x1 = x ^ x0; fp16 y = fp16::from_raw(0x00018000); reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<12>(y), fp16::from_raw(0x00030000), dpf::lt(uint64_t{1000})); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, dpf::at<12>(y), fp16::from_raw(0x00030000), dpf::lt(uint64_t{1000})); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 12>, dealer.first, x), *dpf::eval_point(dpf::out<0, 12>, dealer.second, x)), y); } TEST_F(StressScenariosTest, DsIdentityLocalCwProtocolVsDealer) { uint32_t x = 0xabcdef01u; uint32_t x0 = 0x11111111u; uint32_t x1 = x ^ x0; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<10>(dpf::bit::one), uint32_t{5}); reset_tape_roots(); PadA pads{}; dpf::local_cw_protocol proto{pads}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, take_root, proto, dpf::at<10>(dpf::bit::one), uint32_t{5}); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1, 32>, dealer.first, x), *dpf::eval_point(dpf::out<1, 32>, dealer.second, x)), uint32_t{5}); } TEST_F(StressScenariosTest, DsIdentityAesInteriorLowmcExteriorMultilevel) { uint32_t x = 0x0f1e2d3cu; uint32_t x0 = 0x13572468u; uint32_t x1 = x ^ x0; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<12>(uint8_t{9}), uint16_t{4}); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat( x0, x1, rng, dpf::at<12>(uint8_t{9}), uint16_t{4}); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 12>, ds.first, x), *dpf::eval_point(dpf::out<0, 12>, ds.second, x)), uint8_t{9}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, ds.first, x), *dpf::eval_point(dpf::out<1>, ds.second, x)), uint16_t{4}); } TEST_F(StressScenariosTest, DsIdentityWildcardCmpThenAssign) { uint32_t x = 0x00abcdefu; uint32_t x0 = 0x12345678u; uint32_t x1 = x ^ x0; const uint64_t yt = 99u, yf = 3u; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<12>(uint8_t{5}), uint32_t{7}, dpf::geq(dpf::wildcard_value{})); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, dpf::at<12>(uint8_t{5}), uint32_t{7}, dpf::geq(dpf::wildcard_value{})); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); EXPECT_FALSE(ds.first.cmp_assigned()); dpf::assign_cmp(ds.first, ds.second, yt, yf); dpf::assign_cmp(dealer.first, dealer.second, yt, yf); // Public CWs match after independent assign (same δ + identical coeffs). using WT = std::decay_t; EXPECT_TRUE(same_bytes(ds.first.value_cw().data(), dealer.first.value_cw().data(), ds.first.value_cw().size() * sizeof(typename WT::value_cw_word))); EXPECT_EQ(ds.first.cw_last(), dealer.first.cw_last()); const uint64_t mask = ds.first.cmp().mask; for (uint32_t q : {x - 1u, x, x + 1u}) { EXPECT_EQ(dpf::reconstruct(dpf::eval_point(dpf::cmp, ds.first, q), dpf::eval_point(dpf::cmp, ds.second, q)) & mask, dpf::reconstruct(dpf::eval_point(dpf::cmp, dealer.first, q), dpf::eval_point(dpf::cmp, dealer.second, q)) & mask) << "q=" << q; } EXPECT_EQ(dpf::reconstruct(ds.first.cmp_addend(), ds.second.cmp_addend()) & mask, yt); } TEST_F(StressScenariosTest, DsIdentityLocalCwComplexManyLevelsCmp) { // Real DS path with injectable local_cw_protocol on a rich pack: mixed // widths, xor+additive co-located, deepest concrete, and lt cmp. uint32_t x = 0x00abcdefu; uint32_t x0 = 0x12345678u; uint32_t x1 = x ^ x0; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<8>(dpf::bit::one), dpf::at<16>(uint8_t{3}, dpf::xor_wrapper{0xa5}), dpf::at<24>(uint16_t{7}), uint32_t{9}, dpf::lt(uint64_t{42})); reset_tape_roots(); PadA pads{}; dpf::local_cw_protocol proto{pads}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, take_root, proto, dpf::at<8>(dpf::bit::one), dpf::at<16>(uint8_t{3}, dpf::xor_wrapper{0xa5}), dpf::at<24>(uint16_t{7}), uint32_t{9}, dpf::lt(uint64_t{42})); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); } TEST_F(StressScenariosTest, DsIdentityModintMultilevelXorAdditiveCmp) { using in_t = dpf::modint<12>; constexpr auto to_int = dpf::utils::to_integral_type{}; constexpr auto from_int = dpf::utils::make_from_integral_value{}; in_t x{0x2ab}; in_t x0{0x111}; in_t x1 = from_int(static_cast( to_int(x) ^ to_int(x0))); reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<6>(uint8_t{5}, dpf::xor_wrapper{0x3c}), uint16_t{9}, dpf::lt(uint16_t{4}, uint16_t{1})); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, dpf::at<6>(uint8_t{5}, dpf::xor_wrapper{0x3c}), uint16_t{9}, dpf::lt(uint16_t{4}, uint16_t{1})); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 6>, ds.first, x), *dpf::eval_point(dpf::out<0, 6>, ds.second, x)), uint8_t{5}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<2>, ds.first, x), *dpf::eval_point(dpf::out<2>, ds.second, x)), uint16_t{9}); } TEST_F(StressScenariosTest, DsIdentityKeywordMultilevel) { using in_t = dpf::keyword<3, dpf::alphabets::hex>; constexpr auto to_int = dpf::utils::to_integral_type{}; constexpr auto from_int = dpf::utils::make_from_integral_value{}; in_t x{"abc"}; in_t x0{"a00"}; in_t x1 = from_int(static_cast( to_int(x) ^ to_int(x0))); reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<6>(uint8_t{1}), uint16_t{2}); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, dpf::at<6>(uint8_t{1}), uint16_t{2}); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 6>, ds.first, x), *dpf::eval_point(dpf::out<0, 6>, ds.second, x)), uint8_t{1}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1>, ds.first, x), *dpf::eval_point(dpf::out<1>, ds.second, x)), uint16_t{2}); } // =========================================================================== // G) eval_full coverage: multilevel prefix + deepest (multi-lane), cmp lt/geq // sweeps, classic full-tree memoizer multi-leaf, explicit memoizer. // =========================================================================== TEST_F(StressScenariosTest, MlEvalFullPrefixAndDeepestMultiLane) { uint16_t x = 0xa5c3; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint8_t{42}), dpf::at<12>(uint16_t{7})); // Prefix slot out<0,8>: compare several lanes to point eval. auto [pb0, pit0] = dpf::eval_full(dpf::out<0, 8>, k0); auto [pb1, pit1] = dpf::eval_full(dpf::out<0, 8>, k1); (void)pit0; (void)pit1; for (int L : {0, 1, x >> 8, (x >> 8) ^ 1, 0xff}) { const uint16_t q = static_cast(L << 8); EXPECT_EQ(recon(pb0[L], pb1[L]), recon(*dpf::eval_point(dpf::out<0, 8>, k0, q), *dpf::eval_point(dpf::out<0, 8>, k1, q))) << "L=" << L; } EXPECT_EQ(recon(pb0[x >> 8], pb1[x >> 8]), uint8_t{42}); // Deepest slot out<1,12>: compare several lanes to point eval. auto [db0, dit0] = dpf::eval_full(dpf::out<1, 12>, k0); auto [db1, dit1] = dpf::eval_full(dpf::out<1, 12>, k1); (void)dit0; (void)dit1; for (int L : {0, x >> 4, (x >> 4) ^ 1, 0xfff}) { const uint16_t q = static_cast(L << 4); EXPECT_EQ(recon(db0[L], db1[L]), recon(*dpf::eval_point(dpf::out<1, 12>, k0, q), *dpf::eval_point(dpf::out<1, 12>, k1, q))) << "L=" << L; } EXPECT_EQ(recon(db0[x >> 4], db1[x >> 4]), uint16_t{7}); } // Multilevel eval_full with an explicit per-slot interval memoizer // (`make_basic_interval_memoizer` stops at meta[I].tree_level). TEST_F(StressScenariosTest, MlEvalFullWithPerSlotMemoizer) { uint16_t x = 0x0abc; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint8_t{42}), dpf::at<12>(uint16_t{7})); using KT = std::decay_t; constexpr uint16_t lo = 0; constexpr uint16_t hi = 0xff; // full 8-bit prefix domain auto memo0 = dpf::make_basic_interval_memoizer(lo, hi); auto memo1 = dpf::make_basic_interval_memoizer(lo, hi); auto buf0 = dpf::make_output_buffer(dpf::out<0, 8>, k0, lo, hi); auto buf1 = dpf::make_output_buffer(dpf::out<0, 8>, k1, lo, hi); auto it0 = dpf::eval_full(dpf::out<0, 8>, k0, buf0, memo0); auto it1 = dpf::eval_full(dpf::out<0, 8>, k1, buf1, memo1); (void)it0; (void)it1; const uint16_t lane = static_cast(x >> 8); EXPECT_EQ(recon(buf0[lane], buf1[lane]), uint8_t{42}); EXPECT_EQ(recon(buf0[lane ^ 1u], buf1[lane ^ 1u]), uint8_t{0}); EXPECT_EQ(recon(buf0[0], buf1[0]), recon(*dpf::eval_point(dpf::out<0, 8>, k0, uint16_t{0}), *dpf::eval_point(dpf::out<0, 8>, k1, uint16_t{0}))); // Deepest slot with its own per-slot memoizer (stop = tree_level of I=1). constexpr uint16_t dlo = 0; constexpr uint16_t dhi = 0xfff; auto dmemo0 = dpf::make_basic_interval_memoizer(dlo, dhi); auto dmemo1 = dpf::make_basic_interval_memoizer(dlo, dhi); auto dbuf0 = dpf::make_output_buffer(dpf::out<1, 12>, k0, dlo, dhi); auto dbuf1 = dpf::make_output_buffer(dpf::out<1, 12>, k1, dlo, dhi); dpf::eval_full(dpf::out<1, 12>, k0, dbuf0, dmemo0); dpf::eval_full(dpf::out<1, 12>, k1, dbuf1, dmemo1); const uint16_t dlane = static_cast(x >> 4); EXPECT_EQ(recon(dbuf0[dlane], dbuf1[dlane]), uint16_t{7}); EXPECT_EQ(recon(dbuf0[dlane ^ 1u], dbuf1[dlane ^ 1u]), uint16_t{0}); } TEST_F(StressScenariosTest, ModintMlEvalFullMatchesPoint) { using in_t = dpf::modint<10>; in_t x{0x155}; auto [k0, k1] = dpf::make_dpf(x, dpf::at<6>(uint8_t{11}), uint16_t{22}); auto [b0, it0] = dpf::eval_full(dpf::out<0, 6>, k0); auto [b1, it1] = dpf::eval_full(dpf::out<0, 6>, k1); (void)it0; (void)it1; constexpr auto to_int = dpf::utils::to_integral_type{}; const std::size_t lane = static_cast(to_int(x) >> (10 - 6)); EXPECT_EQ(recon(b0[lane], b1[lane]), uint8_t{11}); for (int L : {0, 1, static_cast(lane), static_cast(lane ^ 1u), 0x3f}) { in_t q = dpf::utils::make_from_integral_value{}( static_cast::integral_type>( L << (10 - 6))); EXPECT_EQ(recon(b0[L], b1[L]), recon(*dpf::eval_point(dpf::out<0, 6>, k0, q), *dpf::eval_point(dpf::out<0, 6>, k1, q))) << "L=" << L; } } TEST_F(StressScenariosTest, MlLtFullCmpMatchesPointSweep) { const uint8_t alpha = 0x40; auto [k0, k1] = dpf::make_dpf(alpha, dpf::lt(uint16_t{5}, uint16_t{1})); auto b0 = dpf::eval_full(dpf::cmp, k0); auto b1 = dpf::eval_full(dpf::cmp, k1); const uint64_t mask = k0.cmp().mask; ASSERT_EQ(b0.size(), 256u); bool saw_true = false, saw_false = false; for (uint16_t q = 0; q < 256; ++q) { const auto got = recon(b0[q], b1[q]) & mask; const auto want = dpf::reconstruct( dpf::eval_point(dpf::cmp, k0, static_cast(q)), dpf::eval_point(dpf::cmp, k1, static_cast(q))) & mask; EXPECT_EQ(got, want) << "q=" << q; if (got == 5u) saw_true = true; if (got == 1u) saw_false = true; } // Both branches of the lt comparison are actually exercised. EXPECT_TRUE(saw_true); EXPECT_TRUE(saw_false); } TEST_F(StressScenariosTest, ClassicFullTreeMemoizerMultiLeafIndexed) { using dpf_type = dpf::utils::dpf_type_t>; uint8_t x = 0x11; auto [k0, k1] = dpf::make_dpf(x, uint16_t{3}, dpf::xor_wrapper{0x00ff}); auto memo0 = dpf::make_full_tree_full_memoizer(); auto memo1 = dpf::make_full_tree_full_memoizer(); auto [bufs0, its0] = dpf::eval_full<0, 1>(k0, memo0); auto [bufs1, its1] = dpf::eval_full<0, 1>(k1, memo1); (void)its0; (void)its1; auto & a0 = std::get<0>(bufs0); auto & a1 = std::get<0>(bufs1); auto & c0 = std::get<1>(bufs0); auto & c1 = std::get<1>(bufs1); ASSERT_EQ(a0.size(), 256u); for (int q = 0; q < 256; ++q) { // Index the memoized full-tree buffers directly and cross-check. EXPECT_EQ(recon(a0[q], a1[q]), (q == x) ? uint16_t{3} : uint16_t{0}) << "q=" << q; EXPECT_EQ(recon(c0[q], c1[q]), (q == x) ? dpf::xor_wrapper{0x00ff} : dpf::xor_wrapper{0}) << "q=" << q; EXPECT_EQ(recon(a0[q], a1[q]), recon(*dpf::eval_point<0>(k0, static_cast(q)), *dpf::eval_point<0>(k1, static_cast(q)))) << "q=" << q; } } // =========================================================================== // H) Packed small wildcards at a non-terminal level + full assignment; // LowMC / counter_wrapper PRG adapters. // =========================================================================== TEST_F(StressScenariosTest, MlPackedSmallWildcardAtNonTerminalAssignAll) { // Eight packed uint8 wildcards at prefix 12 (non-terminal), deepest concrete. uint16_t x = 0x4c1d; dpf::wildcard_value w; auto [k0, k1] = dpf::make_dpf(x, dpf::at<12>(w, w, w, w, w, w, w, w), uint32_t{999}); using KT = std::decay_t; static_assert(KT::meta[0].prefix == 12); EXPECT_EQ(KT::meta[0].group_id, KT::meta[7].group_id); EXPECT_NE(KT::meta[0].group_id, KT::meta[8].group_id); // Unassigned packed wildcards throw; deepest concrete still works. EXPECT_ANY_THROW((void)dpf::eval_point(dpf::out<0, 12>, k0, x)); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<8>, k0, x), *dpf::eval_point(dpf::out<8>, k1, x)), uint32_t{999}); // Complete assignment of every packed wildcard slot. const std::array want{ 1, 2, 3, 4, 5, 6, 7, 8}; const std::array shr0{ 10, 20, 30, 40, 50, 60, 70, 80}; assign_wildcard_leaf_local<0>(k0, k1, shr0[0], static_cast(want[0] - shr0[0])); assign_wildcard_leaf_local<1>(k0, k1, shr0[1], static_cast(want[1] - shr0[1])); assign_wildcard_leaf_local<2>(k0, k1, shr0[2], static_cast(want[2] - shr0[2])); assign_wildcard_leaf_local<3>(k0, k1, shr0[3], static_cast(want[3] - shr0[3])); assign_wildcard_leaf_local<4>(k0, k1, shr0[4], static_cast(want[4] - shr0[4])); assign_wildcard_leaf_local<5>(k0, k1, shr0[5], static_cast(want[5] - shr0[5])); assign_wildcard_leaf_local<6>(k0, k1, shr0[6], static_cast(want[6] - shr0[6])); assign_wildcard_leaf_local<7>(k0, k1, shr0[7], static_cast(want[7] - shr0[7])); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 12>, k0, x), *dpf::eval_point(dpf::out<0, 12>, k1, x)), want[0]); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<3, 12>, k0, x), *dpf::eval_point(dpf::out<3, 12>, k1, x)), want[3]); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<7, 12>, k0, x), *dpf::eval_point(dpf::out<7, 12>, k1, x)), want[7]); // Neighbour lane in the packed leaf is zero after assignment. const uint16_t off = flip_lane_lsb(x, 12, 16); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 12>, k0, off), *dpf::eval_point(dpf::out<0, 12>, k1, off)), uint8_t{0}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<8>, k0, x), *dpf::eval_point(dpf::out<8>, k1, x)), uint32_t{999}); } TEST_F(StressScenariosTest, MlPackedXorWildcardAtNonTerminalAssignAll) { uint16_t x = 0xabcd; dpf::wildcard_value> w; auto [k0, k1] = dpf::make_dpf(x, dpf::at<10>(w, w, w, w), uint16_t{42}); const std::array, 4> want{ dpf::xor_wrapper{0x11}, dpf::xor_wrapper{0x22}, dpf::xor_wrapper{0x33}, dpf::xor_wrapper{0x44}}; const std::array, 4> shr0{ dpf::xor_wrapper{0xaa}, dpf::xor_wrapper{0xbb}, dpf::xor_wrapper{0xcc}, dpf::xor_wrapper{0xdd}}; assign_wildcard_leaf_local<0>(k0, k1, shr0[0], want[0] ^ shr0[0]); assign_wildcard_leaf_local<1>(k0, k1, shr0[1], want[1] ^ shr0[1]); assign_wildcard_leaf_local<2>(k0, k1, shr0[2], want[2] ^ shr0[2]); assign_wildcard_leaf_local<3>(k0, k1, shr0[3], want[3] ^ shr0[3]); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 10>, k0, x), *dpf::eval_point(dpf::out<0, 10>, k1, x)), want[0]); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<3, 10>, k0, x), *dpf::eval_point(dpf::out<3, 10>, k1, x)), want[3]); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<4>, k0, x), *dpf::eval_point(dpf::out<4>, k1, x)), uint16_t{42}); } TEST_F(StressScenariosTest, DsIdentityPackedSmallWildcardAtNonTerminal) { uint32_t x = 0x00abcdefu; uint32_t x0 = 0x12345678u; uint32_t x1 = x ^ x0; dpf::wildcard_value w; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<14>(w, w, w, w, uint8_t{9}, w, w, w), uint32_t{5}); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat(x0, x1, rng, dpf::at<14>(w, w, w, w, uint8_t{9}, w, w, w), uint32_t{5}); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); // Concrete sibling in the packed group + deepest reconstruct before assign. EXPECT_EQ(recon(*dpf::eval_point(dpf::out<4, 14>, dealer.first, x), *dpf::eval_point(dpf::out<4, 14>, dealer.second, x)), uint8_t{9}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<8>, dealer.first, x), *dpf::eval_point(dpf::out<8>, dealer.second, x)), uint32_t{5}); // Full assignment on the dealer keys; DS keys are byte-identical so the // same shares complete them equivalently. const std::array want{1, 2, 3, 4, 6, 7, 8}; const std::array s0{11, 22, 33, 44, 55, 66, 77}; // Slot indices of wildcards: 0,1,2,3,5,6,7 (slot 4 is concrete). assign_wildcard_leaf_local<0>(dealer.first, dealer.second, s0[0], static_cast(want[0] - s0[0])); assign_wildcard_leaf_local<1>(dealer.first, dealer.second, s0[1], static_cast(want[1] - s0[1])); assign_wildcard_leaf_local<2>(dealer.first, dealer.second, s0[2], static_cast(want[2] - s0[2])); assign_wildcard_leaf_local<3>(dealer.first, dealer.second, s0[3], static_cast(want[3] - s0[3])); assign_wildcard_leaf_local<5>(dealer.first, dealer.second, s0[4], static_cast(want[4] - s0[4])); assign_wildcard_leaf_local<6>(dealer.first, dealer.second, s0[5], static_cast(want[5] - s0[5])); assign_wildcard_leaf_local<7>(dealer.first, dealer.second, s0[6], static_cast(want[6] - s0[6])); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 14>, dealer.first, x), *dpf::eval_point(dpf::out<0, 14>, dealer.second, x)), want[0]); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<5, 14>, dealer.first, x), *dpf::eval_point(dpf::out<5, 14>, dealer.second, x)), want[4]); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<7, 14>, dealer.first, x), *dpf::eval_point(dpf::out<7, 14>, dealer.second, x)), want[6]); // Assign the DS pair with the same shares; reconstruction must match. assign_wildcard_leaf_local<0>(ds.first, ds.second, s0[0], static_cast(want[0] - s0[0])); assign_wildcard_leaf_local<1>(ds.first, ds.second, s0[1], static_cast(want[1] - s0[1])); assign_wildcard_leaf_local<2>(ds.first, ds.second, s0[2], static_cast(want[2] - s0[2])); assign_wildcard_leaf_local<3>(ds.first, ds.second, s0[3], static_cast(want[3] - s0[3])); assign_wildcard_leaf_local<5>(ds.first, ds.second, s0[4], static_cast(want[4] - s0[4])); assign_wildcard_leaf_local<6>(ds.first, ds.second, s0[5], static_cast(want[5] - s0[5])); assign_wildcard_leaf_local<7>(ds.first, ds.second, s0[6], static_cast(want[6] - s0[6])); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 14>, ds.first, x), *dpf::eval_point(dpf::out<0, 14>, ds.second, x)), want[0]); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<7, 14>, ds.first, x), *dpf::eval_point(dpf::out<7, 14>, ds.second, x)), want[6]); } TEST_F(StressScenariosTest, DsIdentityPackedWildcardLowmcExteriorThenAssign) { uint32_t x = 0x0f1e2d3cu; uint32_t x0 = 0x13572468u; uint32_t x1 = x ^ x0; dpf::wildcard_value w; reset_tape_roots(); auto dealer = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, dpf::at<12>(w, w, w, w), uint16_t{77}); reset_tape_roots(); dpf::ds_randomness rng{take_root, {}}; auto ds = dpf::make_dpf_doerner_shelat( x0, x1, rng, dpf::at<12>(w, w, w, w), uint16_t{77}); EXPECT_TRUE(same_incr_key(dealer.first, ds.first)); EXPECT_TRUE(same_incr_key(dealer.second, ds.second)); const std::array want{9, 8, 7, 6}; const std::array s0{1, 2, 3, 4}; assign_wildcard_leaf_local<0>(ds.first, ds.second, s0[0], static_cast(want[0] - s0[0])); assign_wildcard_leaf_local<1>(ds.first, ds.second, s0[1], static_cast(want[1] - s0[1])); assign_wildcard_leaf_local<2>(ds.first, ds.second, s0[2], static_cast(want[2] - s0[2])); assign_wildcard_leaf_local<3>(ds.first, ds.second, s0[3], static_cast(want[3] - s0[3])); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 12>, ds.first, x), *dpf::eval_point(dpf::out<0, 12>, ds.second, x)), want[0]); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<3, 12>, ds.first, x), *dpf::eval_point(dpf::out<3, 12>, ds.second, x)), want[3]); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<4>, ds.first, x), *dpf::eval_point(dpf::out<4>, ds.second, x)), uint16_t{77}); } TEST_F(StressScenariosTest, PrgCounterWrapperAesClassicEvalIncrements) { using prg = dpf::prg::counter_wrapper; const auto before = prg::count(); uint8_t x = 0x2a; auto [k0, k1] = dpf::make_dpf(x, uint32_t{0x1111}); const auto after_gen = prg::count(); EXPECT_GT(after_gen, before); (void)dpf::eval_point(k0, x); (void)dpf::eval_full(k0); const auto after_eval = prg::count(); EXPECT_GT(after_eval, after_gen); EXPECT_EQ(recon(*dpf::eval_point(k0, x), *dpf::eval_point(k1, x)), uint32_t{0x1111}); } TEST_F(StressScenariosTest, PrgCounterWrapperLowmcMultilevelEvalIncrements) { using interior = dpf::prg::counter_wrapper; using exterior = dpf::prg::counter_wrapper; const auto bi = interior::count(); const auto be = exterior::count(); uint16_t x = 0x3c5a; auto [k0, k1] = dpf::make_dpf(x, dpf::at<8>(uint8_t{42}), uint16_t{7}); EXPECT_GT(interior::count(), bi); EXPECT_GT(exterior::count(), be); const auto bi2 = interior::count(); const auto be2 = exterior::count(); (void)dpf::eval_point(dpf::out<0, 8>, k0, x); (void)dpf::eval_full(dpf::out<1, 16>, k0); EXPECT_GT(interior::count(), bi2); EXPECT_GT(exterior::count(), be2); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<0, 8>, k0, x), *dpf::eval_point(dpf::out<0, 8>, k1, x)), uint8_t{42}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<1, 16>, k0, x), *dpf::eval_point(dpf::out<1, 16>, k1, x)), uint16_t{7}); } TEST_F(StressScenariosTest, PrgLowmcBothSidesPackedInterval) { uint16_t x = 0x91a2; auto [k0, k1] = dpf::make_dpf(x, dpf::at<12>(uint8_t{1}, uint8_t{2}, uint8_t{3}, uint8_t{4}), uint32_t{55}); constexpr std::size_t N = 12; const uint16_t lane = static_cast(x >> (16 - N)); const uint16_t from = static_cast(lane & ~uint16_t{0xf}); const uint16_t to = static_cast(from + 0x20u); auto [b0, it0] = dpf::eval_interval(dpf::out<0, N>, k0, from, to); auto [b1, it1] = dpf::eval_interval(dpf::out<0, N>, k1, from, to); (void)it0; (void)it1; const std::size_t idx = static_cast(lane - from); ASSERT_LT(idx, b0.size()); EXPECT_EQ(recon(b0[idx], b1[idx]), uint8_t{1}); EXPECT_EQ(recon(*dpf::eval_point(dpf::out<4>, k0, x), *dpf::eval_point(dpf::out<4>, k1, x)), uint32_t{55}); }