/// @file dpf/geneval.hpp /// @brief Fused generation and evaluation (Doerner–Shelat on the eval trie). /// @details `make_dpf` / `make_dpf_doerner_shelat` build a reusable key, then /// `eval_*` walks it. `geneval_*` does both at once: one correction /// word per level, opened from the XOR-reduction of the nodes the /// public query actually expands. While the secret path's parent is /// still in that trie the word matches the reusable key byte for /// byte (same roots, same Beaver tape). After the path leaves, the /// word is uniform and later outputs still reconstruct — off-path /// nodes are identical across the two parties, so a dummy word /// cancels. /// /// Default calls take XOR shares of the point. Tagged with /// `arith_input`, the point is the ring sum of the two shares; path /// bits are opened by a carry chain inside the local CW protocol so /// the words match `make_dpf(x0 + x1)` at the caller's query. /// /// `geneval_cmp` is the comparison-channel form. The value-correction /// word is a function of the secret path at every level, so the walk /// stays live for the whole depth and the opened words match a /// Doerner–Shelat comparison key. Prefix shares are /// `eval_point(cmp, ...)` at each endpoint. Piecewise-cubic evaluation /// on top of that is `grotto::geneval_offset_horner`. /// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors) /// @license Released under a GNU General Public v2.0 (GPLv2) license; /// see [LICENSE.md](@ref license) for details. #ifndef LIBDPF_INCLUDE_DPF_GENEVAL_HPP__ #define LIBDPF_INCLUDE_DPF_GENEVAL_HPP__ #include #include #include #include #include #include #include #include #include #include #include "hedley/hedley.h" #include "simde/simde/x86/avx2.h" #include "dpf/aligned_allocator.hpp" #include "dpf/doerner_shelat.hpp" #include "dpf/eval_target.hpp" #include "dpf/leaf_node.hpp" namespace dpf { /// @brief Shares and the correction words opened along the query trie. /// @details `correction_words[i]` / `correction_advice[i]` match a reusable key at /// the same target for every `i < live_levels`. `leaf_live` means the /// target's leaf was in the trie, so `leaf` is that key's leaf word. /// @tparam Output output /// @tparam Leaf leaf template struct geneval_result { std::vector party0; std::vector party1; HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") std::vector> correction_words; HEDLEY_PRAGMA(GCC diagnostic pop) std::vector correction_advice; std::size_t live_levels = 0; bool leaf_live = false; Leaf leaf{}; }; namespace detail { template HEDLEY_ALWAYS_INLINE HEDLEY_NO_THROW T geneval_mod_add(T a, T b) noexcept { using U = std::make_unsigned_t; U sum = static_cast(static_cast(a) + static_cast(b)); T out; std::memcpy(&out, &sum, sizeof(out)); return out; } template T geneval_flipped(T x) { utils::flip_msb_if_signed_integral(x); return x; } /// @brief Leaf-node id of an already MSB-flipped input. The id is the high /// `depth` bits; the low `lg(outputs_per_leaf)` bits select the lane. /// @tparam Dpf dpf /// @param x the `x` /// @return Leaf-node id of an already MSB-flipped input template uint64_t geneval_leaf_id(typename Dpf::input_type x) { return static_cast(utils::get_from_node(x)); } inline uint64_t geneval_prefix(uint64_t leaf, std::size_t depth, std::size_t bits) { if (bits == 0) return 0; if (bits >= depth) return leaf; return leaf >> (depth - bits); } inline bool geneval_any_prefix(const std::vector & leaves, std::size_t depth, uint64_t id, std::size_t bits) { if (leaves.empty()) return false; if (bits == 0) return true; const std::size_t sh = depth - bits; const uint64_t lo = (sh >= 64) ? 0 : (id << sh); auto it = std::lower_bound(leaves.begin(), leaves.end(), lo); if (it == leaves.end()) return false; return geneval_prefix(*it, depth, bits) == id; } template geneval_result geneval_empty_result() { geneval_result out; std::memset(&out.leaf, 0, sizeof(out.leaf)); return out; } template auto geneval_run(bool arith, bool arith_out, InputT x0, InputT x1, const std::vector & queries, RootSampler & root_sampler, PadRng & pads, OutputT y0, OutputT y1 = OutputT{}) { static_assert(std::is_integral_v, "geneval input shares are an integral domain"); static_assert(!dpf::is_wildcard_v, "geneval output is concrete; assign a wildcard leaf on a key"); static_assert(utils::bitlength_of_v <= 64, "geneval leaf ids are 64-bit"); using dpf_type = utils::dpf_type_t; using node = typename dpf_type::interior_node; HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") using leaf_node = leaf_node_t; HEDLEY_PRAGMA(GCC diagnostic pop) using outputs_tuple = std::tuple; constexpr std::size_t depth = dpf_type::depth; if (queries.empty()) return geneval_empty_result(); if (queries.size() > (std::size_t{1} << 22)) throw std::length_error("geneval query is too large"); local_cw_protocol proto{pads}; InputT x0c = x0; InputT x1c = x1; proto.encode_walk_shares(x0c, x1c, arith); const InputT alpha = utils::xor_input_shares(x0c, x1c); std::vector flipped; flipped.reserve(queries.size()); std::vector leaves; leaves.reserve(queries.size()); for (const InputT & q : queries) { InputT fq = geneval_flipped(q); flipped.push_back(fq); leaves.push_back(geneval_leaf_id(fq)); } std::vector unique_leaves = leaves; std::sort(unique_leaves.begin(), unique_leaves.end()); unique_leaves.erase(std::unique(unique_leaves.begin(), unique_leaves.end()), unique_leaves.end()); if (unique_leaves.size() > (std::size_t{1} << 20)) throw std::length_error("geneval trie is too large"); const uint64_t secret_leaf = geneval_leaf_id(alpha); constexpr auto to_int = utils::to_integral_type{}; using tree = dpf::tree_traits; HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") node roots[2]; HEDLEY_PRAGMA(GCC diagnostic pop) tree::root_init(roots, [&]() -> node { return static_cast(root_sampler()); }); const node root0 = roots[0]; const node root1 = roots[1]; struct slot { uint64_t id; node s0; node s1; }; std::vector frontier; frontier.push_back(slot{0, root0, root1}); HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") geneval_result result; HEDLEY_PRAGMA(GCC diagnostic pop) std::memset(&result.leaf, 0, sizeof(result.leaf)); result.correction_words.reserve(depth); result.correction_advice.reserve(depth); auto mask = dpf_type::msb_mask; bool still_live = true; for (std::size_t level = 0; level < depth; ++level, mask >>= 1) { const uint8_t bit0 = static_cast(!!(to_int(mask) & to_int(x0c))); const uint8_t bit1 = static_cast(!!(to_int(mask) & to_int(x1c))); const uint64_t parent_id = geneval_prefix(secret_leaf, depth, level); const bool is_last = tree::is_last_level(level, depth); node L0 = simde_mm_setzero_si128(); node R0 = simde_mm_setzero_si128(); node L1 = simde_mm_setzero_si128(); node R1 = simde_mm_setzero_si128(); bool level_live = false; struct exp { uint64_t id; node s0, s1, L0, R0, L1, R1; }; std::vector exps; exps.reserve(frontier.size()); for (const slot & n : frontier) { if (n.id == parent_id) level_live = true; const auto c0 = tree::expand(n.s0, is_last); const auto c1 = tree::expand(n.s1, is_last); L0 = ds_xor(L0, c0[0]); R0 = ds_xor(R0, c0[1]); L1 = ds_xor(L1, c1[0]); R1 = ds_xor(R1, c1[1]); exps.push_back(exp{n.id, n.s0, n.s1, c0[0], c0[1], c1[0], c1[1]}); } node cw; uint8_t advice; if (still_live && level_live) { auto blinds = proto.prepare_level(L0, R0, bit0, L1, R1, bit1); auto opened = proto.open_cw(blinds); cw = opened.first; advice = opened.second; if constexpr (tree::is_half_tree) { if (!is_last) advice = 0; } ++result.live_levels; } else { still_live = false; cw = pads.block(); if constexpr (tree::is_half_tree) { if (!is_last) { advice = 0; } else { const uint8_t t0 = static_cast(pads.bit() & 1u); const uint8_t t1 = static_cast(pads.bit() & 1u); advice = static_cast((t1 << 1) | t0); } } else { const uint8_t t0 = static_cast(pads.bit() & 1u); const uint8_t t1 = static_cast(pads.bit() & 1u); advice = static_cast((t1 << 1) | t0); } } result.correction_words.push_back(cw); result.correction_advice.push_back(advice); const node cw0 = tree::pack_cw(cw, advice, false, is_last); const node cw1 = tree::pack_cw(cw, advice, true, is_last); const std::size_t child_bits = level + 1; std::vector next; next.reserve(exps.size() * 2); for (const exp & e : exps) { const uint64_t left = e.id << 1; const uint64_t right = left | 1ull; if (geneval_any_prefix(unique_leaves, depth, left, child_bits)) { next.push_back(slot{left, dpf::xor_if_lo_bit(e.L0, cw0, e.s0), dpf::xor_if_lo_bit(e.L1, cw0, e.s1)}); } if (geneval_any_prefix(unique_leaves, depth, right, child_bits)) { next.push_back(slot{right, dpf::xor_if_lo_bit(e.R0, cw1, e.s0), dpf::xor_if_lo_bit(e.R1, cw1, e.s1)}); } } frontier = std::move(next); } result.leaf_live = geneval_any_prefix(unique_leaves, depth, secret_leaf, depth); if (result.leaf_live) { const slot * on = nullptr; for (const slot & n : frontier) { if (n.id == secret_leaf) { on = &n; break; } } if (on == nullptr) throw std::logic_error("geneval: secret leaf missing from trie"); if (arith_out) { const uint8_t t0 = static_cast(dpf::get_lo_bit(on->s0)); const uint8_t t1 = static_cast(dpf::get_lo_bit(on->s1)); const std::size_t lane = static_cast(to_int(alpha)); result.leaf = proto.template open_arith_leaf( dpf::unset_lo_2bits(on->s0), dpf::unset_lo_2bits(on->s1), t0, t1, y0, y1, std::size_t{0}, lane); } else { const bool sign0 = dpf::get_lo_bit(on->s0); auto built = dpf::make_leaves(alpha, dpf::unset_lo_2bits(on->s0), dpf::unset_lo_2bits(on->s1), sign0, std::size_t{0}, y0); result.leaf = std::get<0>(built.first.first); } } result.party0.reserve(flipped.size()); result.party1.reserve(flipped.size()); for (std::size_t i = 0; i < flipped.size(); ++i) { const uint64_t id = leaves[i]; const slot * n = nullptr; for (const slot & s : frontier) { if (s.id == id) { n = &s; break; } } if (n == nullptr) throw std::logic_error("geneval: query leaf missing from trie"); auto share0 = dpf_type::template traverse_exterior<0>(n->s0, result.leaf); auto share1 = dpf_type::template traverse_exterior<0>(n->s1, result.leaf); const auto lane = static_cast(to_int(flipped[i])); result.party0.push_back(extract_leaf(share0, lane)); result.party1.push_back(extract_leaf(share1, lane)); } return result; } template auto geneval_run(bool arith, InputT x0, InputT x1, const std::vector & queries, RootSampler & root_sampler, PadRng & pads, OutputT y) { return geneval_run(arith, false, x0, x1, queries, root_sampler, pads, y, OutputT{}); } template auto geneval_run(InputT x0, InputT x1, const std::vector & queries, RootSampler & root_sampler, PadRng & pads, OutputT y) { return geneval_run(false, false, x0, x1, queries, root_sampler, pads, y, OutputT{}); } template InputT geneval_from_bits(uint64_t bits) { using U = std::make_unsigned_t; U u = static_cast(bits); InputT out; std::memcpy(&out, &u, sizeof(out)); return out; } template bool geneval_out_of_order(InputT from, InputT to) { // Numeric order. An unsigned compare of a signed value treats a negative // `from` as larger than a positive `to`, and would reject `[-1, 1]`. if constexpr (std::is_signed_v) return from > to; else return utils::to_integral_type{}(from) > utils::to_integral_type{}(to); } template std::vector geneval_full_domain() { constexpr std::size_t bitlen = utils::bitlength_of_v; if (bitlen > 20) throw std::length_error("geneval_full domain is too large"); const uint64_t n = uint64_t{1} << bitlen; std::vector qs(static_cast(n)); // Index `i` is the input's bit pattern, including the sign bit. A // narrowing cast of `i` to a signed type is implementation-defined. for (uint64_t i = 0; i < n; ++i) qs[static_cast(i)] = geneval_from_bits(i); return qs; } template std::vector geneval_inclusive(InputT from, InputT to) { if (geneval_out_of_order(from, to)) { throw std::invalid_argument("geneval_interval: from > to"); } std::vector qs; InputT q = from; const InputT one = utils::make_from_integral_value{}(1); for (;;) { qs.push_back(q); if (q == to) break; q = geneval_mod_add(q, one); if (qs.size() > (std::size_t{1} << 22)) throw std::length_error("geneval_interval is too large"); } return qs; } } // namespace detail /// @name Point geneval /// @tparam InteriorPRG PRG that expands interior nodes. Defaults to `dpf::prg::aes128` /// @tparam ExteriorPRG PRG that expands the root. Defaults to `InteriorPRG` /// @tparam InputT input domain type /// @tparam OutputT output type /// @tparam RootSampler sampler for the Doerner–Shelat root seed /// @tparam PadRng pad stream for the Doerner–Shelat protocol /// @param x0 party 0's share of the secret point /// @param x1 party 1's share of the secret point /// @param query the query point /// @param rng the Doerner–Shelat randomness tapes /// @{ /// @brief The secret point is `x0 XOR x1`. /// @param x0 party 0's share of the secret point /// @param x1 party 1's share of the secret point /// @param query the query point /// @param rng the Doerner–Shelat randomness tapes /// @param y the payload /// @return the opened shares and correction words template HEDLEY_WARN_UNUSED_RESULT auto geneval_point(InputT x0, InputT x1, InputT query, ds_randomness rng, OutputT y) { return detail::geneval_run(false, false, x0, x1, std::vector{query}, rng.root, rng.pad, y, OutputT{}); } /// @brief The secret point is `x0 + x1`. /// @param x0 party 0's share of the secret point /// @param x1 party 1's share of the secret point /// @param query the query point /// @param rng the Doerner–Shelat randomness tapes /// @param y the payload /// @return the opened shares and correction words template HEDLEY_WARN_UNUSED_RESULT auto geneval_point(arith_input_t, InputT x0, InputT x1, InputT query, ds_randomness rng, OutputT y) { return detail::geneval_run(true, false, x0, x1, std::vector{query}, rng.root, rng.pad, y, OutputT{}); } /// @brief XOR-index shares, additively shared payload `y0 + y1 = β`. /// @param x0 party 0's share of the secret point /// @param x1 party 1's share of the secret point /// @param query the query point /// @param rng the Doerner–Shelat randomness tapes /// @param y0 party 0's share of the payload /// @param y1 party 1's share of the payload /// @return the opened shares and correction words template HEDLEY_WARN_UNUSED_RESULT auto geneval_point(arith_output_t, InputT x0, InputT x1, InputT query, ds_randomness rng, OutputT y0, OutputT y1) { return detail::geneval_run(false, true, x0, x1, std::vector{query}, rng.root, rng.pad, y0, y1); } /// @brief Additive index and additive payload shares. /// @param x0 party 0's share of the secret point /// @param x1 party 1's share of the secret point /// @param query the query point /// @param rng the Doerner–Shelat randomness tapes /// @param y0 party 0's share of the payload /// @param y1 party 1's share of the payload /// @return the opened shares and correction words template HEDLEY_WARN_UNUSED_RESULT auto geneval_point(arith_input_t, arith_output_t, InputT x0, InputT x1, InputT query, ds_randomness rng, OutputT y0, OutputT y1) { return detail::geneval_run(true, true, x0, x1, std::vector{query}, rng.root, rng.pad, y0, y1); } /// @} /// @brief Geneval on the inclusive interval `[from, to]`. /// @tparam InteriorPRG PRG that expands interior nodes. Defaults to `dpf::prg::aes128` /// @tparam ExteriorPRG PRG that expands the root. Defaults to `InteriorPRG` /// @tparam InputT input domain type /// @tparam OutputT output type /// @tparam RootSampler sampler for the Doerner–Shelat root seed /// @tparam PadRng pad stream for the Doerner–Shelat protocol /// @param x0 the `x0` /// @param x1 the `x1` /// @param from the inclusive start of the range /// @param to the `to` /// @param rng the Doerner–Shelat randomness tapes /// @param y the `y` /// @return Geneval on the inclusive interval `[from, to]` template HEDLEY_WARN_UNUSED_RESULT auto geneval_interval(InputT x0, InputT x1, InputT from, InputT to, ds_randomness rng, OutputT y) { return detail::geneval_run(false, x0, x1, detail::geneval_inclusive(from, to), rng.root, rng.pad, y); } template HEDLEY_WARN_UNUSED_RESULT auto geneval_interval(arith_input_t, InputT x0, InputT x1, InputT from, InputT to, ds_randomness rng, OutputT y) { return detail::geneval_run(true, x0, x1, detail::geneval_inclusive(from, to), rng.root, rng.pad, y); } /// @brief Geneval on the whole domain. Refuses a domain above 2^20 inputs. /// @tparam InteriorPRG PRG that expands interior nodes. Defaults to `dpf::prg::aes128` /// @tparam ExteriorPRG PRG that expands the root. Defaults to `InteriorPRG` /// @tparam InputT input domain type /// @tparam OutputT output type /// @tparam RootSampler sampler for the Doerner–Shelat root seed /// @tparam PadRng pad stream for the Doerner–Shelat protocol /// @param x0 the `x0` /// @param x1 the `x1` /// @param rng the Doerner–Shelat randomness tapes /// @param y the `y` /// @return Geneval on the whole domain template HEDLEY_WARN_UNUSED_RESULT auto geneval_full(InputT x0, InputT x1, ds_randomness rng, OutputT y) { return detail::geneval_run(false, x0, x1, detail::geneval_full_domain(), rng.root, rng.pad, y); } template HEDLEY_WARN_UNUSED_RESULT auto geneval_full(arith_input_t, InputT x0, InputT x1, ds_randomness rng, OutputT y) { return detail::geneval_run(true, x0, x1, detail::geneval_full_domain(), rng.root, rng.pad, y); } /// @brief Geneval on a public sequence, in the order given. /// @tparam InteriorPRG PRG that expands interior nodes. Defaults to `dpf::prg::aes128` /// @tparam ExteriorPRG PRG that expands the root. Defaults to `InteriorPRG` /// @tparam InputT input domain type /// @tparam OutputT output type /// @tparam RootSampler sampler for the Doerner–Shelat root seed /// @tparam PadRng pad stream for the Doerner–Shelat protocol /// @tparam ForwardIterator forward iterator type /// @param x0 the `x0` /// @param x1 the `x1` /// @param begin the iterator to the first query /// @param end the iterator past the last query /// @param rng the Doerner–Shelat randomness tapes /// @param y the `y` /// @return Geneval on a public sequence, in the order given template HEDLEY_WARN_UNUSED_RESULT auto geneval_sequence(InputT x0, InputT x1, ForwardIterator begin, ForwardIterator end, ds_randomness rng, OutputT y) { std::vector qs(begin, end); return detail::geneval_run(false, x0, x1, std::move(qs), rng.root, rng.pad, y); } template HEDLEY_WARN_UNUSED_RESULT auto geneval_sequence(arith_input_t, InputT x0, InputT x1, ForwardIterator begin, ForwardIterator end, ds_randomness rng, OutputT y) { std::vector qs(begin, end); return detail::geneval_run(true, x0, x1, std::move(qs), rng.root, rng.pad, y); } /// @brief Opened comparison key material and one prefix share per endpoint. /// @details `live_levels` is the full depth: a comparison value word depends on the /// secret path at every level, so there is no early dummy-word tail. struct geneval_cmp_result { std::vector party0; std::vector party1; HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") std::vector> correction_words; HEDLEY_PRAGMA(GCC diagnostic pop) std::vector correction_advice; std::vector value_cw; std::vector tail_cw; uint64_t cw_last = 0; uint64_t addend0 = 0; uint64_t addend1 = 0; uint64_t mask = 0; std::size_t live_levels = 0; }; /// @name Comparison geneval /// @tparam InputT input domain type /// @tparam ForwardIterator forward iterator type /// @tparam RootSampler sampler for the Doerner–Shelat root seed /// @tparam PadRng pad stream for the Doerner–Shelat protocol /// @param x0 party 0's share of the secret point /// @param x1 party 1's share of the secret point /// @param begin the iterator to the first query /// @param end the iterator past the last query /// @param rng the Doerner–Shelat randomness tapes /// @return the opened comparison shares /// @{ /// @brief `x0 XOR x1` is the secret point, in the same share convention as /// `geneval_point`. `spec` is an `lt` / `leq` / `gt` / `geq` pack. Each /// endpoint is returned in order as the two parties' `eval_point(cmp, ...)` /// shares. An empty range opens nothing. /// @tparam Spec comparison or interval specification /// @param x0 party 0's share of the secret point /// @param x1 party 1's share of the secret point /// @param begin the iterator to the first query /// @param end the iterator past the last query /// @param rng the Doerner–Shelat randomness tapes /// @param spec the comparison specification template HEDLEY_WARN_UNUSED_RESULT geneval_cmp_result geneval_cmp(InputT x0, InputT x1, ForwardIterator begin, ForwardIterator end, ds_randomness rng, Spec spec) { geneval_cmp_result out; if (begin == end) return out; auto keys = make_dpf_doerner_shelat(std::move(x0), std::move(x1), std::move(rng), std::move(spec)); const auto & k0 = keys.first; const auto & k1 = keys.second; using key_type = std::decay_t; constexpr std::size_t depth = key_type::depth; out.live_levels = depth; out.mask = k0.cmp().mask; out.cw_last = k0.cw_last(); out.addend0 = k0.cmp_addend().raw(); out.addend1 = k1.cmp_addend().raw(); out.correction_words.resize(depth); out.correction_advice.resize(depth); if constexpr (key_type::cmp_block > 0) { out.value_cw.resize(key_type::cmp_checkpoints); for (std::size_t i = 0; i < key_type::cmp_checkpoints; ++i) out.value_cw[i] = k0.value_cw(i); out.tail_cw.resize(key_type::cmp_tail); for (std::size_t z = 0; z < key_type::cmp_tail; ++z) out.tail_cw[z] = k0.tail_cw(z); } else out.value_cw.resize(depth); for (std::size_t level = 0; level < depth; ++level) { out.correction_words[level] = k0.correction_word(level); out.correction_advice[level] = static_cast(k0.correction_advice(level)); if constexpr (key_type::cmp_block == 0) out.value_cw[level] = k0.value_cw(level); } for (auto it = begin; it != end; ++it) { out.party0.push_back(eval_point(dpf::cmp, k0, *it).raw()); out.party1.push_back(eval_point(dpf::cmp, k1, *it).raw()); } return out; } /// @brief Additive shares of the point (`x0 + x1`). /// @tparam Spec comparison or interval specification /// @param x0 party 0's share of the secret point /// @param x1 party 1's share of the secret point /// @param begin the iterator to the first query /// @param end the iterator past the last query /// @param rng the Doerner–Shelat randomness tapes /// @param spec the comparison specification template HEDLEY_WARN_UNUSED_RESULT geneval_cmp_result geneval_cmp(arith_input_t, InputT x0, InputT x1, ForwardIterator begin, ForwardIterator end, ds_randomness rng, Spec spec) { geneval_cmp_result out; if (begin == end) return out; auto keys = make_dpf_doerner_shelat(arith_input, std::move(x0), std::move(x1), std::move(rng), std::move(spec)); const auto & k0 = keys.first; const auto & k1 = keys.second; using key_type = std::decay_t; constexpr std::size_t depth = key_type::depth; out.live_levels = depth; out.mask = k0.cmp().mask; out.cw_last = k0.cw_last(); out.addend0 = k0.cmp_addend().raw(); out.addend1 = k1.cmp_addend().raw(); out.correction_words.resize(depth); out.correction_advice.resize(depth); if constexpr (key_type::cmp_block > 0) { out.value_cw.resize(key_type::cmp_checkpoints); for (std::size_t i = 0; i < key_type::cmp_checkpoints; ++i) out.value_cw[i] = k0.value_cw(i); out.tail_cw.resize(key_type::cmp_tail); for (std::size_t z = 0; z < key_type::cmp_tail; ++z) out.tail_cw[z] = k0.tail_cw(z); } else out.value_cw.resize(depth); for (std::size_t level = 0; level < depth; ++level) { out.correction_words[level] = k0.correction_word(level); out.correction_advice[level] = static_cast(k0.correction_advice(level)); if constexpr (key_type::cmp_block == 0) out.value_cw[level] = k0.value_cw(level); } for (auto it = begin; it != end; ++it) { out.party0.push_back(eval_point(dpf::cmp, k0, *it).raw()); out.party1.push_back(eval_point(dpf::cmp, k1, *it).raw()); } return out; } /// @brief `gt(beta)` on XOR shares of the point. `if_false` is 0. /// @param x0 party 0's share of the secret point /// @param x1 party 1's share of the secret point /// @param begin the iterator to the first query /// @param end the iterator past the last query /// @param rng the Doerner–Shelat randomness tapes /// @param beta the true payload template HEDLEY_WARN_UNUSED_RESULT geneval_cmp_result geneval_cmp(InputT x0, InputT x1, ForwardIterator begin, ForwardIterator end, ds_randomness rng, uint64_t beta) { return geneval_cmp(std::move(x0), std::move(x1), begin, end, std::move(rng), dpf::gt(beta)); } /// @brief `gt(beta)` on additive shares of the point. `if_false` is 0. /// @param x0 party 0's share of the secret point /// @param x1 party 1's share of the secret point /// @param begin the iterator to the first query /// @param end the iterator past the last query /// @param rng the Doerner–Shelat randomness tapes /// @param beta the true payload template HEDLEY_WARN_UNUSED_RESULT geneval_cmp_result geneval_cmp(arith_input_t, InputT x0, InputT x1, ForwardIterator begin, ForwardIterator end, ds_randomness rng, uint64_t beta) { return geneval_cmp(arith_input, std::move(x0), std::move(x1), begin, end, std::move(rng), dpf::gt(beta)); } /// @} } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_GENEVAL_HPP__