Checkpoint the party/runtime stack before share-program and malicious-mode work.

Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ryan Henry 2026-09-28 05:59:19 -06:00
parent 695f8e84f7
commit 0d22946a0e
1835 changed files with 170291 additions and 2849 deletions

View file

@ -18,6 +18,7 @@
#include <cstring>
#include <algorithm>
#include <iterator>
#include <limits>
#include <list>
#include <stdexcept>
#include <type_traits>
@ -36,6 +37,7 @@
#include "dpf/interval_memoizer.hpp"
#include "dpf/aligned_allocator.hpp"
#include "dpf/leaf_node.hpp"
#include "dpf/verifiable.hpp"
namespace dpf
{
@ -70,8 +72,10 @@ constexpr std::size_t resolved_out_prefix() noexcept
// eval_point(target, key, x [, path])
// ---------------------------------------------------------------------------
/// \complexity O(n) time. n is `depth`. One interior traversal per level from the memoizer resume index through the leaf. Extra space is the path memoizer (O(n) nodes, or one node if it does not memoize). A proof token adds one fold per level walked.
template <std::size_t I, std::size_t N, typename KeyT, typename QueryT,
typename PathMemoizer = nonmemoizing_path_memoizer<KeyT>>
typename PathMemoizer = nonmemoizing_path_memoizer<KeyT>,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_point(out_t<I, N>, const KeyT & key, QueryT && x,
PathMemoizer && path = PathMemoizer{})
{
@ -88,8 +92,10 @@ auto eval_point(out_t<I, N>, const KeyT & key, QueryT && x,
}
}
/// \complexity O(n) time. n is `depth`. One interior traversal per level from the memoizer resume index through the leaf. Extra space is the path memoizer (O(n) nodes, or one node if it does not memoize). A proof token adds one fold per level walked.
template <typename Beta = uint64_t, typename KeyT, typename QueryT,
typename PathMemoizer = basic_path_memoizer<KeyT>>
typename PathMemoizer = basic_path_memoizer<KeyT>,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_point(cmp_t, const KeyT & key, QueryT && x,
PathMemoizer && path = PathMemoizer{})
{
@ -97,8 +103,41 @@ auto eval_point(cmp_t, const KeyT & key, QueryT && x,
std::forward<PathMemoizer>(path));
}
/// \complexity O(n) time. n is `depth`. One interior traversal per level from the memoizer resume index through the leaf. Extra space is the path memoizer (O(n) nodes, or one node if it does not memoize). A proof token adds one fold per level walked.
template <typename Beta = uint64_t, typename KeyT, typename QueryT,
typename PathMemoizer = basic_path_memoizer<KeyT>,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_point(cmp_t, const KeyT & key, QueryT && x, prove_ref pr,
PathMemoizer && path = PathMemoizer{})
{
static_assert(KeyT::is_verifiable,
"eval_point(cmp, ..., prove(π)): key must carry dpf::verifiable");
detail::vdpf::init_proof(pr.token, key);
auto out = detail::incr::eval_cmp_point_impl<Beta>(key, std::forward<QueryT>(x),
std::forward<PathMemoizer>(path), &pr.token);
detail::vdpf::fold_output_binding(pr.token, key);
return out;
}
/// \complexity O(n) time. n is `depth`. One interior traversal per level from the memoizer resume index through the leaf. Extra space is the path memoizer (O(n) nodes, or one node if it does not memoize). A proof token adds one fold per level walked.
template <typename Beta = uint64_t, typename KeyT, typename QueryT,
typename PathMemoizer = basic_path_memoizer<KeyT>,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_point(cmp_t, const KeyT & key, QueryT && x, sketch_ref & sk,
PathMemoizer && path = PathMemoizer{})
{
static_assert(KeyT::is_extractable,
"eval_point(cmp, ..., sketch(σ)): key must carry dpf::extractable");
auto y = detail::incr::eval_cmp_point_impl<Beta>(key, std::forward<QueryT>(x),
std::forward<PathMemoizer>(path));
sk.absorb(y);
return y;
}
/// \complexity O(n) time. n is `depth`. One interior traversal per level from the memoizer resume index through the leaf. Extra space is the path memoizer (O(n) nodes, or one node if it does not memoize). A proof token adds one fold per level walked.
template <std::size_t L, typename Beta = uint64_t, typename KeyT, typename QueryT,
typename PathMemoizer = basic_path_memoizer<KeyT>>
typename PathMemoizer = basic_path_memoizer<KeyT>,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_point(cmp_prefix_t<L>, const KeyT & key, QueryT && x,
PathMemoizer && path = PathMemoizer{})
{
@ -106,12 +145,30 @@ auto eval_point(cmp_prefix_t<L>, const KeyT & key, QueryT && x,
std::forward<QueryT>(x), std::forward<PathMemoizer>(path));
}
/// \complexity O(n) time. n is `depth`. One interior traversal per level from the memoizer resume index through the leaf. Extra space is the path memoizer (O(n) nodes, or one node if it does not memoize). A proof token adds one fold per level walked.
template <std::size_t L, typename Beta = uint64_t, typename KeyT, typename QueryT,
typename PathMemoizer = basic_path_memoizer<KeyT>,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_point(cmp_prefix_t<L>, const KeyT & key, QueryT && x, prove_ref pr,
PathMemoizer && path = PathMemoizer{})
{
static_assert(KeyT::is_verifiable,
"eval_point(cmp_prefix, ..., prove(π)): key must carry dpf::verifiable");
detail::vdpf::init_proof(pr.token, key);
auto out = detail::incr::eval_cmp_prefix_point_impl<L, Beta>(key,
std::forward<QueryT>(x), std::forward<PathMemoizer>(path), &pr.token);
detail::vdpf::fold_output_binding(pr.token, key);
return out;
}
// ---------------------------------------------------------------------------
// eval_interval(target, key, from, to [, buf [, memo]])
// ---------------------------------------------------------------------------
/// \complexity O(L) interior traversals and O(L) workspace in the basic memoizer. L is the number of leaf nodes covering the closed interval (`get_nodes_at_level` at `depth`). Level k expands `(to >> (n-k)) - (from >> (n-k)) + 1` nodes; those counts sum to Θ(L). The output buffer holds one slot per input in the interval. n is `depth`.
template <std::size_t I, std::size_t N, typename KeyT, typename LaneT,
typename OutputBuffer, typename IntervalMemoizer>
typename OutputBuffer, typename IntervalMemoizer,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_interval(out_t<I, N>, const KeyT & key, LaneT from, LaneT to,
OutputBuffer && outbuf, IntervalMemoizer && memo)
{
@ -130,8 +187,10 @@ auto eval_interval(out_t<I, N>, const KeyT & key, LaneT from, LaneT to,
}
}
/// \complexity O(L) interior traversals and O(L) workspace in the basic memoizer. L is the number of leaf nodes covering the closed interval (`get_nodes_at_level` at `depth`). Level k expands `(to >> (n-k)) - (from >> (n-k)) + 1` nodes; those counts sum to Θ(L). The output buffer holds one slot per input in the interval. n is `depth`.
template <std::size_t I, std::size_t N, typename KeyT, typename LaneT,
typename OutputBuffer>
typename OutputBuffer,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_interval(out_t<I, N>, const KeyT & key, LaneT from, LaneT to,
OutputBuffer && outbuf)
{
@ -148,7 +207,9 @@ auto eval_interval(out_t<I, N>, const KeyT & key, LaneT from, LaneT to,
}
}
template <std::size_t I, std::size_t N, typename KeyT, typename LaneT>
/// \complexity O(L) interior traversals and O(L) workspace in the basic memoizer. L is the number of leaf nodes covering the closed interval (`get_nodes_at_level` at `depth`). Level k expands `(to >> (n-k)) - (from >> (n-k)) + 1` nodes; those counts sum to Θ(L). The output buffer holds one slot per input in the interval. n is `depth`.
template <std::size_t I, std::size_t N, typename KeyT, typename LaneT,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_interval(out_t<I, N>, const KeyT & key, LaneT from, LaneT to)
{
if constexpr (is_multilevel_key_v<KeyT>)
@ -162,8 +223,10 @@ auto eval_interval(out_t<I, N>, const KeyT & key, LaneT from, LaneT to)
}
}
/// \complexity O(L) interior traversals and O(L) workspace in the basic memoizer. L is the number of leaf nodes covering the closed interval (`get_nodes_at_level` at `depth`). Level k expands `(to >> (n-k)) - (from >> (n-k)) + 1` nodes; those counts sum to Θ(L). The output buffer holds one slot per input in the interval. n is `depth`.
template <typename Beta = uint64_t, typename KeyT, typename LaneT,
typename OutputBuffer>
typename OutputBuffer,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
void eval_interval(cmp_t, const KeyT & key, LaneT from, LaneT to,
OutputBuffer && outbuf)
{
@ -171,8 +234,25 @@ void eval_interval(cmp_t, const KeyT & key, LaneT from, LaneT to,
std::forward<OutputBuffer>(outbuf));
}
/// \complexity O(L) interior traversals and O(L) workspace in the basic memoizer. L is the number of leaf nodes covering the closed interval (`get_nodes_at_level` at `depth`). Level k expands `(to >> (n-k)) - (from >> (n-k)) + 1` nodes; those counts sum to Θ(L). The output buffer holds one slot per input in the interval. n is `depth`.
template <typename Beta = uint64_t, typename KeyT, typename LaneT,
typename OutputBuffer, typename IntervalMemoizer>
typename OutputBuffer,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
void eval_interval(cmp_t, const KeyT & key, LaneT from, LaneT to,
OutputBuffer && outbuf, prove_ref pr)
{
static_assert(KeyT::is_verifiable,
"eval_interval(cmp, ..., prove(π)): key must carry dpf::verifiable");
detail::vdpf::init_proof(pr.token, key);
detail::incr::eval_cmp_interval_impl<Beta>(key, from, to,
std::forward<OutputBuffer>(outbuf), &pr.token);
detail::vdpf::fold_output_binding(pr.token, key);
}
/// \complexity O(L) interior traversals and O(L) workspace in the basic memoizer. L is the number of leaf nodes covering the closed interval (`get_nodes_at_level` at `depth`). Level k expands `(to >> (n-k)) - (from >> (n-k)) + 1` nodes; those counts sum to Θ(L). The output buffer holds one slot per input in the interval. n is `depth`.
template <typename Beta = uint64_t, typename KeyT, typename LaneT,
typename OutputBuffer, typename IntervalMemoizer,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
void eval_interval(cmp_t, const KeyT & key, LaneT from, LaneT to,
OutputBuffer && outbuf, IntervalMemoizer && memo)
{
@ -181,18 +261,51 @@ void eval_interval(cmp_t, const KeyT & key, LaneT from, LaneT to,
std::forward<IntervalMemoizer>(memo));
}
template <typename Beta = uint64_t, typename KeyT, typename LaneT>
/// \complexity O(L) interior traversals and O(L) workspace in the basic memoizer. L is the number of leaf nodes covering the closed interval (`get_nodes_at_level` at `depth`). Level k expands `(to >> (n-k)) - (from >> (n-k)) + 1` nodes; those counts sum to Θ(L). The output buffer holds one slot per input in the interval. n is `depth`.
template <typename Beta = uint64_t, typename KeyT, typename LaneT,
typename OutputBuffer, typename IntervalMemoizer,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
void eval_interval(cmp_t, const KeyT & key, LaneT from, LaneT to,
OutputBuffer && outbuf, IntervalMemoizer && memo, prove_ref pr)
{
static_assert(KeyT::is_verifiable,
"eval_interval(cmp, ..., prove(π)): key must carry dpf::verifiable");
detail::vdpf::init_proof(pr.token, key);
detail::incr::eval_cmp_interval_impl<Beta>(key, from, to,
std::forward<OutputBuffer>(outbuf),
std::forward<IntervalMemoizer>(memo), &pr.token);
detail::vdpf::fold_output_binding(pr.token, key);
}
/// \complexity O(L) interior traversals and O(L) workspace in the basic memoizer. L is the number of leaf nodes covering the closed interval (`get_nodes_at_level` at `depth`). Level k expands `(to >> (n-k)) - (from >> (n-k)) + 1` nodes; those counts sum to Θ(L). The output buffer holds one slot per input in the interval. n is `depth`.
template <typename Beta = uint64_t, typename KeyT, typename LaneT,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_interval(cmp_t, const KeyT & key, LaneT from, LaneT to)
{
return detail::incr::eval_cmp_interval_impl<Beta>(key, from, to);
}
/// \complexity O(L) interior traversals and O(L) workspace in the basic memoizer. L is the number of leaf nodes covering the closed interval (`get_nodes_at_level` at `depth`). Level k expands `(to >> (n-k)) - (from >> (n-k)) + 1` nodes; those counts sum to Θ(L). The output buffer holds one slot per input in the interval. n is `depth`.
template <typename Beta = uint64_t, typename KeyT, typename LaneT,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_interval(cmp_t, const KeyT & key, LaneT from, LaneT to, prove_ref pr)
{
static_assert(KeyT::is_verifiable,
"eval_interval(cmp, ..., prove(π)): key must carry dpf::verifiable");
detail::vdpf::init_proof(pr.token, key);
auto out = detail::incr::eval_cmp_interval_impl<Beta>(key, from, to, &pr.token);
detail::vdpf::fold_output_binding(pr.token, key);
return out;
}
// ---------------------------------------------------------------------------
// eval_full(target, key [, …])
// ---------------------------------------------------------------------------
/// \complexity Same expansion as `eval_interval` on the whole domain. L = 2^{n - lg(outputs_per_leaf)} leaf nodes, n = `depth`. Time Θ(L) interior traversals. The output buffer stores one slot per domain point (2^n).
template <std::size_t I, std::size_t N, typename KeyT,
typename OutputBuffer, typename IntervalMemoizer>
typename OutputBuffer, typename IntervalMemoizer,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_full(out_t<I, N>, const KeyT & key, OutputBuffer && outbuf,
IntervalMemoizer && memo)
{
@ -210,7 +323,9 @@ auto eval_full(out_t<I, N>, const KeyT & key, OutputBuffer && outbuf,
}
}
template <std::size_t I, std::size_t N, typename KeyT>
/// \complexity Same expansion as `eval_interval` on the whole domain. L = 2^{n - lg(outputs_per_leaf)} leaf nodes, n = `depth`. Time Θ(L) interior traversals. The output buffer stores one slot per domain point (2^n).
template <std::size_t I, std::size_t N, typename KeyT,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_full(out_t<I, N>, const KeyT & key)
{
if constexpr (is_multilevel_key_v<KeyT>)
@ -224,7 +339,9 @@ auto eval_full(out_t<I, N>, const KeyT & key)
}
}
template <typename Beta = uint64_t, typename KeyT>
/// \complexity Same expansion as `eval_interval` on the whole domain. L = 2^{n - lg(outputs_per_leaf)} leaf nodes, n = `depth`. Time Θ(L) interior traversals. The output buffer stores one slot per domain point (2^n).
template <typename Beta = uint64_t, typename KeyT,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_full(cmp_t, const KeyT & key)
{
if (!key.has_cmp())
@ -238,13 +355,36 @@ auto eval_full(cmp_t, const KeyT & key)
return detail::incr::eval_cmp_interval_impl<Beta>(key, lo, hi);
}
/// \complexity Same expansion as `eval_interval` on the whole domain. L = 2^{n - lg(outputs_per_leaf)} leaf nodes, n = `depth`. Time Θ(L) interior traversals. The output buffer stores one slot per domain point (2^n).
template <typename Beta = uint64_t, typename KeyT,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_full(cmp_t, const KeyT & key, prove_ref pr)
{
static_assert(KeyT::is_verifiable,
"eval_full(cmp, ..., prove(π)): key must carry dpf::verifiable");
if (!key.has_cmp())
throw std::invalid_argument("eval_full(cmp): no comparison channel");
using lane_t = typename KeyT::integral_type;
const auto nbits = static_cast<std::size_t>(key.cmp().nbits);
const lane_t lo = 0;
const lane_t hi = (nbits >= 8 * sizeof(lane_t))
? static_cast<lane_t>(~lane_t{0})
: static_cast<lane_t>((lane_t{1} << nbits) - 1);
detail::vdpf::init_proof(pr.token, key);
auto out = detail::incr::eval_cmp_interval_impl<Beta>(key, lo, hi, &pr.token);
detail::vdpf::fold_output_binding(pr.token, key);
return out;
}
// ---------------------------------------------------------------------------
// eval_sequence(target, key, begin, end, buf [, path])
// ---------------------------------------------------------------------------
/// \complexity O(n k) interior traversals in the worst case and O(k) node workspace. k is the number of listed points and n is `depth`. The breadth-first buffer is 2k nodes, so each level traverses at most one node per point. Shared prefixes do fewer traversals. A recipe memoizer instead stores O(recipe leaf nodes) (see that memoizer).
template <std::size_t I, std::size_t N, typename KeyT, typename ForwardIterator,
typename OutputBuffer,
typename PathMemoizer = basic_path_memoizer<KeyT>>
typename PathMemoizer = basic_path_memoizer<KeyT>,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_sequence(out_t<I, N>, const KeyT & key, ForwardIterator begin,
ForwardIterator end, OutputBuffer && outbuf,
PathMemoizer && path = PathMemoizer{})
@ -263,9 +403,11 @@ auto eval_sequence(out_t<I, N>, const KeyT & key, ForwardIterator begin,
}
}
/// \complexity O(n k) interior traversals in the worst case and O(k) node workspace. k is the number of listed points and n is `depth`. The breadth-first buffer is 2k nodes, so each level traverses at most one node per point. Shared prefixes do fewer traversals. A recipe memoizer instead stores O(recipe leaf nodes) (see that memoizer).
template <typename Beta = uint64_t, typename KeyT, typename ForwardIterator,
typename OutputBuffer,
typename PathMemoizer = basic_path_memoizer<KeyT>>
typename PathMemoizer = basic_path_memoizer<KeyT>,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
void eval_sequence(cmp_t, const KeyT & key, ForwardIterator begin,
ForwardIterator end, OutputBuffer && outbuf,
PathMemoizer && path = PathMemoizer{})
@ -275,24 +417,45 @@ void eval_sequence(cmp_t, const KeyT & key, ForwardIterator begin,
std::forward<PathMemoizer>(path));
}
/// \complexity O(n k) interior traversals in the worst case and O(k) node workspace. k is the number of listed points and n is `depth`. The breadth-first buffer is 2k nodes, so each level traverses at most one node per point. Shared prefixes do fewer traversals. A recipe memoizer instead stores O(recipe leaf nodes) (see that memoizer).
template <typename Beta = uint64_t, typename KeyT, typename ForwardIterator,
typename OutputBuffer,
typename PathMemoizer = basic_path_memoizer<KeyT>,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
void eval_sequence(cmp_t, const KeyT & key, ForwardIterator begin,
ForwardIterator end, OutputBuffer && outbuf, prove_ref pr,
PathMemoizer && path = PathMemoizer{})
{
static_assert(KeyT::is_verifiable,
"eval_sequence(cmp, ..., prove(π)): key must carry dpf::verifiable");
detail::vdpf::init_proof(pr.token, key);
detail::incr::eval_cmp_sequence_impl<Beta>(key, begin, end,
std::forward<OutputBuffer>(outbuf),
std::forward<PathMemoizer>(path), &pr.token);
detail::vdpf::fold_output_binding(pr.token, key);
}
// ---------------------------------------------------------------------------
// make_output_buffer(target, …)
// ---------------------------------------------------------------------------
template <typename Beta = uint64_t, typename KeyT>
template <typename Beta = uint64_t, typename KeyT,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto make_output_buffer(cmp_t, const KeyT & key, std::size_t n)
{
return detail::incr::make_output_buffer_for_cmp_impl<Beta>(key, n);
}
template <typename Beta = uint64_t, typename KeyT, typename LaneT>
template <typename Beta = uint64_t, typename KeyT, typename LaneT,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto make_output_buffer(cmp_t, const KeyT & key, LaneT from, LaneT to)
{
return detail::incr::make_output_buffer_for_cmp_interval_impl<Beta>(
key, from, to);
}
template <std::size_t I, std::size_t N, typename KeyT, typename LaneT>
template <std::size_t I, std::size_t N, typename KeyT, typename LaneT,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto make_output_buffer(out_t<I, N>, const KeyT & key, LaneT from, LaneT to)
{
constexpr auto pref = detail::resolved_out_prefix<I, N, KeyT>();
@ -426,6 +589,7 @@ auto eval_out_inner_product_impl(const KeyT & dpf, LaneT from, LaneT to,
for (std::size_t j = 0; j < count; ++j)
{
auto leaf = dpf.template traverse_exterior<I>(nodes[j]);
detail::incr::absorb_public_addend_all_lanes<I>(dpf, leaf);
acc.mac(leaf, (start + j) * opl, opl, weights);
}
start += seg.count;
@ -436,7 +600,7 @@ auto eval_out_inner_product_impl(const KeyT & dpf, LaneT from, LaneT to,
template <typename Beta = uint64_t, typename KeyT, typename LaneT,
typename Weights>
Beta eval_cmp_inner_product_impl(const KeyT & dpf, LaneT from, LaneT to,
Weights && weights)
Weights && weights, proof_token * pi = nullptr)
{
if (!dpf.has_cmp())
throw std::invalid_argument("cmp inner product: no comparison channel");
@ -459,7 +623,7 @@ Beta eval_cmp_inner_product_impl(const KeyT & dpf, LaneT from, LaneT to,
const std::size_t levels = unwrap_party_key_t<KeyT>::cmp_block > 0
? unwrap_party_key_t<KeyT>::cmp_h : nbits;
detail::incr::eval_cmp_interval_impl_interior(dpf, a, cmp_exclusive_end(b),
nbits, memo, levels);
nbits, memo, levels, pi);
uint64_t dot = 0;
for (std::size_t i = 0; i < count; ++i)
@ -486,9 +650,11 @@ Beta eval_cmp_inner_product_impl(const KeyT & dpf, LaneT from, LaneT to,
} // namespace incr
} // namespace detail
/// \complexity Same interior expansion as `eval_interval` on `[from, to]` (Θ(L) nodes, L = leaf nodes in the interval) plus a multiply-add per output slot into an O(1) accumulator. The basic memoizer still holds O(L) nodes.
template <std::size_t I, std::size_t N, typename KeyT, typename LaneT,
typename Weights, typename IntervalMemoizer,
std::enable_if_t<is_multilevel_key_v<KeyT>, bool> = true>
std::enable_if_t<is_multilevel_key_v<KeyT>, bool> = true,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_inner_product(out_t<I, N>, const KeyT & key, LaneT from, LaneT to,
Weights && weights, IntervalMemoizer && memo)
{
@ -498,20 +664,24 @@ auto eval_inner_product(out_t<I, N>, const KeyT & key, LaneT from, LaneT to,
std::forward<IntervalMemoizer>(memo));
}
/// \complexity Same interior expansion as `eval_interval` on `[from, to]` (Θ(L) nodes, L = leaf nodes in the interval) plus a multiply-add per output slot into an O(1) accumulator. The basic memoizer still holds O(L) nodes.
template <std::size_t I, std::size_t N, typename KeyT, typename LaneT,
typename Weights,
std::enable_if_t<is_multilevel_key_v<KeyT>, bool> = true>
std::enable_if_t<is_multilevel_key_v<KeyT>, bool> = true,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_inner_product(out_t<I, N>, const KeyT & key, LaneT from, LaneT to,
Weights && weights)
{
auto memo = make_basic_interval_memoizer<KeyT, I>(from, to);
auto memo = make_basic_interval_memoizer<KeyT, I>(key, from, to);
constexpr auto pref = detail::resolved_out_prefix<I, N, KeyT>();
return detail::incr::eval_out_inner_product_impl<pref, I>(key, from, to,
std::forward<Weights>(weights), memo);
}
/// \complexity Same interior expansion as `eval_interval` on `[from, to]` (Θ(L) nodes, L = leaf nodes in the interval) plus a multiply-add per output slot into an O(1) accumulator. The basic memoizer still holds O(L) nodes.
template <typename Beta = uint64_t, typename KeyT, typename LaneT,
typename Weights>
typename Weights,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
Beta eval_inner_product(cmp_t, const KeyT & key, LaneT from, LaneT to,
Weights && weights)
{
@ -519,6 +689,106 @@ Beta eval_inner_product(cmp_t, const KeyT & key, LaneT from, LaneT to,
std::forward<Weights>(weights));
}
/// @brief Comparison inner product over the whole comparison domain.
/// @details `sum_x [x satisfies cmp] * weights[x]` (as complementary halves),
/// the full-domain form of `eval_inner_product(cmp, key, lo, hi, w)`.
/// Pair the two parties' results with `reconstruct_cmp_halves`.
/// Waldo's private-threshold aggregate is this one call.
/// \complexity Same expansion as `eval_full` on the comparison domain, plus a
/// multiply-add per point into an `O(1)` accumulator.
template <typename Beta = uint64_t, typename KeyT, typename Weights,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
Beta eval_full_inner_product(cmp_t, const KeyT & key, Weights && weights)
{
if (!key.has_cmp())
throw std::invalid_argument(
"eval_full_inner_product(cmp): no comparison channel");
using lane_t = typename KeyT::integral_type;
const auto nbits = static_cast<std::size_t>(key.cmp().nbits);
const lane_t lo = 0;
const lane_t hi = (nbits >= 8 * sizeof(lane_t))
? static_cast<lane_t>(~lane_t{0})
: static_cast<lane_t>((lane_t{1} << nbits) - 1);
return eval_inner_product<Beta>(cmp, key, lo, hi,
std::forward<Weights>(weights));
}
/// \complexity Same interior expansion as `eval_interval` on `[from, to]` (Θ(L) nodes, L = leaf nodes in the interval) plus a multiply-add per output slot into an O(1) accumulator. The basic memoizer still holds O(L) nodes.
template <typename Beta = uint64_t, typename KeyT, typename LaneT,
typename Weights,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
Beta eval_inner_product(cmp_t, const KeyT & key, LaneT from, LaneT to,
Weights && weights, prove_ref pr)
{
static_assert(KeyT::is_verifiable,
"eval_inner_product(cmp, ..., prove(π)): key must carry dpf::verifiable");
detail::vdpf::init_proof(pr.token, key);
auto out = detail::incr::eval_cmp_inner_product_impl<Beta>(key, from, to,
std::forward<Weights>(weights), &pr.token);
detail::vdpf::fold_output_binding(pr.token, key);
return out;
}
/// @brief Initialise `pr.token` and fold `[from, to]` once per cmp BFS node.
template <typename KeyT, typename LaneT,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
void prove_cmp_interval(const KeyT & key, LaneT from, LaneT to, prove_ref pr)
{
static_assert(KeyT::is_verifiable,
"prove_cmp_interval: key must carry dpf::verifiable");
detail::vdpf::init_proof(pr.token, key);
detail::incr::prove_fold_cmp_interval(key, from, to, pr.token);
detail::vdpf::fold_output_binding(pr.token, key);
}
/// @brief Initialise `pr.token` and fold the full comparison domain.
template <typename KeyT,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
void prove_cmp_full(const KeyT & key, prove_ref pr)
{
static_assert(KeyT::is_verifiable,
"prove_cmp_full: key must carry dpf::verifiable");
if (!key.has_cmp())
throw std::invalid_argument("prove_cmp_full: no comparison channel");
using lane_t = typename KeyT::integral_type;
const auto nbits = static_cast<std::size_t>(key.cmp().nbits);
const lane_t lo = 0;
const lane_t hi = (nbits >= 8 * sizeof(lane_t))
? static_cast<lane_t>(~lane_t{0})
: static_cast<lane_t>((lane_t{1} << nbits) - 1);
prove_cmp_interval(key, lo, hi, pr);
}
/// @brief Fold a sorted sequence into `pr` via cmp interval-run covers.
template <typename KeyT, typename ForwardIterator,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
void prove_cmp_sequence(const KeyT & key, ForwardIterator begin,
ForwardIterator end, prove_ref pr)
{
static_assert(KeyT::is_verifiable,
"prove_cmp_sequence: key must carry dpf::verifiable");
if (HEDLEY_UNLIKELY(begin != end && !std::is_sorted(begin, end)))
throw std::runtime_error("list must be sorted");
detail::vdpf::init_proof(pr.token, key);
using lane_t = typename KeyT::integral_type;
for (auto it = begin; it != end; )
{
const auto run_from = static_cast<lane_t>(*it);
auto run_to = run_from;
++it;
while (it != end)
{
const auto next = static_cast<lane_t>(*it);
if (next != static_cast<lane_t>(run_to + lane_t{1}))
break;
run_to = next;
++it;
}
detail::incr::prove_fold_cmp_interval(key, run_from, run_to, pr.token);
}
detail::vdpf::fold_output_binding(pr.token, key);
}
// ---------------------------------------------------------------------------
// eval_sequence_breadth_first(out<I>, key, begin, end [, outbuf])
//
@ -621,6 +891,8 @@ void eval_out_sequence_breadth_first_impl(const KeyT & dpf,
auto leaf = dpf.template traverse_exterior<I>(buf[j]);
const std::size_t off =
static_cast<std::size_t>(static_cast<input_type>(*curr) & (opl - 1));
detail::incr::absorb_public_addend_lane<I>(dpf, leaf,
static_cast<input_type>(off));
auto v = dpf::extract_leaf<exterior_node, output_type>(leaf, off);
if constexpr (is_party_key_v<KeyT>)
outbuf[i] = subtractive_share<output_type, party_of_v<KeyT>>::from_raw(v);
@ -633,9 +905,11 @@ void eval_out_sequence_breadth_first_impl(const KeyT & dpf,
} // namespace incr
} // namespace detail
/// \complexity O(n k) interior traversals in the worst case and O(k) node workspace. k is the number of listed points and n is `depth`. The breadth-first buffer is 2k nodes, so each level traverses at most one node per point. Shared prefixes do fewer traversals. A recipe memoizer instead stores O(recipe leaf nodes) (see that memoizer).
template <std::size_t I, std::size_t N, typename KeyT,
typename ForwardIterator, typename OutputBuffer,
std::enable_if_t<is_multilevel_key_v<KeyT>, bool> = true>
std::enable_if_t<is_multilevel_key_v<KeyT>, bool> = true,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
void eval_sequence_breadth_first(out_t<I, N>, const KeyT & key,
ForwardIterator begin, ForwardIterator end, OutputBuffer && outbuf)
{
@ -644,9 +918,11 @@ void eval_sequence_breadth_first(out_t<I, N>, const KeyT & key,
std::forward<OutputBuffer>(outbuf));
}
/// \complexity O(n k) interior traversals in the worst case and O(k) node workspace. k is the number of listed points and n is `depth`. The breadth-first buffer is 2k nodes, so each level traverses at most one node per point. Shared prefixes do fewer traversals. A recipe memoizer instead stores O(recipe leaf nodes) (see that memoizer).
template <std::size_t I, std::size_t N, typename KeyT,
typename ForwardIterator,
std::enable_if_t<is_multilevel_key_v<KeyT>, bool> = true>
std::enable_if_t<is_multilevel_key_v<KeyT>, bool> = true,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto eval_sequence_breadth_first(out_t<I, N>, const KeyT & key,
ForwardIterator begin, ForwardIterator end)
{
@ -668,7 +944,8 @@ auto eval_sequence_breadth_first(out_t<I, N>, const KeyT & key,
/// @param end the iterator past the last query
/// @return the constructed object
template <std::size_t I, std::size_t N, typename KeyT, typename ForwardIterator,
std::enable_if_t<is_multilevel_key_v<KeyT>, bool> = true>
std::enable_if_t<is_multilevel_key_v<KeyT>, bool> = true,
std::enable_if_t<!has_embedded_dpf_key_v<std::decay_t<KeyT>>, int> = 0>
auto make_sequence_recipe(out_t<I, N>, const KeyT & key, ForwardIterator begin,
ForwardIterator end)
{