#include #include "dpf.hpp" #include #include namespace { using in_type = std::uint8_t; using out_type = std::uint64_t; template out_type raw_of(const T & v) { if constexpr (dpf::is_secret_share_v>) return static_cast(v.raw()); else return static_cast(v); } out_type open(out_type a, out_type b) { return static_cast(a - b); } template std::vector interval_alone(const Key & key, in_type from, in_type to) { auto buf = dpf::make_output_buffer_for_interval(key, from, to); dpf::eval_interval(key, from, to, buf); std::vector v; v.reserve(buf.size()); for (std::size_t i = 0; i < buf.size(); ++i) v.push_back(raw_of(buf[i])); return v; } } // namespace TEST(Cohort, SamePointGenAndPointEval) { const in_type alpha = 7; const std::vector beta{1, 2, 3, 4, 5}; auto [c0, c1] = dpf::make_dpf_cohort(alpha, beta.begin(), beta.end()); ASSERT_EQ(c0.size(), beta.size()); std::vector y0, y1; c0.eval_point(alpha, y0); c1.eval_point(alpha, y1); ASSERT_EQ(y0.size(), beta.size()); for (std::size_t k = 0; k < beta.size(); ++k) { EXPECT_EQ(open(y0[k], y1[k]), beta[k]); EXPECT_EQ(y0[k], raw_of(*dpf::eval_point(c0.keys()[k], alpha))); EXPECT_EQ(y1[k], raw_of(*dpf::eval_point(c1.keys()[k], alpha))); } for (std::size_t k = 0; k < beta.size(); ++k) { const in_type other = static_cast(alpha + 1 + k); std::vector z0, z1; c0.eval_point(other, z0); c1.eval_point(other, z1); EXPECT_EQ(open(z0[k], z1[k]), 0u); EXPECT_EQ(z0[k], raw_of(*dpf::eval_point(c0.keys()[k], other))); } } TEST(Cohort, IntervalInterleavesLeaves) { const in_type alpha = 9; const std::vector beta{4, 8, 15, 16}; auto [c0, c1] = dpf::make_dpf_cohort(alpha, beta.begin(), beta.end()); const in_type from = 0, to = 15; std::vector y0, y1; c0.eval_interval(from, to, y0); c1.eval_interval(from, to, y1); using key0 = std::decay_t; constexpr std::size_t opl = key0::outputs_per_leaf; const std::size_t n = beta.size(); const auto alone0 = interval_alone(c0.keys()[0], from, to); ASSERT_EQ(y0.size(), alone0.size() * n); ASSERT_EQ(alone0.size() % opl, 0u); for (std::size_t k = 0; k < n; ++k) { const auto a0 = interval_alone(c0.keys()[k], from, to); const auto a1 = interval_alone(c1.keys()[k], from, to); ASSERT_EQ(a0.size(), alone0.size()); for (std::size_t i = 0; i < a0.size(); ++i) { const std::size_t node = i / opl; const std::size_t lane = i % opl; const std::size_t slot = (node * n + k) * opl + lane; EXPECT_EQ(y0[slot], a0[i]); EXPECT_EQ(y1[slot], a1[i]); const in_type x = static_cast(from + i); const out_type opened = open(a0[i], a1[i]); if (x == alpha) EXPECT_EQ(opened, beta[k]); else EXPECT_EQ(opened, 0u); } } } TEST(Cohort, SequenceRecipeAndInnerProduct) { const in_type alpha = 5; const std::vector beta{3, 9, 1}; auto [c0, c1] = dpf::make_dpf_cohort(alpha, beta.begin(), beta.end()); const std::vector points{1, 5, 5, 20, 40}; const std::size_t n = beta.size(); std::vector y0, y1; c0.eval_sequence(points.begin(), points.end(), y0); c1.eval_sequence(points.begin(), points.end(), y1); ASSERT_EQ(y0.size(), points.size() * n); using key0 = std::decay_t; auto recipe = dpf::make_sequence_recipe( points.begin(), points.end()); std::vector r0, r1; c0.eval_sequence(recipe, r0); c1.eval_sequence(recipe, r1); for (std::size_t q = 0; q < points.size(); ++q) { for (std::size_t k = 0; k < n; ++k) { const std::size_t slot = dpf::cohort_index(q, k, n); const auto e0 = raw_of(*dpf::eval_point(c0.keys()[k], points[q])); const auto e1 = raw_of(*dpf::eval_point(c1.keys()[k], points[q])); EXPECT_EQ(y0[slot], e0); EXPECT_EQ(y1[slot], e1); EXPECT_EQ(r0[slot], e0); EXPECT_EQ(r1[slot], e1); if (points[q] == alpha) EXPECT_EQ(open(e0, e1), beta[k]); else EXPECT_EQ(open(e0, e1), 0u); } } std::vector w(points.size()); for (std::size_t q = 0; q < w.size(); ++q) w[q] = q + 3; const auto s0 = c0.eval_sequence_inner_product(recipe, w); const auto s1 = c1.eval_sequence_inner_product(points.begin(), points.end(), w); ASSERT_EQ(s0.size(), n); for (std::size_t k = 0; k < n; ++k) { out_type acc0 = 0, acc1 = 0; for (std::size_t q = 0; q < points.size(); ++q) { acc0 = static_cast(acc0 + y0[dpf::cohort_index(q, k, n)] * w[q]); acc1 = static_cast(acc1 + y1[dpf::cohort_index(q, k, n)] * w[q]); } EXPECT_EQ(s0[k], acc0); EXPECT_EQ(s1[k], acc1); EXPECT_EQ(open(s0[k], s1[k]), beta[k] * (w[1] + w[2])); } } TEST(Cohort, IntervalInnerProduct) { const in_type alpha = 4; const std::vector beta{6, 7}; auto [c0, c1] = dpf::make_dpf_cohort(alpha, beta.begin(), beta.end()); const in_type from = 0, to = 15; const auto alone = interval_alone(c0.keys()[0], from, to); std::vector w(alone.size()); for (std::size_t i = 0; i < w.size(); ++i) w[i] = (i * 5u) + 1u; const auto a0 = c0.eval_interval_inner_product(from, to, w); const auto a1 = c1.eval_interval_inner_product(from, to, w); for (std::size_t k = 0; k < beta.size(); ++k) { const auto b0 = interval_alone(c0.keys()[k], from, to); const auto b1 = interval_alone(c1.keys()[k], from, to); out_type e0 = 0; out_type e1 = 0; for (std::size_t i = 0; i < w.size(); ++i) { e0 = static_cast(e0 + b0[i] * w[i]); e1 = static_cast(e1 + b1[i] * w[i]); } EXPECT_EQ(a0[k], e0); EXPECT_EQ(a1[k], e1); // Same weights against one-key batched leaf inner product. auto m0 = dpf::make_basic_interval_memoizer(c0.keys()[k], from, to); auto m1 = dpf::make_basic_interval_memoizer(c1.keys()[k], from, to); const auto ip0 = dpf::eval_inner_product(c0.keys()[k], from, to, w, m0); const auto ip1 = dpf::eval_inner_product(c1.keys()[k], from, to, w, m1); EXPECT_EQ(raw_of(ip0), a0[k]); EXPECT_EQ(raw_of(ip1), a1[k]); EXPECT_EQ(open(a0[k], a1[k]), beta[k] * w[alpha - from]); } } TEST(Cohort, InterleaveLeavesMatchesSequenceLayout) { // Sequence cohort layout is `out[q * n + k]`, the same order // `interleave_leaves` writes for whole leaf values. const in_type alpha = 6; const std::vector beta{2, 3, 5}; auto [c0, c1] = dpf::make_dpf_cohort(alpha, beta.begin(), beta.end()); (void)c1; const std::vector pts{1, 6, 10, 20}; const std::size_t n = beta.size(); std::vector cohort; c0.eval_sequence(pts.begin(), pts.end(), cohort); std::vector> per_key(n); std::vector ptrs(n); for (std::size_t k = 0; k < n; ++k) { per_key[k].resize(pts.size()); for (std::size_t q = 0; q < pts.size(); ++q) per_key[k][q] = raw_of(*dpf::eval_point(c0.keys()[k], pts[q])); ptrs[k] = per_key[k].data(); } std::vector interleaved(pts.size() * n); dpf::interleave_leaves( interleaved.data(), ptrs.data(), n, pts.size()); ASSERT_EQ(interleaved.size(), cohort.size()); for (std::size_t i = 0; i < interleaved.size(); ++i) EXPECT_EQ(interleaved[i], cohort[i]) << "slot " << i; } TEST(Cohort, HalfTreeSamePoint) { using ht = dpf::prg::aes128_ccr; const in_type alpha = 3; const std::vector beta{11, 13, 17, 19}; auto [c0, c1] = dpf::make_dpf_cohort( alpha, beta.begin(), beta.end()); std::vector y0, y1; c0.eval_point(alpha, y0); c1.eval_point(alpha, y1); for (std::size_t k = 0; k < beta.size(); ++k) { EXPECT_EQ(open(y0[k], y1[k]), beta[k]); EXPECT_EQ(y0[k], raw_of(*dpf::eval_point(c0.keys()[k], alpha))); } }