2026-09-24 14:08:32 -06:00
/// @file dpf/eval_full.hpp
2026-09-24 20:44:07 -06:00
/// @brief Evaluate every input in the DPF domain.
/// @details Equivalent to `eval_interval` from
/// `std::numeric_limits<input_type>::min()` through `max()`.
2026-09-28 05:59:19 -06:00
/// When the input offset is not yet assigned, use `defer_eval_full`
/// (full-domain identity eval plus a deferred rotation view).
2026-09-24 20:44:07 -06:00
/// @snippet evaluation/eval_full.cpp eval-full
2026-09-24 14:08:32 -06:00
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 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_EVAL_FULL_HPP__
# define LIBDPF_INCLUDE_DPF_EVAL_FULL_HPP__
# include <portable-snippets/builtin/builtin.h>
# include "hedley/hedley.h"
# include <cstddef>
# include <type_traits>
# include <utility>
# include <limits>
# include "dpf/dpf_key.hpp"
# include "dpf/eval_common.hpp"
# include "dpf/eval_target.hpp"
# include "dpf/output_buffer.hpp"
# include "dpf/interval_memoizer.hpp"
# include "dpf/rotation_iterable.hpp"
# include "dpf/subinterval_iterable.hpp"
2026-09-28 05:59:19 -06:00
# include "dpf/verifiable.hpp"
2026-09-24 14:08:32 -06:00
namespace dpf
{
namespace internal
{
template < std : : size_t . . . Is ,
typename DpfKey ,
typename OutputBuffers ,
typename IntervalMemoizer ,
std : : size_t . . . IIs ,
std : : enable_if_t < dpf : : is_wildcard_v < typename DpfKey : : raw_input_type > , bool > = false >
auto eval_full ( const DpfKey & dpf , OutputBuffers & & outbufs ,
2026-09-28 05:59:19 -06:00
IntervalMemoizer & & memoizer , std : : index_sequence < IIs . . . > ,
proof_token * pi = nullptr )
2026-09-24 14:08:32 -06:00
{
using dpf_type = DpfKey ;
using input_type = typename dpf_type : : input_type ;
auto offset = dpf . offset_x ( 0 ) ; // N.B.: throws if dpf is not ready
dpf : : internal : : eval_interval_impl < Is . . . > ( dpf ,
std : : numeric_limits < input_type > : : min ( ) ,
std : : numeric_limits < input_type > : : max ( ) ,
2026-09-28 05:59:19 -06:00
outbufs , memoizer , std : : make_index_sequence < sizeof . . . ( Is ) > ( ) , pi ) ;
2026-09-24 14:08:32 -06:00
return utils : : make_tuple ( dpf : : rotation_iterable ( std : : begin ( utils : : get < IIs > ( outbufs ) ) , std : : end ( utils : : get < IIs > ( outbufs ) ) , offset ) . . . ) ;
}
template < std : : size_t . . . Is ,
typename DpfKey ,
typename OutputBuffers ,
typename IntervalMemoizer ,
std : : size_t . . . IIs ,
std : : enable_if_t < ! dpf : : is_wildcard_v < typename DpfKey : : raw_input_type > , bool > = false >
auto eval_full ( const DpfKey & dpf , OutputBuffers & & outbufs ,
2026-09-28 05:59:19 -06:00
IntervalMemoizer & & memoizer , std : : index_sequence < IIs . . . > ,
proof_token * pi = nullptr )
2026-09-24 14:08:32 -06:00
{
using dpf_type = DpfKey ;
using input_type = typename dpf_type : : input_type ;
dpf : : internal : : eval_interval_impl < Is . . . > ( dpf ,
std : : numeric_limits < input_type > : : min ( ) ,
std : : numeric_limits < input_type > : : max ( ) ,
2026-09-28 05:59:19 -06:00
outbufs , memoizer , std : : make_index_sequence < sizeof . . . ( Is ) > ( ) , pi ) ;
2026-09-24 14:08:32 -06:00
return utils : : make_tuple (
subinterval_iterable ( std : : begin ( utils : : get < IIs > ( outbufs ) ) ,
utils : : size ( utils : : get < IIs > ( outbufs ) ) ,
std : : size_t { 0 } ,
utils : : get < IIs > ( outbufs ) . size ( ) - 1 ,
std : : size_t { 0 } ,
std : : size_t { 0 } ) . . . ) ;
}
} // namespace internal
2026-09-28 05:59:19 -06:00
/// \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).
2026-09-24 14:08:32 -06:00
template < std : : size_t I = 0 ,
std : : size_t . . . Is ,
typename DpfKey ,
typename OutputBuffers ,
typename IntervalMemoizer ,
std : : enable_if_t < looks_like_dpf_key_v < DpfKey > & & ! is_multilevel_key_v < DpfKey > , bool > = true >
HEDLEY_ALWAYS_INLINE
auto eval_full ( const DpfKey & dpf , OutputBuffers & & outbufs ,
2026-09-28 05:59:19 -06:00
IntervalMemoizer & & memoizer , proof_token * pi = nullptr )
2026-09-24 14:08:32 -06:00
{
assert_not_wildcard_output < I , Is . . . > ( dpf ) ;
2026-09-28 05:59:19 -06:00
return internal : : eval_full < I , Is . . . > ( dpf , outbufs , memoizer ,
std : : make_index_sequence < 1 + sizeof . . . ( Is ) > ( ) , pi ) ;
2026-09-24 14:08:32 -06:00
}
2026-09-28 05:59:19 -06:00
/// @brief Evaluate the whole domain and fold a once-per-BFS-node VDPF proof.
/// \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 = 0 ,
std : : size_t . . . Is ,
typename DpfKey ,
typename OutputBuffers ,
typename IntervalMemoizer ,
std : : enable_if_t < looks_like_dpf_key_v < DpfKey > & & ! is_multilevel_key_v < DpfKey > , bool > = true >
HEDLEY_ALWAYS_INLINE
auto eval_full ( const DpfKey & dpf , OutputBuffers & & outbufs ,
IntervalMemoizer & & memoizer , prove_ref pr )
{
static_assert ( DpfKey : : is_verifiable ,
" eval_full(..., prove(π)): key must carry dpf::verifiable " ) ;
detail : : vdpf : : init_proof ( pr . token , dpf ) ;
auto out = eval_full < I , Is . . . > ( dpf , std : : forward < OutputBuffers > ( outbufs ) ,
std : : forward < IntervalMemoizer > ( memoizer ) , & pr . token ) ;
detail : : vdpf : : fold_output_binding ( pr . token , dpf ) ;
return out ;
}
/// @brief Evaluate the whole domain and fold each written output into a sketch.
/// \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 = 0 ,
std : : size_t . . . Is ,
typename DpfKey ,
typename OutputBuffers ,
typename IntervalMemoizer ,
std : : enable_if_t < looks_like_dpf_key_v < DpfKey > & & ! is_multilevel_key_v < DpfKey > , bool > = true >
HEDLEY_ALWAYS_INLINE
auto eval_full ( const DpfKey & dpf , OutputBuffers & & outbufs ,
IntervalMemoizer & & memoizer , sketch_ref & sk )
{
static_assert ( DpfKey : : is_extractable ,
" eval_full(..., sketch(σ )): key must carry dpf::extractable " ) ;
auto ret = eval_full < I , Is . . . > ( dpf , outbufs ,
std : : forward < IntervalMemoizer > ( memoizer ) ) ;
if constexpr ( sizeof . . . ( Is ) = = 0 )
{
for ( std : : size_t k = 0 ; k < utils : : size ( outbufs ) ; + + k )
sk . absorb ( outbufs [ k ] ) ;
}
return ret ;
}
/// \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).
2026-09-24 14:08:32 -06:00
template < std : : size_t I = 0 ,
std : : size_t . . . Is ,
typename DpfKey ,
typename OutputBuffers ,
std : : enable_if_t < looks_like_dpf_key_v < DpfKey > & & ! is_multilevel_key_v < DpfKey > , bool > = true ,
std : : enable_if_t < ! std : : is_base_of_v <
dpf : : interval_memoizer_base < unwrap_party_key_t < DpfKey > > ,
std : : decay_t < OutputBuffers > > , bool > = true >
HEDLEY_ALWAYS_INLINE
auto eval_full ( const DpfKey & dpf , OutputBuffers & outbufs ) // NOLINT(runtime/references)
{
2026-09-28 05:59:19 -06:00
// The full-domain workspace is keyed only by the key type. Reusing it
// drops a heap allocation per call and lets a repeated key skip the
// interior rebuild (assign_interval keeps the last level).
thread_local auto memo = dpf : : make_basic_full_memoizer < DpfKey > ( ) ;
return eval_full < I , Is . . . > ( dpf , outbufs , memo ) ;
}
/// @brief Evaluate the whole domain into `outbufs` and fold a VDPF proof.
/// \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 = 0 ,
std : : size_t . . . Is ,
typename DpfKey ,
typename OutputBuffers ,
std : : enable_if_t < looks_like_dpf_key_v < DpfKey > & & ! is_multilevel_key_v < DpfKey > , bool > = true ,
std : : enable_if_t < ! std : : is_base_of_v <
dpf : : interval_memoizer_base < unwrap_party_key_t < DpfKey > > ,
std : : decay_t < OutputBuffers > > , bool > = true >
HEDLEY_ALWAYS_INLINE
auto eval_full ( const DpfKey & dpf , OutputBuffers & outbufs , prove_ref pr ) // NOLINT(runtime/references)
{
static_assert ( DpfKey : : is_verifiable ,
" eval_full(..., prove(π)): key must carry dpf::verifiable " ) ;
2026-09-24 14:08:32 -06:00
return eval_full < I , Is . . . > ( dpf , outbufs ,
2026-09-28 05:59:19 -06:00
dpf : : make_basic_full_memoizer ( dpf ) , pr ) ;
2026-09-24 14:08:32 -06:00
}
2026-09-28 05:59:19 -06:00
/// \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).
2026-09-24 14:08:32 -06:00
template < std : : size_t I = 0 ,
std : : size_t . . . Is ,
typename DpfKey ,
typename IntervalMemoizer ,
std : : enable_if_t < looks_like_dpf_key_v < DpfKey > & & ! is_multilevel_key_v < DpfKey > , bool > = true ,
std : : enable_if_t < std : : is_base_of_v <
dpf : : interval_memoizer_base < unwrap_party_key_t < DpfKey > > ,
std : : decay_t < IntervalMemoizer > > , bool > = true >
HEDLEY_ALWAYS_INLINE
auto eval_full ( const DpfKey & dpf ,
IntervalMemoizer & & memoizer )
{
auto outbufs = utils : : make_tuple (
make_output_buffer_for_full < I > ( dpf ) ,
make_output_buffer_for_full < Is > ( dpf ) . . . ) ;
// moving `outbufs` is allowed as the `outbufs` are `std::vectors`
// the underlying data remains on the heap
// and thus the data the iterable refers to is still valid
auto iterable = eval_full < I , Is . . . > ( dpf , outbufs , memoizer ) ;
return std : : make_pair ( std : : move ( outbufs ) , std : : move ( iterable ) ) ;
}
2026-09-24 23:18:10 -06:00
/// @brief Evaluate the whole domain, allocating a basic full memoizer and a buffer.
/// @tparam I output index
/// @tparam Is is
/// @tparam DpfKey DPF key type
/// @tparam DpfKey DPF key type
/// @param dpf the DPF key
/// @return the evaluation result
2026-09-28 05:59:19 -06:00
/// \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).
2026-09-24 14:08:32 -06:00
template < std : : size_t I = 0 ,
std : : size_t . . . Is ,
typename DpfKey ,
std : : enable_if_t < looks_like_dpf_key_v < DpfKey > & & ! is_multilevel_key_v < DpfKey > , bool > = true >
HEDLEY_ALWAYS_INLINE
auto eval_full ( const DpfKey & dpf )
{
return eval_full < I , Is . . . > ( dpf ,
dpf : : make_basic_full_memoizer ( dpf ) ) ;
}
2026-09-28 05:59:19 -06:00
/// @brief Full-domain deferred eval while the input offset is unset.
/// @details Sugar for `defer_eval_interval` over `[min, max]`. `outbufs` must
/// be full-domain sized. After assign, `.get()` yields the logical
/// full-domain view (same values as eager `eval_full` after assign).
template < std : : size_t I = 0 ,
std : : size_t . . . Is ,
typename DpfKey ,
typename OutputBuffers ,
typename IntervalMemoizer ,
std : : enable_if_t < looks_like_dpf_key_v < DpfKey >
& & ! is_multilevel_key_v < DpfKey > , bool > = true >
auto defer_eval_full ( const DpfKey & dpf , OutputBuffers & outbufs ,
IntervalMemoizer & & memoizer ) // NOLINT(runtime/references)
{
using input_type = typename DpfKey : : input_type ;
return defer_eval_interval < I , Is . . . > ( dpf ,
std : : numeric_limits < input_type > : : min ( ) ,
std : : numeric_limits < input_type > : : max ( ) ,
outbufs , std : : forward < IntervalMemoizer > ( memoizer ) ) ;
}
/// @brief `defer_eval_full` with a basic full-domain memoizer.
template < std : : size_t I = 0 ,
std : : size_t . . . Is ,
typename DpfKey ,
typename OutputBuffers ,
std : : enable_if_t < looks_like_dpf_key_v < DpfKey >
& & ! is_multilevel_key_v < DpfKey > , bool > = true ,
std : : enable_if_t < ! std : : is_base_of_v <
dpf : : interval_memoizer_base < unwrap_party_key_t < DpfKey > > ,
std : : decay_t < OutputBuffers > > , bool > = true >
auto defer_eval_full ( const DpfKey & dpf , OutputBuffers & outbufs ) // NOLINT(runtime/references)
{
return defer_eval_full < I , Is . . . > ( dpf , outbufs ,
dpf : : make_basic_full_memoizer ( dpf ) ) ;
}
2026-09-24 14:08:32 -06:00
} // namespace dpf
# endif // LIBDPF_INCLUDE_DPF_EVAL_FULL_HPP__