#include #include "dpf.hpp" #include #include #include #include #include namespace { simde__m128i g_roots[8]; int g_ri = 0; simde__m128i take_root() { return g_roots[g_ri++]; } struct Pad { 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_roots() { g_ri = 0; for (int i = 0; i < 8; ++i) g_roots[i] = simde_mm_set_epi64x(0x1111 * (i + 1), 0xA5A50000u + i * 17); } template T bare(const T & v) { return v; } template T bare(const dpf::secret_share & s) { return s.raw(); } template auto recon(const A & a, const B & b) { using T = decltype(bare(a)); // Subtractive reconstruction: party 0 minus party 1. return static_cast(bare(a) - bare(b)); } template auto ev(const Key & key, In x) { return bare(*dpf::eval_point(key, x)); } template uint64_t leaf_of(typename Key::input_type x) { dpf::utils::flip_msb_if_signed_integral(x); return static_cast(dpf::utils::get_from_node(x)); } std::size_t lcp_bits(uint64_t a, uint64_t b, std::size_t depth) { std::size_t n = 0; for (std::size_t i = 0; i < depth; ++i) { const std::size_t sh = depth - 1 - i; if (((a >> sh) & 1ull) != ((b >> sh) & 1ull)) break; ++n; } return n; } std::size_t live_through_lcp(std::size_t lcp, std::size_t depth) { return std::min(depth, lcp + 1); } HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") template void expect_prefix_words(const Key & key, const std::vector> & cws, const std::vector & advice, std::size_t live, bool leaf_live, const void * leaf, std::size_t leaf_bytes) { ASSERT_LE(live, cws.size()); ASSERT_EQ(advice.size(), cws.size()); for (std::size_t i = 0; i < live; ++i) { EXPECT_EQ(std::memcmp(&cws[i], &key.correction_word(i), sizeof(simde__m128i)), 0) << "cw " << i; EXPECT_EQ(advice[i], key.correction_advice(i)) << "advice " << i; } if (leaf_live) { EXPECT_EQ(live, Key::depth); EXPECT_EQ(std::memcmp(leaf, &key.template leaf<0>(), leaf_bytes), 0); } } template dpf::ds_randomness rng() { return {take_root, Pad{}}; } HEDLEY_PRAGMA(GCC diagnostic pop) } // namespace TEST(Geneval, PointOnTargetMatchesKeyAndEval) { using in_t = uint16_t; using out_t = uint16_t; in_t alpha = 0x0abc; in_t x0 = 0x1111; in_t x1 = static_cast(alpha ^ x0); out_t y = 0x4242; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_point(x0, x1, alpha, rng(), y); using key_t = std::decay_t; const auto id = leaf_of(alpha); const std::size_t live = live_through_lcp(lcp_bits(id, id, key_t::depth), key_t::depth); EXPECT_EQ(g.live_levels, live); EXPECT_TRUE(g.leaf_live); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, g.leaf_live, &g.leaf, sizeof(g.leaf)); auto e0 = ev(keys.first, alpha); auto e1 = ev(keys.second, alpha); ASSERT_EQ(g.party0.size(), 1u); EXPECT_EQ(g.party0[0], e0); EXPECT_EQ(g.party1[0], e1); EXPECT_EQ(recon(g.party0[0], g.party1[0]), y); } TEST(Geneval, PointDivergesAfterSharedPrefix) { using in_t = uint16_t; using out_t = uint16_t; in_t alpha = 0x0abc; in_t query = 0x0a7e; in_t x0 = 0x00ff; in_t x1 = static_cast(alpha ^ x0); out_t y = 7; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_point(x0, x1, query, rng(), y); using key_t = std::decay_t; const auto live = live_through_lcp( lcp_bits(leaf_of(alpha), leaf_of(query), key_t::depth), key_t::depth); EXPECT_LT(live, key_t::depth); EXPECT_EQ(g.live_levels, live); EXPECT_FALSE(g.leaf_live); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, false, nullptr, 0); auto e0 = ev(keys.first, query); auto e1 = ev(keys.second, query); EXPECT_EQ(recon(g.party0[0], g.party1[0]), recon(e0, e1)); EXPECT_EQ(recon(g.party0[0], g.party1[0]), out_t{0}); } TEST(Geneval, SameLeafDifferentLane) { using in_t = uint16_t; using out_t = uint8_t; in_t alpha = 0x1234; in_t query = static_cast(alpha ^ 1u); in_t x0 = 0x0101; in_t x1 = static_cast(alpha ^ x0); out_t y = 9; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); using key_t = std::decay_t; ASSERT_GT(key_t::lg_outputs_per_leaf, 0u); ASSERT_EQ(leaf_of(alpha), leaf_of(query)); reset_roots(); auto g = dpf::geneval_point(x0, x1, query, rng(), y); EXPECT_EQ(g.live_levels, key_t::depth); EXPECT_TRUE(g.leaf_live); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, true, &g.leaf, sizeof(g.leaf)); auto e0 = ev(keys.first, query); auto e1 = ev(keys.second, query); EXPECT_EQ(g.party0[0], e0); EXPECT_EQ(g.party1[0], e1); EXPECT_EQ(recon(g.party0[0], g.party1[0]), out_t{0}); } TEST(Geneval, IntervalContainsTarget) { using in_t = uint16_t; using out_t = uint16_t; in_t alpha = 1000; in_t from = 990; in_t to = 1010; in_t x0 = 0x0f0f; in_t x1 = static_cast(alpha ^ x0); out_t y = 33; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_interval(x0, x1, from, to, rng(), y); using key_t = std::decay_t; EXPECT_EQ(g.live_levels, key_t::depth); EXPECT_TRUE(g.leaf_live); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, true, &g.leaf, sizeof(g.leaf)); ASSERT_EQ(g.party0.size(), static_cast(to - from) + 1); for (in_t q = from; ; ++q) { const std::size_t i = static_cast(q - from); auto e0 = ev(keys.first, q); auto e1 = ev(keys.second, q); EXPECT_EQ(g.party0[i], e0) << q; EXPECT_EQ(g.party1[i], e1) << q; const out_t want = (q == alpha) ? y : out_t{0}; EXPECT_EQ(recon(g.party0[i], g.party1[i]), want) << q; if (q == to) break; } } TEST(Geneval, IntervalMissesAfterSharedPrefix) { using in_t = uint16_t; using out_t = uint16_t; in_t alpha = 0x8000; in_t from = 0x8100; in_t to = 0x8108; in_t x0 = 1; in_t x1 = static_cast(alpha ^ x0); out_t y = 5; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_interval(x0, x1, from, to, rng(), y); using key_t = std::decay_t; const auto a = leaf_of(alpha); const auto q = leaf_of(from); const auto live = live_through_lcp(lcp_bits(a, q, key_t::depth), key_t::depth); EXPECT_LT(g.live_levels, key_t::depth); EXPECT_EQ(g.live_levels, live); EXPECT_FALSE(g.leaf_live); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, false, nullptr, 0); for (std::size_t i = 0; i < g.party0.size(); ++i) EXPECT_EQ(recon(g.party0[i], g.party1[i]), out_t{0}) << i; } TEST(Geneval, SequenceOrderAndSharedTrie) { using in_t = uint16_t; using out_t = uint16_t; in_t alpha = 0x2222; in_t x0 = 0x00aa; in_t x1 = static_cast(alpha ^ x0); out_t y = 11; const in_t xs[] = {0x2200, alpha, 0x00ff, alpha, 0x2223}; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_sequence(x0, x1, std::begin(xs), std::end(xs), rng(), y); using key_t = std::decay_t; EXPECT_TRUE(g.leaf_live); EXPECT_EQ(g.live_levels, key_t::depth); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, true, &g.leaf, sizeof(g.leaf)); ASSERT_EQ(g.party0.size(), 5u); for (std::size_t i = 0; i < 5; ++i) { auto e0 = ev(keys.first, xs[i]); auto e1 = ev(keys.second, xs[i]); EXPECT_EQ(g.party0[i], e0); EXPECT_EQ(g.party1[i], e1); EXPECT_EQ(recon(g.party0[i], g.party1[i]), xs[i] == alpha ? y : out_t{0}); } reset_roots(); auto miss = dpf::geneval_sequence(x0, x1, xs, xs + 1, rng(), y); const auto live = live_through_lcp( lcp_bits(leaf_of(alpha), leaf_of(xs[0]), key_t::depth), key_t::depth); EXPECT_EQ(miss.live_levels, live); EXPECT_LT(miss.live_levels, key_t::depth); expect_prefix_words(keys.first, miss.correction_words, miss.correction_advice, miss.live_levels, false, nullptr, 0); EXPECT_EQ(recon(miss.party0[0], miss.party1[0]), out_t{0}); } TEST(Geneval, FullDomainUint8) { using in_t = uint8_t; using out_t = uint8_t; in_t alpha = 0x3c; in_t x0 = 0x10; in_t x1 = static_cast(alpha ^ x0); out_t y = 0x7e; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_full(x0, x1, rng(), y); using key_t = std::decay_t; EXPECT_EQ(g.party0.size(), 256u); EXPECT_EQ(g.live_levels, key_t::depth); EXPECT_TRUE(g.leaf_live); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, true, &g.leaf, sizeof(g.leaf)); for (int q = 0; q < 256; ++q) { auto e0 = ev(keys.first, static_cast(q)); auto e1 = ev(keys.second, static_cast(q)); EXPECT_EQ(g.party0[q], e0); EXPECT_EQ(g.party1[q], e1); } EXPECT_EQ(recon(g.party0[alpha], g.party1[alpha]), y); EXPECT_EQ(recon(g.party0[0], g.party1[0]), out_t{0}); } TEST(Geneval, EmptySequence) { using in_t = uint16_t; in_t alpha = 1; in_t x0 = 2; in_t x1 = static_cast(alpha ^ x0); const in_t * p = nullptr; reset_roots(); auto g = dpf::geneval_sequence(x0, x1, p, p, rng(), uint16_t{1}); EXPECT_TRUE(g.party0.empty()); EXPECT_EQ(g.live_levels, 0u); EXPECT_TRUE(g.correction_words.empty()); } TEST(Geneval, ArithPointMatchesDealerAtQuery) { using in_t = uint16_t; using out_t = uint16_t; in_t x = 0x1357; in_t x0 = 0x0100; in_t x1 = static_cast(x - x0); in_t query = 0x2000; out_t y = 99; reset_roots(); auto keys = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_point(dpf::arith_input, x0, x1, query, rng(), y); using key_t = std::decay_t; const auto live = live_through_lcp( lcp_bits(leaf_of(x), leaf_of(query), key_t::depth), key_t::depth); EXPECT_EQ(g.live_levels, live); EXPECT_LT(live, key_t::depth); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, g.leaf_live, &g.leaf, sizeof(g.leaf)); auto e0 = ev(keys.first, query); auto e1 = ev(keys.second, query); EXPECT_EQ(recon(g.party0[0], g.party1[0]), recon(e0, e1)); } TEST(Geneval, ArithPointOnSecretIsFullKey) { using in_t = uint16_t; using out_t = uint16_t; in_t x = 0x42; in_t x0 = 0x10; in_t x1 = static_cast(x - x0); out_t y = 8; reset_roots(); auto keys = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_point(dpf::arith_input, x0, x1, x, rng(), y); using key_t = std::decay_t; EXPECT_EQ(g.live_levels, key_t::depth); EXPECT_TRUE(g.leaf_live); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, true, &g.leaf, sizeof(g.leaf)); auto e0 = ev(keys.first, x); auto e1 = ev(keys.second, x); EXPECT_EQ(g.party0[0], e0); EXPECT_EQ(g.party1[0], e1); EXPECT_EQ(recon(g.party0[0], g.party1[0]), y); } TEST(Geneval, ArithIntervalAndSequence) { using in_t = uint8_t; using out_t = uint8_t; in_t x = 40; in_t x0 = 7; in_t x1 = static_cast(x - x0); out_t y = 3; reset_roots(); auto keys = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, y); reset_roots(); auto iv = dpf::geneval_interval(dpf::arith_input, x0, x1, in_t{10}, in_t{20}, rng(), y); using key_t = std::decay_t; ASSERT_EQ(iv.party0.size(), 11u); for (in_t q = 10; q <= 20; ++q) { const std::size_t i = static_cast(q - 10); auto e0 = ev(keys.first, q); auto e1 = ev(keys.second, q); EXPECT_EQ(recon(iv.party0[i], iv.party1[i]), recon(e0, e1)) << int(q); } const auto live = live_through_lcp( lcp_bits(leaf_of(x), leaf_of(in_t{10}), key_t::depth), key_t::depth); EXPECT_EQ(iv.live_levels, live); expect_prefix_words(keys.first, iv.correction_words, iv.correction_advice, iv.live_levels, iv.leaf_live, &iv.leaf, sizeof(iv.leaf)); const in_t seq[] = {1, x, 255, 2}; reset_roots(); auto sq = dpf::geneval_sequence(dpf::arith_input, x0, x1, std::begin(seq), std::end(seq), rng(), y); ASSERT_EQ(sq.party0.size(), 4u); EXPECT_TRUE(sq.leaf_live); EXPECT_EQ(sq.live_levels, key_t::depth); expect_prefix_words(keys.first, sq.correction_words, sq.correction_advice, sq.live_levels, true, &sq.leaf, sizeof(sq.leaf)); for (std::size_t i = 0; i < 4; ++i) { auto e0 = ev(keys.first, seq[i]); auto e1 = ev(keys.second, seq[i]); EXPECT_EQ(sq.party0[i], e0); EXPECT_EQ(sq.party1[i], e1); EXPECT_EQ(recon(sq.party0[i], sq.party1[i]), seq[i] == x ? y : out_t{0}); } } TEST(Geneval, ArithFullMatchesDealer) { using in_t = uint8_t; using out_t = uint8_t; in_t x = 40; in_t x0 = 7; in_t x1 = static_cast(x - x0); out_t y = 3; reset_roots(); auto keys = dpf::make_dpf(x, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_full(dpf::arith_input, x0, x1, rng(), y); using key_t = std::decay_t; EXPECT_EQ(g.party0.size(), 256u); EXPECT_EQ(g.live_levels, key_t::depth); EXPECT_TRUE(g.leaf_live); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, true, &g.leaf, sizeof(g.leaf)); EXPECT_EQ(recon(g.party0[x], g.party1[x]), y); for (int q = 0; q < 256; ++q) { auto e0 = ev(keys.first, static_cast(q)); auto e1 = ev(keys.second, static_cast(q)); EXPECT_EQ(g.party0[q], e0); EXPECT_EQ(g.party1[q], e1); } } TEST(Geneval, DoernerShelatKeyAgreesOnLivePrefix) { using in_t = uint16_t; using out_t = uint16_t; in_t alpha = 0x55aa; in_t query = 0x5500; in_t x0 = 0x1234; in_t x1 = static_cast(alpha ^ x0); out_t y = 1; reset_roots(); HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") dpf::ds_randomness ds_rng{take_root, Pad{}}; HEDLEY_PRAGMA(GCC diagnostic pop) auto ds = dpf::make_dpf_doerner_shelat(x0, x1, ds_rng, y); reset_roots(); auto g = dpf::geneval_point(x0, x1, query, rng(), y); using key_t = std::decay_t; const auto live = live_through_lcp( lcp_bits(leaf_of(alpha), leaf_of(query), key_t::depth), key_t::depth); EXPECT_EQ(g.live_levels, live); EXPECT_GT(live, 0u); EXPECT_LT(live, key_t::depth); expect_prefix_words(ds.first, g.correction_words, g.correction_advice, g.live_levels, false, nullptr, 0); } struct PadB { uint64_t n = 99; simde__m128i block() { auto v = simde_mm_set_epi64x(static_cast(n * 7), static_cast(n ^ 0x5a5a)); n += 3; return v; } uint8_t bit() { return static_cast((n++ >> 2) & 1u); } }; template std::size_t best_live(const Key &, In alpha, const std::vector & qs) { using key_t = Key; const auto a = leaf_of(alpha); std::size_t best = 0; for (In q : qs) best = std::max(best, lcp_bits(a, leaf_of(q), key_t::depth)); return live_through_lcp(best, key_t::depth); } template std::vector span_inclusive(T from, T to) { std::vector qs; for (T q = from; ; ++q) { qs.push_back(q); if (q == to) break; } return qs; } TEST(Geneval, EdgesZeroMaxAndSinglePointShapesAgree) { using in_t = uint16_t; using out_t = uint16_t; in_t alpha = 0; in_t x0 = 0xffff; in_t x1 = static_cast(alpha ^ x0); out_t y = 0x1111; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); using key_t = std::decay_t; for (in_t q : {in_t{0}, in_t{1}, in_t{0x8000}, in_t{0xffff}}) { reset_roots(); auto pt = dpf::geneval_point(x0, x1, q, rng(), y); reset_roots(); auto iv = dpf::geneval_interval(x0, x1, q, q, rng(), y); const in_t seq[] = {q}; reset_roots(); auto sq = dpf::geneval_sequence(x0, x1, std::begin(seq), std::end(seq), rng(), y); const auto live = live_through_lcp( lcp_bits(leaf_of(alpha), leaf_of(q), key_t::depth), key_t::depth); EXPECT_EQ(pt.live_levels, live) << q; EXPECT_EQ(iv.live_levels, live) << q; EXPECT_EQ(sq.live_levels, live) << q; EXPECT_EQ(pt.correction_words.size(), key_t::depth); EXPECT_EQ(std::memcmp(pt.correction_words.data(), iv.correction_words.data(), key_t::depth * sizeof(simde__m128i)), 0) << q; EXPECT_EQ(std::memcmp(pt.correction_words.data(), sq.correction_words.data(), key_t::depth * sizeof(simde__m128i)), 0) << q; EXPECT_EQ(pt.correction_advice, iv.correction_advice); EXPECT_EQ(pt.correction_advice, sq.correction_advice); EXPECT_EQ(recon(pt.party0[0], pt.party1[0]), q == alpha ? y : out_t{0}); EXPECT_EQ(recon(iv.party0[0], iv.party1[0]), recon(pt.party0[0], pt.party1[0])); EXPECT_EQ(recon(sq.party0[0], sq.party1[0]), recon(pt.party0[0], pt.party1[0])); if (live < key_t::depth) { EXPECT_NE(std::memcmp(&pt.correction_words[live], &keys.first.correction_word(live), sizeof(simde__m128i)), 0) << q; auto e0 = ev(keys.first, q); EXPECT_NE(pt.party0[0], e0) << q; } } } TEST(Geneval, XorSplitAndPadStreamDoNotChangeLiveWords) { using in_t = uint16_t; using out_t = uint32_t; in_t alpha = 0x0f0f; in_t query = 0x0e00; out_t y = 0xabcdef01u; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); using key_t = std::decay_t; const auto live = live_through_lcp( lcp_bits(leaf_of(alpha), leaf_of(query), key_t::depth), key_t::depth); ASSERT_GT(live, 0u); ASSERT_LT(live, key_t::depth); auto check = [&](in_t x0, auto pad, const char * name) { in_t x1 = static_cast(alpha ^ x0); reset_roots(); HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") dpf::ds_randomness r{take_root, pad}; HEDLEY_PRAGMA(GCC diagnostic pop) auto g = dpf::geneval_point(x0, x1, query, r, y); EXPECT_EQ(g.live_levels, live) << name; expect_prefix_words(keys.first, g.correction_words, g.correction_advice, live, false, nullptr, 0); EXPECT_NE(std::memcmp(&g.correction_words[live], &keys.first.correction_word(live), sizeof(simde__m128i)), 0) << name; }; check(0, Pad{}, "share 0"); check(alpha, Pad{}, "share alpha"); check(0x1234, Pad{}, "mixed share"); check(0x1234, PadB{}, "other pads"); } TEST(Geneval, IntervalLiveFollowsLongestPrefixNotTheFirstPoint) { using in_t = uint16_t; using out_t = uint16_t; in_t alpha = 0x0f08; in_t from = 0x0000; in_t to = 0x0f00; in_t x0 = 0x00ff; in_t x1 = static_cast(alpha ^ x0); out_t y = 4; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_interval(x0, x1, from, to, rng(), y); using key_t = std::decay_t; const auto only_from = live_through_lcp( lcp_bits(leaf_of(alpha), leaf_of(from), key_t::depth), key_t::depth); const auto want = best_live(keys.first, alpha, span_inclusive(from, to)); EXPECT_GT(want, only_from); EXPECT_FALSE(g.leaf_live); EXPECT_EQ(g.live_levels, want); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, false, nullptr, 0); if (g.live_levels < key_t::depth) { EXPECT_NE(std::memcmp(&g.correction_words[g.live_levels], &keys.first.correction_word(g.live_levels), sizeof(simde__m128i)), 0); } else { EXPECT_NE(std::memcmp(&g.leaf, &keys.first.template leaf<0>(), sizeof(g.leaf)), 0); } ASSERT_EQ(g.party0.size(), static_cast(to - from) + 1); for (std::size_t i = 0; i < g.party0.size(); ++i) EXPECT_EQ(recon(g.party0[i], g.party1[i]), out_t{0}) << i; } TEST(Geneval, PartialLeafExcludesTargetButKeepsItsWord) { using in_t = uint16_t; using out_t = uint8_t; in_t alpha = 0x1000; in_t from = 0x1001; in_t to = 0x1005; in_t x0 = 7; in_t x1 = static_cast(alpha ^ x0); out_t y = 0x5a; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); using key_t = std::decay_t; ASSERT_GT(key_t::lg_outputs_per_leaf, 0u); ASSERT_EQ(leaf_of(alpha), leaf_of(from)); ASSERT_NE(alpha, from); reset_roots(); auto g = dpf::geneval_interval(x0, x1, from, to, rng(), y); EXPECT_TRUE(g.leaf_live); EXPECT_EQ(g.live_levels, key_t::depth); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, true, &g.leaf, sizeof(g.leaf)); for (in_t q = from; q <= to; ++q) { const std::size_t i = static_cast(q - from); EXPECT_EQ(g.party0[i], ev(keys.first, q)); EXPECT_EQ(g.party1[i], ev(keys.second, q)); EXPECT_EQ(recon(g.party0[i], g.party1[i]), out_t{0}); } } TEST(Geneval, DepthOneBitOutput) { using in_t = uint8_t; using out_t = dpf::bit; in_t alpha = 0x80; in_t other = 0x7f; in_t same_leaf = 0x81; in_t x0 = 0x3c; in_t x1 = static_cast(alpha ^ x0); out_t y = dpf::bit::one; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); using key_t = std::decay_t; ASSERT_EQ(key_t::depth, 1u); reset_roots(); auto on = dpf::geneval_point(x0, x1, alpha, rng(), y); EXPECT_EQ(on.live_levels, 1u); EXPECT_TRUE(on.leaf_live); expect_prefix_words(keys.first, on.correction_words, on.correction_advice, 1, true, &on.leaf, sizeof(on.leaf)); EXPECT_EQ(recon(on.party0[0], on.party1[0]), y); EXPECT_EQ(on.party0[0], ev(keys.first, alpha)); reset_roots(); auto lane = dpf::geneval_point(x0, x1, same_leaf, rng(), y); EXPECT_TRUE(lane.leaf_live); EXPECT_EQ(recon(lane.party0[0], lane.party1[0]), out_t{false}); EXPECT_EQ(lane.party0[0], ev(keys.first, same_leaf)); reset_roots(); auto off = dpf::geneval_point(x0, x1, other, rng(), y); EXPECT_EQ(off.live_levels, 1u); EXPECT_FALSE(off.leaf_live); expect_prefix_words(keys.first, off.correction_words, off.correction_advice, 1, false, nullptr, 0); EXPECT_EQ(recon(off.party0[0], off.party1[0]), out_t{false}); EXPECT_NE(std::memcmp(&off.leaf, &keys.first.template leaf<0>(), sizeof(off.leaf)), 0); } TEST(Geneval, FullMatchesWholeIntervalAndRejectsHugeDomain) { using in_t = uint8_t; using out_t = uint8_t; in_t alpha = 255; in_t x0 = 0; in_t x1 = alpha; out_t y = 9; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto full = dpf::geneval_full(x0, x1, rng(), y); reset_roots(); auto iv = dpf::geneval_interval(x0, x1, in_t{0}, in_t{255}, rng(), y); ASSERT_EQ(full.party0.size(), iv.party0.size()); EXPECT_EQ(full.correction_advice, iv.correction_advice); EXPECT_EQ(std::memcmp(full.correction_words.data(), iv.correction_words.data(), full.correction_words.size() * sizeof(simde__m128i)), 0); for (std::size_t i = 0; i < full.party0.size(); ++i) { EXPECT_EQ(full.party0[i], iv.party0[i]); EXPECT_EQ(full.party1[i], iv.party1[i]); } EXPECT_EQ(recon(full.party0[255], full.party1[255]), y); EXPECT_EQ(recon(full.party0[0], full.party1[0]), out_t{0}); EXPECT_THROW((dpf::geneval_full(uint32_t{1}, uint32_t{2}, rng(), uint32_t{1})), std::length_error); EXPECT_THROW((dpf::geneval_interval(in_t{5}, in_t{1}, in_t{4}, in_t{3}, rng(), y)), std::invalid_argument); } TEST(Geneval, SequencePermutationKeepsWordsAndDuplicates) { using in_t = uint16_t; using out_t = uint16_t; in_t alpha = 0x4444; in_t x0 = 0x0001; in_t x1 = static_cast(alpha ^ x0); out_t y = 6; const in_t fwd[] = {0x1000, 0x0100, alpha, 0x1000}; const in_t rev[] = {0x1000, alpha, 0x0100, 0x1000}; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto a = dpf::geneval_sequence(x0, x1, std::begin(fwd), std::end(fwd), rng(), y); reset_roots(); auto b = dpf::geneval_sequence(x0, x1, std::begin(rev), std::end(rev), rng(), y); using key_t = std::decay_t; EXPECT_EQ(a.live_levels, key_t::depth); EXPECT_EQ(b.live_levels, key_t::depth); EXPECT_EQ(std::memcmp(a.correction_words.data(), b.correction_words.data(), key_t::depth * sizeof(simde__m128i)), 0); EXPECT_EQ(a.correction_advice, b.correction_advice); EXPECT_EQ(recon(a.party0[2], a.party1[2]), y); EXPECT_EQ(recon(a.party0[0], a.party1[0]), recon(a.party0[3], a.party1[3])); EXPECT_EQ(recon(b.party0[1], b.party1[1]), y); EXPECT_EQ(a.party0[0], ev(keys.first, fwd[0])); EXPECT_EQ(b.party0[2], ev(keys.first, rev[2])); } TEST(Geneval, SignedPointIntervalAndCrossZero) { using in_t = int16_t; using out_t = int16_t; in_t alpha = -100; in_t x0 = 1; in_t x1 = static_cast(alpha ^ x0); out_t y = -25; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); using key_t = std::decay_t; reset_roots(); auto on = dpf::geneval_point(x0, x1, alpha, rng(), y); EXPECT_EQ(on.live_levels, key_t::depth); EXPECT_TRUE(on.leaf_live); expect_prefix_words(keys.first, on.correction_words, on.correction_advice, on.live_levels, true, &on.leaf, sizeof(on.leaf)); EXPECT_EQ(on.party0[0], ev(keys.first, alpha)); EXPECT_EQ(on.party1[0], ev(keys.second, alpha)); EXPECT_EQ(recon(on.party0[0], on.party1[0]), y); in_t far = 100; reset_roots(); auto off = dpf::geneval_point(x0, x1, far, rng(), y); const auto live = live_through_lcp( lcp_bits(leaf_of(alpha), leaf_of(far), key_t::depth), key_t::depth); EXPECT_EQ(off.live_levels, live); EXPECT_LT(live, key_t::depth); expect_prefix_words(keys.first, off.correction_words, off.correction_advice, live, false, nullptr, 0); EXPECT_EQ(recon(off.party0[0], off.party1[0]), out_t{0}); in_t from = -3; in_t to = 3; reset_roots(); auto iv = dpf::geneval_interval(x0, x1, from, to, rng(), y); ASSERT_EQ(iv.party0.size(), 7u); EXPECT_FALSE(iv.leaf_live); for (in_t q = from; ; ++q) { const std::size_t i = static_cast(q - from); EXPECT_EQ(recon(iv.party0[i], iv.party1[i]), out_t{0}) << q; EXPECT_EQ(recon(iv.party0[i], iv.party1[i]), recon(ev(keys.first, q), ev(keys.second, q))) << q; if (q == to) break; } in_t near = -101; reset_roots(); auto around = dpf::geneval_interval(x0, x1, in_t{-102}, in_t{-98}, rng(), y); EXPECT_TRUE(around.leaf_live); EXPECT_EQ(around.live_levels, key_t::depth); expect_prefix_words(keys.first, around.correction_words, around.correction_advice, around.live_levels, true, &around.leaf, sizeof(around.leaf)); for (in_t q = -102; q <= -98; ++q) { const std::size_t i = static_cast(q - in_t{-102}); EXPECT_EQ(around.party0[i], ev(keys.first, q)) << q; EXPECT_EQ(recon(around.party0[i], around.party1[i]), q == alpha ? y : out_t{0}); } (void)near; } TEST(Geneval, SignedFullAndArithFull) { using in_t = int8_t; using out_t = int8_t; in_t alpha = -5; in_t x_xor0 = 3; in_t x_xor1 = static_cast(alpha ^ x_xor0); out_t y = -9; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_full(x_xor0, x_xor1, rng(), y); ASSERT_EQ(g.party0.size(), 256u); constexpr auto to_int = dpf::utils::to_integral_type{}; for (int q = -128; q <= 127; ++q) { in_t v = static_cast(q); const std::size_t i = static_cast(to_int(v)); EXPECT_EQ(g.party0[i], ev(keys.first, v)) << q; EXPECT_EQ(g.party1[i], ev(keys.second, v)) << q; } in_t secret = -20; in_t a0 = 100; in_t a1 = static_cast(secret - a0); ASSERT_EQ(static_cast(a0 + a1), secret); reset_roots(); auto wkeys = dpf::make_dpf(secret, dpf::root_sampler_t{take_root}, y); reset_roots(); auto w = dpf::geneval_full(dpf::arith_input, a0, a1, rng(), y); ASSERT_EQ(w.party0.size(), 256u); EXPECT_EQ(w.live_levels, std::decay_t::depth); expect_prefix_words(wkeys.first, w.correction_words, w.correction_advice, w.live_levels, true, &w.leaf, sizeof(w.leaf)); for (int q = -128; q <= 127; ++q) { in_t v = static_cast(q); const std::size_t i = static_cast(to_int(v)); EXPECT_EQ(w.party0[i], ev(wkeys.first, v)) << q; EXPECT_EQ(w.party1[i], ev(wkeys.second, v)) << q; } EXPECT_EQ(recon(w.party0[static_cast(to_int(secret))], w.party1[static_cast(to_int(secret))]), y); } TEST(Geneval, ArithShareOverflowAndWrappingInterval) { using in_t = uint8_t; using out_t = uint8_t; in_t secret = 10; in_t x0 = 200; in_t x1 = 66; ASSERT_EQ(static_cast(x0 + x1), secret); out_t y = 17; reset_roots(); auto keys = dpf::make_dpf(secret, dpf::root_sampler_t{take_root}, y); reset_roots(); auto on = dpf::geneval_point(dpf::arith_input, x0, x1, secret, rng(), y); using key_t = std::decay_t; EXPECT_EQ(on.live_levels, key_t::depth); EXPECT_TRUE(on.leaf_live); expect_prefix_words(keys.first, on.correction_words, on.correction_advice, on.live_levels, true, &on.leaf, sizeof(on.leaf)); EXPECT_EQ(recon(on.party0[0], on.party1[0]), y); in_t query = 250; reset_roots(); auto off = dpf::geneval_point(dpf::arith_input, x0, x1, query, rng(), y); const auto live = live_through_lcp( lcp_bits(leaf_of(secret), leaf_of(query), key_t::depth), key_t::depth); EXPECT_EQ(off.live_levels, live); expect_prefix_words(keys.first, off.correction_words, off.correction_advice, off.live_levels, off.leaf_live, &off.leaf, sizeof(off.leaf)); EXPECT_EQ(recon(off.party0[0], off.party1[0]), recon(ev(keys.first, query), ev(keys.second, query))); in_t from = 250; in_t to = 10; EXPECT_THROW((dpf::geneval_interval(dpf::arith_input, x0, x1, from, to, rng(), y)), std::invalid_argument); from = 250; to = 255; reset_roots(); auto iv = dpf::geneval_interval(dpf::arith_input, x0, x1, from, to, rng(), y); ASSERT_EQ(iv.party0.size(), 6u); for (in_t q = from; ; ++q) { const std::size_t i = static_cast(static_cast(q - from)); EXPECT_EQ(recon(iv.party0[i], iv.party1[i]), recon(ev(keys.first, q), ev(keys.second, q))) << int(q); if (q == to) break; } } TEST(Geneval, XorWrapperOutput) { using in_t = uint16_t; using out_t = dpf::xor_wrapper; in_t alpha = 0x0102; in_t query = 0x0180; in_t x0 = 0x00f0; in_t x1 = static_cast(alpha ^ x0); out_t y{0x01020304u}; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto on = dpf::geneval_point(x0, x1, alpha, rng(), y); using key_t = std::decay_t; EXPECT_TRUE(on.leaf_live); expect_prefix_words(keys.first, on.correction_words, on.correction_advice, on.live_levels, true, &on.leaf, sizeof(on.leaf)); EXPECT_EQ(recon(on.party0[0], on.party1[0]), y); EXPECT_EQ(on.party0[0], ev(keys.first, alpha)); reset_roots(); auto off = dpf::geneval_point(x0, x1, query, rng(), y); const auto live = live_through_lcp( lcp_bits(leaf_of(alpha), leaf_of(query), key_t::depth), key_t::depth); EXPECT_EQ(off.live_levels, live); EXPECT_LT(live, key_t::depth); expect_prefix_words(keys.first, off.correction_words, off.correction_advice, live, false, nullptr, 0); EXPECT_EQ(recon(off.party0[0], off.party1[0]), out_t{0}); } TEST(Geneval, SignedRegressionsFromTheCornerPass) { using in_t = int8_t; using out_t = int8_t; in_t alpha = -40; in_t x0 = 3; in_t x1 = static_cast(alpha ^ x0); out_t y = -7; // An inverted signed range must not wrap the long way around. EXPECT_THROW((dpf::geneval_interval(in_t{2}, in_t{1}, in_t{4}, in_t{-3}, rng(), y)), std::invalid_argument); // [INT_MIN, INT_MAX] is numeric order; full is bit-pattern order. // The words are the same trie. Each input's share matches either way. reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto full = dpf::geneval_full(x0, x1, rng(), y); reset_roots(); auto iv = dpf::geneval_interval(x0, x1, std::numeric_limits::min(), std::numeric_limits::max(), rng(), y); ASSERT_EQ(full.party0.size(), 256u); ASSERT_EQ(iv.party0.size(), 256u); EXPECT_EQ(std::memcmp(full.correction_words.data(), iv.correction_words.data(), full.correction_words.size() * sizeof(simde__m128i)), 0); EXPECT_EQ(full.correction_advice, iv.correction_advice); constexpr auto to_int = dpf::utils::to_integral_type{}; for (int q = -128; q <= 127; ++q) { in_t v = static_cast(q); const std::size_t bit = static_cast(to_int(v)); const std::size_t num = static_cast(q - (-128)); EXPECT_EQ(full.party0[bit], iv.party0[num]) << q; EXPECT_EQ(full.party1[bit], iv.party1[num]) << q; EXPECT_EQ(full.party0[bit], ev(keys.first, v)) << q; } // Negative secret, additive shares that wrap through the signed MSB. in_t secret = -20; in_t a0 = 100; in_t a1 = static_cast(secret - a0); ASSERT_EQ(static_cast(a0 + a1), secret); in_t query = 60; reset_roots(); auto akeys = dpf::make_dpf(secret, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_point(dpf::arith_input, a0, a1, query, rng(), y); EXPECT_EQ(recon(g.party0[0], g.party1[0]), recon(ev(akeys.first, query), ev(akeys.second, query))); expect_prefix_words(akeys.first, g.correction_words, g.correction_advice, g.live_levels, g.leaf_live, &g.leaf, sizeof(g.leaf)); } TEST(Geneval, CmpEmptyRangeOpensNothing) { reset_roots(); const uint8_t ends[] = {0}; auto g = dpf::geneval_cmp(uint8_t{1}, uint8_t{2}, ends, ends, rng(), uint64_t{1}); EXPECT_TRUE(g.party0.empty()); EXPECT_TRUE(g.party1.empty()); EXPECT_EQ(g.live_levels, 0u); EXPECT_TRUE(g.value_cw.empty()); } TEST(Geneval, CmpMatchesDoernerShelatKeyAndGtPredicate) { using in_t = uint8_t; const in_t alpha = 40; const in_t x0 = 0x11; const in_t x1 = static_cast(alpha ^ x0); const uint64_t beta = 7; const std::vector ends{0, 1, 10, 40, 200, 255, 40}; reset_roots(); auto keys = dpf::make_dpf_doerner_shelat(x0, x1, rng(), dpf::gt(beta)); reset_roots(); auto g = dpf::geneval_cmp(x0, x1, ends.begin(), ends.end(), rng(), beta); using key_t = std::decay_t; EXPECT_EQ(g.live_levels, key_t::depth); ASSERT_EQ(g.correction_words.size(), key_t::depth); ASSERT_EQ(g.value_cw.size(), key_t::depth); for (std::size_t level = 0; level < key_t::depth; ++level) { EXPECT_EQ(std::memcmp(&g.correction_words[level], &keys.first.correction_word(level), sizeof(simde__m128i)), 0) << level; EXPECT_EQ(g.correction_advice[level], keys.first.correction_advice(level)) << level; EXPECT_EQ(g.value_cw[level], keys.first.value_cw(level)) << level; } EXPECT_EQ(g.cw_last, keys.first.cw_last()); EXPECT_EQ(g.addend0, keys.first.cmp_addend().raw()); EXPECT_EQ(g.addend1, keys.second.cmp_addend().raw()); EXPECT_EQ((g.addend0 + g.addend1) & g.mask, beta); ASSERT_EQ(g.party0.size(), ends.size()); for (std::size_t i = 0; i < ends.size(); ++i) { const auto e0 = dpf::eval_point(dpf::cmp, keys.first, ends[i]); const auto e1 = dpf::eval_point(dpf::cmp, keys.second, ends[i]); EXPECT_EQ(g.party0[i], e0.raw()) << int(ends[i]); EXPECT_EQ(g.party1[i], e1.raw()) << int(ends[i]); const uint64_t opened = (g.party0[i] + g.party1[i]) & g.mask; EXPECT_EQ(opened, ends[i] > alpha ? beta : 0u) << int(ends[i]); } } TEST(Geneval, CmpSignedPayloadAndDomainMax) { using in_t = int16_t; const in_t alpha = -3; const in_t x0 = 9; const in_t x1 = static_cast(alpha ^ x0); const uint64_t beta = 5; const std::vector ends{-100, -3, -2, 0, 4, 32767}; reset_roots(); auto g = dpf::geneval_cmp(x0, x1, ends.begin(), ends.end(), rng(), dpf::gt(beta)); EXPECT_EQ(g.live_levels, 16u); EXPECT_EQ(g.value_cw.size(), g.live_levels); for (std::size_t i = 0; i < ends.size(); ++i) { const uint64_t opened = (g.party0[i] + g.party1[i]) & g.mask; EXPECT_EQ(opened, ends[i] > alpha ? beta : 0u) << ends[i]; } const in_t top0 = 1; const in_t top = std::numeric_limits::max(); const in_t top1 = static_cast(top ^ top0); const std::vector all{std::numeric_limits::min(), in_t{0}, top}; reset_roots(); auto trivial = dpf::geneval_cmp(top0, top1, all.begin(), all.end(), rng(), uint64_t{1}); for (std::size_t i = 0; i < all.size(); ++i) EXPECT_EQ((trivial.party0[i] + trivial.party1[i]) & trivial.mask, 0u) << all[i]; } TEST(Geneval, CmpLtIsTheComplementOfTheStrictUpperSet) { using in_t = uint8_t; const in_t alpha = 10; const in_t x0 = 3; const in_t x1 = static_cast(alpha ^ x0); const std::vector ends{0, 10, 11, 255}; reset_roots(); auto g = dpf::geneval_cmp(x0, x1, ends.begin(), ends.end(), rng(), dpf::lt(uint64_t{4})); for (std::size_t i = 0; i < ends.size(); ++i) { const uint64_t opened = (g.party0[i] + g.party1[i]) & g.mask; EXPECT_EQ(opened, ends[i] < alpha ? 4u : 0u) << int(ends[i]); } } TEST(Geneval, DoernerShelatOnTargetSharesMatch) { using in_t = uint16_t; using out_t = uint16_t; const in_t alpha = 0x55aa; const in_t x0 = 0x1234; const in_t x1 = static_cast(alpha ^ x0); const out_t y = 0x9f3c; reset_roots(); HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") dpf::ds_randomness ds_rng{take_root, Pad{}}; HEDLEY_PRAGMA(GCC diagnostic pop) auto ds = dpf::make_dpf_doerner_shelat(x0, x1, ds_rng, y); reset_roots(); auto g = dpf::geneval_point(x0, x1, alpha, rng(), y); using key_t = std::decay_t; EXPECT_EQ(g.live_levels, key_t::depth); EXPECT_TRUE(g.leaf_live); expect_prefix_words(ds.first, g.correction_words, g.correction_advice, g.live_levels, true, &g.leaf, sizeof(g.leaf)); EXPECT_EQ(g.party0[0], ev(ds.first, alpha)); EXPECT_EQ(g.party1[0], ev(ds.second, alpha)); EXPECT_EQ(recon(g.party0[0], g.party1[0]), y); } TEST(Geneval, WideLiveFrontierMatchesDealer) { using in_t = uint16_t; using out_t = uint16_t; const in_t alpha = 0x00ff; const in_t x0 = 0x0f0f; const in_t x1 = static_cast(alpha ^ x0); const out_t y = 0xabcd; const in_t from = 0; const in_t to = 0x00ff; reset_roots(); auto keys = dpf::make_dpf(alpha, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_interval(x0, x1, from, to, rng(), y); using key_t = std::decay_t; EXPECT_EQ(g.live_levels, key_t::depth); EXPECT_TRUE(g.leaf_live); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, true, &g.leaf, sizeof(g.leaf)); ASSERT_EQ(g.party0.size(), static_cast(to - from) + 1); for (in_t q = from; ; ++q) { const std::size_t i = static_cast(q - from); EXPECT_EQ(g.party0[i], ev(keys.first, q)) << q; EXPECT_EQ(g.party1[i], ev(keys.second, q)) << q; const out_t opened = recon(g.party0[i], g.party1[i]); EXPECT_EQ(opened, q == alpha ? y : out_t{0}) << q; if (q == to) break; } } TEST(Geneval, CmpLeqGeqNonzeroElseAndDomainMin) { using in_t = uint8_t; const in_t alpha = 10; const in_t x0 = 3; const in_t x1 = static_cast(alpha ^ x0); const std::vector ends{0, 9, 10, 11, 255}; auto check = [&](auto spec, auto pred) { auto spec_ds = spec; auto spec_g = spec; reset_roots(); HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") dpf::ds_randomness ds_rng{take_root, Pad{}}; HEDLEY_PRAGMA(GCC diagnostic pop) auto ds = dpf::make_dpf_doerner_shelat(x0, x1, ds_rng, spec_ds); reset_roots(); auto g = dpf::geneval_cmp(x0, x1, ends.begin(), ends.end(), rng(), spec_g); using key_t = std::decay_t; EXPECT_EQ(g.live_levels, key_t::depth); EXPECT_EQ(g.correction_words.size(), key_t::depth); for (std::size_t level = 0; level < key_t::depth; ++level) { EXPECT_EQ(std::memcmp(&g.correction_words[level], &ds.first.correction_word(level), sizeof(simde__m128i)), 0) << level; EXPECT_EQ(g.correction_advice[level], ds.first.correction_advice(level)); } for (std::size_t i = 0; i < ends.size(); ++i) { const uint64_t opened = (g.party0[i] + g.party1[i]) & g.mask; const uint64_t from_key = (dpf::eval_point(dpf::cmp, ds.first, ends[i]).raw() + dpf::eval_point(dpf::cmp, ds.second, ends[i]).raw()) & ds.first.cmp().mask; EXPECT_EQ(opened, from_key) << int(ends[i]); EXPECT_EQ(opened, pred(ends[i])) << int(ends[i]); } }; check(dpf::leq(uint64_t{5}, uint64_t{2}), [&](in_t e) { return e <= alpha ? uint64_t{5} : uint64_t{2}; }); check(dpf::geq(uint64_t{5}, uint64_t{2}), [&](in_t e) { return e >= alpha ? uint64_t{5} : uint64_t{2}; }); using wide = int16_t; const wide amin = std::numeric_limits::min(); const wide w0 = 1; const wide w1 = static_cast(amin ^ w0); const std::vector wends{amin, static_cast(amin + 1), wide{-1}, wide{0}, std::numeric_limits::max()}; reset_roots(); auto g = dpf::geneval_cmp(w0, w1, wends.begin(), wends.end(), rng(), dpf::gt(uint64_t{3})); for (std::size_t i = 0; i < wends.size(); ++i) { const uint64_t opened = (g.party0[i] + g.party1[i]) & g.mask; EXPECT_EQ(opened, wends[i] > amin ? 3u : 0u) << wends[i]; } } TEST(Geneval, ArithSignedMsbAndCarryAcrossPowerOfTwo) { using in_t = int8_t; using out_t = int8_t; const in_t secret = -20; const in_t a0 = 100; const in_t a1 = static_cast(secret - a0); ASSERT_EQ(static_cast(a0 + a1), secret); const out_t y = -7; reset_roots(); auto keys = dpf::make_dpf(secret, dpf::root_sampler_t{take_root}, y); reset_roots(); auto g = dpf::geneval_point(dpf::arith_input, a0, a1, secret, rng(), y); using key_t = std::decay_t; EXPECT_EQ(g.live_levels, key_t::depth); EXPECT_TRUE(g.leaf_live); expect_prefix_words(keys.first, g.correction_words, g.correction_advice, g.live_levels, true, &g.leaf, sizeof(g.leaf)); EXPECT_EQ(g.party0[0], ev(keys.first, secret)); EXPECT_EQ(g.party1[0], ev(keys.second, secret)); EXPECT_EQ(recon(g.party0[0], g.party1[0]), y); // Carry across 2^k: shares that wrap the unsigned modulus. using u8 = uint8_t; const u8 usecret = 7; const u8 x0 = 200; const u8 x1 = static_cast(usecret - x0); ASSERT_EQ(static_cast(x0 + x1), usecret); const u8 uy = 9; reset_roots(); auto ukeys = dpf::make_dpf(usecret, dpf::root_sampler_t{take_root}, uy); reset_roots(); auto iv = dpf::geneval_interval(dpf::arith_input, x0, x1, u8{0}, u8{3}, rng(), uy); ASSERT_EQ(iv.party0.size(), 4u); for (u8 q = 0; q <= 3; ++q) { const std::size_t i = static_cast(q); EXPECT_EQ(iv.party0[i], ev(ukeys.first, q)) << int(q); EXPECT_EQ(iv.party1[i], ev(ukeys.second, q)) << int(q); EXPECT_EQ(recon(iv.party0[i], iv.party1[i]), recon(ev(ukeys.first, q), ev(ukeys.second, q))) << int(q); } using ukey_t = std::decay_t; EXPECT_EQ(iv.live_levels, live_through_lcp( lcp_bits(leaf_of(usecret), leaf_of(u8{0}), ukey_t::depth), ukey_t::depth)); } TEST(Geneval, ArithDoernerShelatAndCmpMatchDealer) { using in_t = uint8_t; const in_t secret = 40; const in_t a0 = 250; const in_t a1 = static_cast(secret - a0); ASSERT_EQ(static_cast(a0 + a1), secret); const uint64_t beta = 7; const std::vector ends{0, 1, 10, 40, 200, 255}; reset_roots(); auto dealer = dpf::make_dpf(secret, dpf::root_sampler_t{take_root}, dpf::gt(beta)); reset_roots(); auto ds = dpf::make_dpf_doerner_shelat(dpf::arith_input, a0, a1, rng(), dpf::gt(beta)); using key_t = std::decay_t; for (std::size_t level = 0; level < key_t::depth; ++level) { EXPECT_EQ(std::memcmp(&ds.first.correction_word(level), &dealer.first.correction_word(level), sizeof(simde__m128i)), 0) << level; EXPECT_EQ(ds.first.correction_advice(level), dealer.first.correction_advice(level)) << level; EXPECT_EQ(ds.first.value_cw(level), dealer.first.value_cw(level)) << level; } reset_roots(); auto g = dpf::geneval_cmp(dpf::arith_input, a0, a1, ends.begin(), ends.end(), rng(), beta); EXPECT_EQ(g.live_levels, key_t::depth); for (std::size_t i = 0; i < ends.size(); ++i) { const uint64_t opened = (g.party0[i] + g.party1[i]) & g.mask; EXPECT_EQ(opened, ends[i] > secret ? beta : 0u) << int(ends[i]); } }