#include #include #include #include #include "dpf.hpp" namespace { // Full-block payload ⇒ outputs_per_leaf = 1, so a single public F matches the // leaf correction word share-for-share. using input_t = std::uint8_t; using output_t = simde_uint128; constexpr std::size_t n = 256; output_t leaf_cw_as_output(const dpf::utils::dpf_type_t & key) { using exterior = typename std::decay_t::exterior_node; return dpf::extract_leaf(key.template leaf<0>(), 0); } // party_key wrappers from make_dpf template output_t leaf_cw_as_output(const Key & key) { using exterior = typename Key::exterior_node; return dpf::extract_leaf(key.template leaf<0>(), 0); } output_t make_payload(std::uint64_t lo) { return output_t{lo}; } } // namespace TEST(LeafLater, MatchesCorrectedImmediately) { const input_t alpha = 42; const output_t beta = make_payload(7); auto [k0, k1] = dpf::make_dpf(alpha, beta); // Public leaf CW (same on both keys); applying it recovers the immediate path. const output_t F = leaf_cw_as_output(k0); EXPECT_EQ(F, leaf_cw_as_output(k1)); auto full0 = dpf::eval_full(k0); auto full1 = dpf::eval_full(k1); std::vector buf0(n), buf1(n); std::vector c0(n), c1(n); dpf::eval_full(buf0, c0, k0, dpf::leaf_later{}); dpf::eval_full(buf1, c1, k1, dpf::leaf_later{}); dpf::apply_leaf_correction(buf0, c0, F); dpf::apply_leaf_correction(buf1, c1, F); auto it0 = std::begin(full0.second); auto it1 = std::begin(full1.second); for (std::size_t i = 0; i < n; ++i, ++it0, ++it1) { const auto imm0 = dpf::detail_walk::group_value(*it0); const auto imm1 = dpf::detail_walk::group_value(*it1); EXPECT_EQ(buf0[i], imm0) << "party0 @" << i; EXPECT_EQ(buf1[i], imm1) << "party1 @" << i; EXPECT_EQ(buf0[i] - buf1[i], imm0 - imm1) << i; const auto got = buf0[i] - buf1[i]; if (i == alpha) EXPECT_EQ(got, beta); else EXPECT_EQ(got, output_t{0}); } } TEST(LeafLater, ApplyAfterRotateMatchesCorrectThenRotate) { const input_t alpha = 10; const output_t beta = make_payload(99); const std::size_t s = 32; auto [k0, k1] = dpf::make_dpf(alpha, beta); const output_t F = leaf_cw_as_output(k0); auto full0 = dpf::eval_full(k0); auto full1 = dpf::eval_full(k1); std::vector early0(n), early1(n); { auto a = std::begin(full0.second); auto b = std::begin(full1.second); for (std::size_t i = 0; i < n; ++i, ++a, ++b) { early0[i] = dpf::detail_walk::group_value(*a); early1[i] = dpf::detail_walk::group_value(*b); } } early0 = dpf::cyclic_shift(early0, s); early1 = dpf::cyclic_shift(early1, s); std::vector late0(n), late1(n); std::vector c0(n), c1(n); dpf::eval_full(late0, c0, k0, dpf::leaf_later{}); dpf::eval_full(late1, c1, k1, dpf::leaf_later{}); dpf::cyclic_shift_pair(late0, c0, s); dpf::cyclic_shift_pair(late1, c1, s); dpf::apply_leaf_correction(late0, c0, F); dpf::apply_leaf_correction(late1, c1, F); for (std::size_t i = 0; i < n; ++i) { EXPECT_EQ(late0[i], early0[i]) << i; EXPECT_EQ(late1[i], early1[i]) << i; } } TEST(LeafLater, DuoramShapedUpdate) { // Unit at r, rotate by i*−r, then apply a public F built from the leaf CW // of a key keyed at the online message (same as correcting immediately for // that message). Neighbours stay unchanged. const input_t r = 10; const input_t i_star = 42; const std::size_t shift = static_cast(i_star - r); const output_t message = make_payload(7); auto [k0, k1] = dpf::make_dpf(r, message); const output_t F = leaf_cw_as_output(k0); std::vector mem0(n), mem1(n); mem0[i_star] = make_payload(100); std::vector v0(n), v1(n); std::vector t0(n), t1(n); dpf::eval_full_add_into(v0, t0, k0, dpf::leaf_later{}, dpf::rotate{shift}); dpf::eval_full_add_into(v1, t1, k1, dpf::leaf_later{}, dpf::rotate{shift}); dpf::apply_leaf_correction(v0, t0, F); dpf::apply_leaf_correction(v1, t1, F); for (std::size_t i = 0; i < n; ++i) { mem0[i] = mem0[i] + v0[i]; mem1[i] = mem1[i] + v1[i]; } EXPECT_EQ(mem0[i_star] - mem1[i_star], make_payload(100) + message); EXPECT_EQ(mem0[r] - mem1[r], output_t{0}); EXPECT_EQ(mem0[0] - mem1[0], output_t{0}); }