2026-09-24 14:08:32 -06:00
|
|
|
|
/// @file dpf/dpf_key.hpp
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief The DPF key, its correction words, and interior traversal.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
/// @author Ryan Henry <ryan.henry@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_DPF_KEY_HPP__
|
|
|
|
|
|
#define LIBDPF_INCLUDE_DPF_DPF_KEY_HPP__
|
|
|
|
|
|
|
|
|
|
|
|
#include "hedley/hedley.h"
|
|
|
|
|
|
|
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
#include <tuple>
|
|
|
|
|
|
#include <array>
|
|
|
|
|
|
#include <bitset>
|
|
|
|
|
|
#include <atomic>
|
|
|
|
|
|
|
|
|
|
|
|
#include "dpf/prg_aes.hpp"
|
2026-09-24 23:18:10 -06:00
|
|
|
|
#include "dpf/tree_traits.hpp"
|
2026-09-24 14:08:32 -06:00
|
|
|
|
#include "dpf/wildcard.hpp"
|
|
|
|
|
|
#include "dpf/twiddle.hpp"
|
|
|
|
|
|
#include "dpf/leaf_node.hpp"
|
|
|
|
|
|
#include "dpf/offset_wrapper.hpp"
|
|
|
|
|
|
#include "dpf/leaf_wrapper.hpp"
|
|
|
|
|
|
#include "dpf/emplace.hpp"
|
|
|
|
|
|
#include "dpf/placement.hpp"
|
2026-09-24 23:18:10 -06:00
|
|
|
|
#include "dpf/verifiable.hpp"
|
2026-09-24 14:08:32 -06:00
|
|
|
|
#include "dpf/dcf.hpp"
|
|
|
|
|
|
|
|
|
|
|
|
namespace dpf
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef LIBDPF_HAS_ASIO
|
|
|
|
|
|
namespace asio
|
|
|
|
|
|
{
|
|
|
|
|
|
template <std::size_t I,
|
|
|
|
|
|
typename PeerT,
|
|
|
|
|
|
typename DpfKey,
|
|
|
|
|
|
typename OutputType,
|
|
|
|
|
|
typename CompletionToken>
|
|
|
|
|
|
auto async_assign_wildcard_output(PeerT & peer, DpfKey & dpf,
|
|
|
|
|
|
OutputType && output_share, CompletionToken && token);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InputT,
|
|
|
|
|
|
typename OutputT = dpf::bit,
|
|
|
|
|
|
typename ...OutputTs>
|
|
|
|
|
|
HEDLEY_WARN_UNUSED_RESULT
|
|
|
|
|
|
auto make_dpfargs(InputT && x, OutputT && y = dpf::bit::one, OutputTs && ...ys);
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InputT,
|
|
|
|
|
|
typename OutputT,
|
|
|
|
|
|
typename ...OutputTs>
|
|
|
|
|
|
struct dpfargs final
|
|
|
|
|
|
{
|
|
|
|
|
|
using input_type = InputT;
|
|
|
|
|
|
using output_type = std::tuple<OutputT, OutputTs...>;
|
|
|
|
|
|
|
|
|
|
|
|
dpfargs() = delete;
|
|
|
|
|
|
dpfargs(dpfargs &&) = default;
|
|
|
|
|
|
dpfargs(const dpfargs &) = default;
|
|
|
|
|
|
|
|
|
|
|
|
input_type x;
|
|
|
|
|
|
output_type y;
|
|
|
|
|
|
private:
|
|
|
|
|
|
dpfargs(input_type x_, output_type y_) { x = x_; y = y_; }
|
|
|
|
|
|
|
|
|
|
|
|
template <typename I, typename O, typename ...Os>
|
|
|
|
|
|
friend auto make_dpfargs(I &&, O &&, Os && ...);
|
|
|
|
|
|
// friend auto make_dpfargs(InputT && x, OutputT && y, OutputTs && ...ys)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InputT,
|
|
|
|
|
|
typename OutputT,
|
|
|
|
|
|
typename ...OutputTs>
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
HEDLEY_PURE
|
|
|
|
|
|
HEDLEY_WARN_UNUSED_RESULT
|
|
|
|
|
|
auto make_dpfargs(InputT && x, OutputT && y, OutputTs && ...ys)
|
|
|
|
|
|
{
|
|
|
|
|
|
return dpfargs<std::decay_t<InputT>,
|
|
|
|
|
|
std::decay_t<OutputT>,
|
|
|
|
|
|
std::decay_t<OutputTs>...>
|
|
|
|
|
|
{ std::forward<InputT>(x),
|
|
|
|
|
|
std::make_tuple(std::forward<OutputT>(y),
|
|
|
|
|
|
std::forward<OutputTs>(ys)...) };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
2026-09-24 14:08:32 -06:00
|
|
|
|
template <typename InteriorPRG>
|
|
|
|
|
|
using root_sampler_t = std::add_pointer_t<typename InteriorPRG::block_type()>;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
|
|
namespace detail
|
|
|
|
|
|
{
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Classic single-level DPF key body (all outputs bare, at full input width,
|
2026-09-24 14:08:32 -06:00
|
|
|
|
/// equal widths, no comparison channel). `Derived` is the public `dpf_key`
|
|
|
|
|
|
/// specialization that inherits this body — threaded through only so that
|
|
|
|
|
|
/// `emplace`/`emplace_back` construct the public key type.
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @tparam Derived derived
|
|
|
|
|
|
/// @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 OutputTs output ts
|
2026-09-24 14:08:32 -06:00
|
|
|
|
template <typename Derived,
|
|
|
|
|
|
typename InteriorPRG,
|
|
|
|
|
|
typename ExteriorPRG,
|
|
|
|
|
|
typename InputT,
|
|
|
|
|
|
typename OutputT,
|
|
|
|
|
|
typename ...OutputTs>
|
|
|
|
|
|
struct classic_dpf_key_impl
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
using interior_prg = InteriorPRG;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
using tree = dpf::tree_traits<InteriorPRG>;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
using interior_node = typename InteriorPRG::block_type;
|
|
|
|
|
|
|
|
|
|
|
|
using exterior_prg = ExteriorPRG;
|
|
|
|
|
|
using exterior_node = typename ExteriorPRG::block_type;
|
|
|
|
|
|
|
|
|
|
|
|
using input_type = dpf::concrete_type_t<InputT>;
|
|
|
|
|
|
using raw_input_type = InputT;
|
|
|
|
|
|
using integral_type = utils::integral_type_from_bitlength_t<utils::bitlength_of_v<input_type>, utils::bitlength_of_v<std::size_t>>;
|
|
|
|
|
|
|
|
|
|
|
|
using outputs_tuple = std::tuple<OutputT, OutputTs...>;
|
|
|
|
|
|
template <std::size_t I>
|
|
|
|
|
|
using output_type_t = std::tuple_element_t<I, outputs_tuple>;
|
|
|
|
|
|
|
|
|
|
|
|
using concrete_outputs_tuple
|
|
|
|
|
|
= std::tuple<concrete_type_t<OutputT>, concrete_type_t<OutputTs>...>;
|
|
|
|
|
|
template <std::size_t I>
|
|
|
|
|
|
using concrete_output_type = std::tuple_element_t<I, concrete_outputs_tuple>;
|
|
|
|
|
|
using offset_type = offset_wrapper<InputT>; // N.B.: `InputT`, not `input_type`
|
|
|
|
|
|
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
|
|
|
|
|
using leaf_tuple = dpf::leaf_tuple_t<exterior_node, OutputT, OutputTs...>;
|
|
|
|
|
|
using beaver_tuple = dpf::beaver_tuple_t<exterior_node, OutputT, OutputTs...>;
|
|
|
|
|
|
using leaf_wrapper_tuple = std::tuple<dpf::leaf_wrapper<OutputT, exterior_node>, dpf::leaf_wrapper<OutputTs, exterior_node>...>;
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr std::size_t outputs_per_leaf = dpf::outputs_per_leaf_v<OutputT, exterior_node>;
|
|
|
|
|
|
static constexpr std::size_t lg_outputs_per_leaf = dpf::lg_outputs_per_leaf_v<OutputT, exterior_node>;
|
|
|
|
|
|
static constexpr std::size_t depth
|
|
|
|
|
|
= utils::bitlength_of_v<input_type> - lg_outputs_per_leaf;
|
|
|
|
|
|
static constexpr auto msb_mask = utils::msb_of_v<input_type>;
|
|
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
// Slot-meta foundation (unified with the multi-level `incr_key_base`).
|
|
|
|
|
|
// Every key carries a `slot_meta` table; for a classic (single-level,
|
|
|
|
|
|
// equal-width, no-cmp) pack all slots sit at `prefix == bitlen` and the
|
|
|
|
|
|
// packing positions match the classic `leaf_prg` layout. These are all
|
|
|
|
|
|
// `static constexpr`, so the object layout is unchanged.
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
static constexpr std::size_t num_outputs = 1 + sizeof...(OutputTs);
|
|
|
|
|
|
static constexpr std::size_t cmp_depth = 0;
|
|
|
|
|
|
static constexpr std::size_t cmp_out_bits = 0;
|
2026-09-24 20:44:07 -06:00
|
|
|
|
static constexpr std::size_t cmp_block = 0;
|
|
|
|
|
|
static constexpr bool cmp_idcf = false;
|
|
|
|
|
|
static constexpr std::size_t cmp_q = 0;
|
|
|
|
|
|
static constexpr std::size_t cmp_h = 0;
|
|
|
|
|
|
static constexpr std::size_t cmp_checkpoints = 0;
|
|
|
|
|
|
static constexpr std::size_t cmp_tail = 0;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Classic keys are single-level; the unified eval surface keeps routing
|
2026-09-24 14:08:32 -06:00
|
|
|
|
/// them through the classic `eval_*` fast paths (see `is_multilevel_key`).
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @see `is_multilevel_key`
|
2026-09-24 14:08:32 -06:00
|
|
|
|
static constexpr bool is_multilevel = false;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
static constexpr bool is_verifiable = false;
|
|
|
|
|
|
static constexpr bool is_extractable = false;
|
|
|
|
|
|
using correction_seeds_array = std::array<cs_block, 0>;
|
|
|
|
|
|
const correction_seeds_array & correction_seeds() const
|
|
|
|
|
|
{
|
|
|
|
|
|
static const correction_seeds_array empty{};
|
|
|
|
|
|
return empty;
|
|
|
|
|
|
}
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
using meta_placed_tuple = std::tuple<
|
|
|
|
|
|
dpf::detail::incr::placed<utils::bitlength_of_v<input_type>, OutputT>,
|
|
|
|
|
|
dpf::detail::incr::placed<utils::bitlength_of_v<input_type>,
|
|
|
|
|
|
OutputTs>...>;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
using meta_array = std::array<dpf::detail::incr::slot_meta, num_outputs>;
|
|
|
|
|
|
static constexpr meta_array meta =
|
|
|
|
|
|
dpf::detail::incr::build_meta<exterior_node, meta_placed_tuple>();
|
|
|
|
|
|
static constexpr std::size_t deepest_output = 0;
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t I>
|
|
|
|
|
|
static constexpr std::size_t lg_outputs_per_leaf_of = meta[I].lg_opl;
|
|
|
|
|
|
template <std::size_t I>
|
|
|
|
|
|
static constexpr std::size_t outputs_per_leaf_of =
|
|
|
|
|
|
std::size_t{1} << lg_outputs_per_leaf_of<I>;
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
2026-09-24 14:08:32 -06:00
|
|
|
|
using correction_words_array = std::array<interior_node, depth>;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
2026-09-24 14:08:32 -06:00
|
|
|
|
using correction_advice_array = std::array<psnip_uint8_t, depth>;
|
|
|
|
|
|
|
|
|
|
|
|
template <typename Emplaceable>
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
static void emplace(Emplaceable & output,
|
|
|
|
|
|
const interior_node & root,
|
|
|
|
|
|
const correction_words_array & correction_words,
|
|
|
|
|
|
const correction_advice_array & correction_advice,
|
|
|
|
|
|
const leaf_tuple & leaves,
|
|
|
|
|
|
const beaver_tuple & beavers,
|
|
|
|
|
|
const input_type & offset_share)
|
|
|
|
|
|
{
|
|
|
|
|
|
utils::dpf_emplacer<Derived, Emplaceable>::emplace(output, root, correction_words, correction_advice, leaves, beavers, offset_share);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename EmplaceableContainer>
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
static void emplace_back(EmplaceableContainer & output,
|
|
|
|
|
|
const interior_node & root,
|
|
|
|
|
|
const correction_words_array & correction_words,
|
|
|
|
|
|
const correction_advice_array & correction_advice,
|
|
|
|
|
|
const leaf_tuple & leaves,
|
|
|
|
|
|
const beaver_tuple & beavers,
|
|
|
|
|
|
const input_type & offset_share)
|
|
|
|
|
|
{
|
|
|
|
|
|
utils::dpf_back_emplacer<Derived, EmplaceableContainer>::emplace_back(output, root, correction_words, correction_advice, leaves, beavers, offset_share);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
|
|
|
|
|
static_assert(((dpf::utils::bitlength_of_output_v<OutputT, exterior_node>
|
|
|
|
|
|
== dpf::utils::bitlength_of_output_v<OutputTs, exterior_node>) && ...),
|
|
|
|
|
|
"all output types must be the same length");
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
|
|
|
|
|
static_assert(std::conjunction_v<std::is_trivially_copyable<OutputT>,
|
|
|
|
|
|
std::is_trivially_copyable<OutputTs>...>,
|
|
|
|
|
|
"all output types must be trivially copyable");
|
|
|
|
|
|
static_assert(std::conjunction_v<std::is_standard_layout<OutputT>,
|
|
|
|
|
|
std::is_standard_layout<OutputTs>...>,
|
|
|
|
|
|
"all output types must be standard layout");
|
|
|
|
|
|
// static_assert(std::has_unique_object_representations_v<input_type>);
|
|
|
|
|
|
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
constexpr classic_dpf_key_impl(interior_node root,
|
|
|
|
|
|
const correction_words_array & correction_words,
|
|
|
|
|
|
const correction_advice_array & correction_advice,
|
|
|
|
|
|
const leaf_tuple & leaves,
|
|
|
|
|
|
const beaver_tuple & beavers,
|
|
|
|
|
|
input_type offset_share)
|
|
|
|
|
|
: root_{root},
|
|
|
|
|
|
correction_words_{correction_words},
|
|
|
|
|
|
correction_advice_{correction_advice},
|
|
|
|
|
|
mutable_wildcard_mask_{dpf::utils::make_bitset(dpf::is_wildcard_v<OutputT>,
|
|
|
|
|
|
dpf::is_wildcard_v<OutputTs>...)},
|
|
|
|
|
|
leaf_nodes(get_wrappers(leaves, beavers)),
|
|
|
|
|
|
common_part_hash_{utils::get_common_part_hash(correction_words_, correction_advice_, leaf_nodes, wildcard_mask)},
|
|
|
|
|
|
offset_x{offset_share}
|
|
|
|
|
|
{ }
|
|
|
|
|
|
classic_dpf_key_impl(const classic_dpf_key_impl &) = default;
|
|
|
|
|
|
classic_dpf_key_impl(classic_dpf_key_impl &&) = default;
|
|
|
|
|
|
classic_dpf_key_impl & operator=(const classic_dpf_key_impl &) = default;
|
|
|
|
|
|
classic_dpf_key_impl & operator=(classic_dpf_key_impl &&) = default;
|
|
|
|
|
|
|
|
|
|
|
|
const interior_node & root() const { return root_; }
|
|
|
|
|
|
const correction_words_array & correction_words() const { return correction_words_; }
|
|
|
|
|
|
const correction_advice_array & correction_advice() const { return correction_advice_; }
|
|
|
|
|
|
const digest_type & common_part_hash() const { return common_part_hash_; }
|
|
|
|
|
|
|
|
|
|
|
|
std::string wildcard_bitmask() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return mutable_wildcard_mask_.to_string();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
const interior_node & correction_word(std::size_t level) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return correction_words_[level];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
psnip_uint8_t correction_advice(std::size_t level) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return correction_advice_[level];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
auto correction_word(std::size_t level, bool direction) const
|
|
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
return tree::pack_cw(correction_word(level),
|
|
|
|
|
|
correction_advice_[level], direction,
|
|
|
|
|
|
tree::is_last_level(level, depth));
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t I = 0>
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
const auto & leaf() const
|
|
|
|
|
|
{
|
|
|
|
|
|
if constexpr (dpf::is_wildcard_v<output_type_t<I>>)
|
|
|
|
|
|
{
|
|
|
|
|
|
return std::get<I>(leaf_nodes).raw_leaf();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return std::get<I>(leaf_nodes).get();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t I = 0>
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
const auto & beaver() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return std::get<I>(leaf_nodes).beaver();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
HEDLEY_PURE
|
|
|
|
|
|
constexpr bool is_wildcard(std::size_t i) const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
return wildcard_mask[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef LIBDPF_HAS_ASIO
|
|
|
|
|
|
template <std::size_t I = 0,
|
|
|
|
|
|
typename PeerT,
|
|
|
|
|
|
typename OutputType,
|
|
|
|
|
|
typename CompletionToken>
|
|
|
|
|
|
auto async_assign_leaf(PeerT & peer, OutputType && output_share,
|
|
|
|
|
|
CompletionToken && token)
|
|
|
|
|
|
{
|
|
|
|
|
|
return dpf::asio::async_assign_wildcard_output<I>(
|
|
|
|
|
|
peer, *this, std::forward<OutputType>(output_share),
|
|
|
|
|
|
std::forward<CompletionToken>(token));
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
HEDLEY_PURE
|
|
|
|
|
|
static auto traverse_interior(const interior_node & node,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
const interior_node & cw, bool dir, bool is_last = false) noexcept
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
return tree::traverse(node, cw, dir, is_last);
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Expand both children of `node` with one pipelined expand.
|
|
|
|
|
|
/// @details Equivalent to `traverse_interior(node, cw0, 0)` and
|
|
|
|
|
|
/// `traverse_interior(node, cw1, 1)`. Full-domain interval eval uses this
|
|
|
|
|
|
/// at almost every interior parent.
|
|
|
|
|
|
/// @param node the GGM node
|
|
|
|
|
|
/// @param cw0 correction word for the left child
|
|
|
|
|
|
/// @param cw1 correction word for the right child
|
|
|
|
|
|
/// @param is_last whether this is the last interior level
|
|
|
|
|
|
/// @return both children of `node`
|
2026-09-24 14:08:32 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
HEDLEY_PURE
|
|
|
|
|
|
static auto traverse_interior01(const interior_node & node,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
const interior_node & cw0, const interior_node & cw1,
|
|
|
|
|
|
bool is_last = false) noexcept
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
return tree::traverse01(node, cw0, cw1, is_last);
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Four independent `traverse_interior01` via traits batched expand.
|
|
|
|
|
|
/// @details `left[i]` / `right[i]` are the children of `parents[i]`.
|
|
|
|
|
|
/// @param parents the `parents`
|
|
|
|
|
|
/// @param cw0 the `cw0`
|
|
|
|
|
|
/// @param cw1 the `cw1`
|
|
|
|
|
|
/// @param left the `left`
|
|
|
|
|
|
/// @param right the `right`
|
|
|
|
|
|
/// @param is_last the `is_last`
|
2026-09-24 14:08:32 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
static void traverse_interior01_x4(const interior_node * HEDLEY_RESTRICT parents,
|
|
|
|
|
|
const interior_node & cw0, const interior_node & cw1,
|
|
|
|
|
|
interior_node * HEDLEY_RESTRICT left,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
interior_node * HEDLEY_RESTRICT right, bool is_last = false) noexcept
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
tree::traverse01_x4(parents, cw0, cw1, left, right, is_last);
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t I = 0,
|
|
|
|
|
|
typename LeafT>
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
HEDLEY_PURE
|
|
|
|
|
|
static auto traverse_exterior(const interior_node & node,
|
|
|
|
|
|
const LeafT & correction_word) noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
|
|
|
|
|
using output_type = std::tuple_element_t<I, concrete_outputs_tuple>;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
2026-09-24 14:08:32 -06:00
|
|
|
|
// Subtractive share: CW_if_t − mask so reconstruct(y0, y1) = y0 − y1 = β.
|
|
|
|
|
|
return dpf::subtract_leaf<output_type>(
|
|
|
|
|
|
dpf::get_if_lo_bit(correction_word, node),
|
|
|
|
|
|
make_leaf_mask_inner<exterior_prg, I, concrete_outputs_tuple>(unset_lo_2bits(node)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t I = 0>
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
auto traverse_exterior(const interior_node & node) const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
return traverse_exterior<I>(node, std::get<I>(leaf_nodes).get());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
leaf_wrapper_tuple leaf_nodes;
|
|
|
|
|
|
offset_type offset_x;
|
|
|
|
|
|
static constexpr std::array<bool, sizeof...(OutputTs)+1> wildcard_mask{dpf::is_wildcard_v<OutputT>,
|
|
|
|
|
|
dpf::is_wildcard_v<OutputTs>...};
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
static auto get_wrappers(const leaf_tuple & leaves,
|
|
|
|
|
|
const beaver_tuple & beavers)
|
|
|
|
|
|
{
|
|
|
|
|
|
outputs_tuple tmp{};
|
|
|
|
|
|
return std::apply([&beavers, &tmp](auto & ...leaf)
|
|
|
|
|
|
{
|
|
|
|
|
|
return std::apply([&leaf..., &tmp](auto & ...beaver)
|
|
|
|
|
|
{
|
|
|
|
|
|
return std::apply([&leaf..., &beaver...](auto & ...foo)
|
|
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
2026-09-24 14:08:32 -06:00
|
|
|
|
return std::make_tuple(
|
|
|
|
|
|
dpf::leaf_wrapper<std::decay_t<decltype(foo)>, exterior_node>(leaf, beaver)...
|
|
|
|
|
|
);
|
2026-09-24 23:18:10 -06:00
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}, tmp);
|
|
|
|
|
|
}, beavers);
|
|
|
|
|
|
}, leaves);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interior_node root_;
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
|
|
|
|
|
correction_words_array correction_words_;
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
|
|
|
|
|
correction_advice_array correction_advice_;
|
|
|
|
|
|
std::bitset<sizeof...(OutputTs)+1> mutable_wildcard_mask_;
|
|
|
|
|
|
digest_type common_part_hash_;
|
|
|
|
|
|
}; // struct classic_dpf_key_impl
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
|
|
namespace detail
|
|
|
|
|
|
{
|
|
|
|
|
|
namespace incr
|
|
|
|
|
|
{
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Comparison-channel storage. Value CWs, `cw_last`, and the `cmp_addend`
|
2026-09-24 14:08:32 -06:00
|
|
|
|
/// share are held at the comparison group width (`ValueCwWord`), not a full
|
|
|
|
|
|
/// padded `uint64_t` per level: a bit comparison carries 1 byte/level, a
|
|
|
|
|
|
/// `uint16_t` payload 2 bytes/level, etc. Arithmetic still runs in `uint64_t`
|
|
|
|
|
|
/// (masked); the narrow word is only the on-key / on-wire representation.
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @details Extra per-level δ-coefficients kept only for wildcard comparison payloads
|
2026-09-24 14:08:32 -06:00
|
|
|
|
/// (empty for concrete cmp keys, so their layout is unchanged). The value CWs
|
|
|
|
|
|
/// / `cw_last` are affine in the payload δ, so after keygen with δ = 0 the
|
|
|
|
|
|
/// concrete values are `base[i] + coeff[i]·δ`; `assign_cmp` patches them in
|
|
|
|
|
|
/// place with no tree re-walk / re-PRG.
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @tparam Depth depth
|
|
|
|
|
|
/// @tparam ValueCwWord value cw word
|
|
|
|
|
|
/// @tparam Wild whether the payload is a wildcard
|
|
|
|
|
|
/// @tparam TailLen tail len
|
|
|
|
|
|
/// @tparam Idcf idcf
|
2026-09-24 20:44:07 -06:00
|
|
|
|
template <std::size_t Depth, typename ValueCwWord, bool Wild,
|
|
|
|
|
|
std::size_t TailLen = 0, bool Idcf = false>
|
2026-09-24 14:08:32 -06:00
|
|
|
|
struct cmp_wild_state { };
|
2026-09-24 20:44:07 -06:00
|
|
|
|
template <std::size_t Depth, typename ValueCwWord, std::size_t TailLen, bool Idcf>
|
|
|
|
|
|
struct cmp_wild_state<Depth, ValueCwWord, true, TailLen, Idcf>
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{
|
|
|
|
|
|
std::array<ValueCwWord, Depth> value_cw_coeff{};
|
|
|
|
|
|
ValueCwWord cw_last_coeff{0};
|
2026-09-24 20:44:07 -06:00
|
|
|
|
std::array<ValueCwWord, TailLen> tail_coeff{};
|
|
|
|
|
|
std::array<ValueCwWord, Idcf ? Depth + 1 : 0> prefix_cw_coeff{};
|
2026-09-24 14:08:32 -06:00
|
|
|
|
bool assigned{false};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
|
template <std::size_t Depth, typename ValueCwWord, bool Wild = false,
|
|
|
|
|
|
std::size_t TailLen = 0, bool Blocked = false, bool Idcf = false>
|
2026-09-24 14:08:32 -06:00
|
|
|
|
struct cmp_storage
|
|
|
|
|
|
{
|
|
|
|
|
|
using value_cw_word = ValueCwWord;
|
|
|
|
|
|
using value_cw_array = std::array<value_cw_word, Depth>;
|
2026-09-24 20:44:07 -06:00
|
|
|
|
using tail_array = std::array<value_cw_word, TailLen>;
|
|
|
|
|
|
static constexpr std::size_t prefix_cw_len = Idcf ? Depth + 1 : 0;
|
|
|
|
|
|
using prefix_cw_array = std::array<value_cw_word, prefix_cw_len>;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
cmp_storage() = default;
|
|
|
|
|
|
cmp_storage(detail::cmp_meta cmp, value_cw_array value_cws,
|
2026-09-24 20:44:07 -06:00
|
|
|
|
value_cw_word cw_last_in, value_cw_word cmp_addend_in,
|
|
|
|
|
|
tail_array tail = {}, tail_array tail_coeff = {},
|
|
|
|
|
|
prefix_cw_array prefix = {}, prefix_cw_array prefix_coeff = {})
|
2026-09-24 14:08:32 -06:00
|
|
|
|
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
|
2026-09-24 20:44:07 -06:00
|
|
|
|
cmp_addend_{cmp_addend_in}, tail_{tail}, prefix_cw_{prefix}
|
|
|
|
|
|
{
|
|
|
|
|
|
if constexpr (Wild && Blocked)
|
|
|
|
|
|
wild_.tail_coeff = tail_coeff;
|
|
|
|
|
|
else
|
|
|
|
|
|
(void)tail_coeff;
|
|
|
|
|
|
if constexpr (Wild && Idcf)
|
|
|
|
|
|
wild_.prefix_cw_coeff = prefix_coeff;
|
|
|
|
|
|
else
|
|
|
|
|
|
(void)prefix_coeff;
|
|
|
|
|
|
}
|
2026-09-24 14:08:32 -06:00
|
|
|
|
cmp_storage(detail::cmp_meta cmp, value_cw_array value_cws,
|
|
|
|
|
|
value_cw_word cw_last_in, value_cw_word cmp_addend_in,
|
2026-09-24 20:44:07 -06:00
|
|
|
|
value_cw_array coeff, value_cw_word cw_last_coeff,
|
|
|
|
|
|
tail_array tail = {}, tail_array tail_coeff = {},
|
|
|
|
|
|
prefix_cw_array prefix = {}, prefix_cw_array prefix_coeff = {})
|
2026-09-24 14:08:32 -06:00
|
|
|
|
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
|
2026-09-24 20:44:07 -06:00
|
|
|
|
cmp_addend_{cmp_addend_in}, tail_{tail}, prefix_cw_{prefix}
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{
|
|
|
|
|
|
if constexpr (Wild)
|
|
|
|
|
|
{
|
|
|
|
|
|
wild_.value_cw_coeff = coeff;
|
|
|
|
|
|
wild_.cw_last_coeff = cw_last_coeff;
|
2026-09-24 20:44:07 -06:00
|
|
|
|
if constexpr (Blocked)
|
|
|
|
|
|
wild_.tail_coeff = tail_coeff;
|
|
|
|
|
|
if constexpr (Idcf)
|
|
|
|
|
|
wild_.prefix_cw_coeff = prefix_coeff;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
(void)coeff;
|
|
|
|
|
|
(void)cw_last_coeff;
|
2026-09-24 20:44:07 -06:00
|
|
|
|
(void)tail_coeff;
|
|
|
|
|
|
(void)prefix_coeff;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const value_cw_array & value_cw() const { return value_cw_; }
|
|
|
|
|
|
uint64_t value_cw(std::size_t level) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return static_cast<uint64_t>(value_cw_[level]);
|
|
|
|
|
|
}
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 23:18:10 -06:00
|
|
|
|
value_cw_word cw_last_word() const noexcept { return cw_last_; }
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
value_cw_word cmp_addend_word() const noexcept { return cmp_addend_; }
|
|
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
uint64_t cw_last() const noexcept { return static_cast<uint64_t>(cw_last_); }
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
const tail_array & tail_cw() const noexcept { return tail_; }
|
|
|
|
|
|
uint64_t tail_cw(std::size_t i) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return static_cast<uint64_t>(tail_[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
uint64_t cmp_addend() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
return static_cast<uint64_t>(cmp_addend_);
|
|
|
|
|
|
}
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
const detail::cmp_meta & cmp() const noexcept { return cmp_; }
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
bool has_cmp() const noexcept { return cmp_.active; }
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
const prefix_cw_array & prefix_cws() const noexcept { return prefix_cw_; }
|
|
|
|
|
|
uint64_t prefix_cw(std::size_t i) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return static_cast<uint64_t>(prefix_cw_[i]);
|
|
|
|
|
|
}
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
|
|
static constexpr bool is_wildcard = Wild;
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
bool cmp_assigned() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
if constexpr (Wild)
|
|
|
|
|
|
return wild_.assigned;
|
|
|
|
|
|
else
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Patch the (public) value CWs / `cw_last` in place for a resolved δ and
|
2026-09-24 14:08:32 -06:00
|
|
|
|
/// install this party's `cmp_addend` share. No-op on the CWs when there is
|
|
|
|
|
|
/// no wildcard coefficient table (trivial domain-edge cmp).
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @param delta the payload difference `if_true - if_false`
|
|
|
|
|
|
/// @param addend_share the `addend_share`
|
2026-09-24 14:08:32 -06:00
|
|
|
|
void assign_cmp_delta(uint64_t delta, uint64_t addend_share)
|
|
|
|
|
|
{
|
|
|
|
|
|
static_assert(Wild,
|
|
|
|
|
|
"assign_cmp on a key whose comparison payload is not a wildcard");
|
|
|
|
|
|
if constexpr (Wild)
|
|
|
|
|
|
{
|
|
|
|
|
|
const uint64_t mask = cmp_.mask;
|
|
|
|
|
|
for (std::size_t i = 0; i < Depth; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
const uint64_t base = static_cast<uint64_t>(value_cw_[i]);
|
|
|
|
|
|
const uint64_t c = static_cast<uint64_t>(wild_.value_cw_coeff[i]);
|
|
|
|
|
|
value_cw_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
|
|
|
|
|
|
}
|
2026-09-24 20:44:07 -06:00
|
|
|
|
if constexpr (Blocked)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (std::size_t i = 0; i < TailLen; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
const uint64_t base = static_cast<uint64_t>(tail_[i]);
|
|
|
|
|
|
const uint64_t c = static_cast<uint64_t>(wild_.tail_coeff[i]);
|
|
|
|
|
|
tail_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-09-24 14:08:32 -06:00
|
|
|
|
const uint64_t lbase = static_cast<uint64_t>(cw_last_);
|
|
|
|
|
|
const uint64_t lc = static_cast<uint64_t>(wild_.cw_last_coeff);
|
|
|
|
|
|
cw_last_ = static_cast<value_cw_word>((lbase + lc * delta) & mask);
|
2026-09-24 20:44:07 -06:00
|
|
|
|
if constexpr (Idcf)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (std::size_t i = 0; i < prefix_cw_len; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
const uint64_t base = static_cast<uint64_t>(prefix_cw_[i]);
|
|
|
|
|
|
const uint64_t c = static_cast<uint64_t>(wild_.prefix_cw_coeff[i]);
|
|
|
|
|
|
prefix_cw_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-09-24 14:08:32 -06:00
|
|
|
|
cmp_addend_ = static_cast<value_cw_word>(addend_share & mask);
|
|
|
|
|
|
wild_.assigned = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Overwrite the public final correction and this party's addend. Used when
|
|
|
|
|
|
/// the group element does not fit in the `uint64_t` the constructor takes.
|
|
|
|
|
|
/// @param last the past-the-end element of the range
|
|
|
|
|
|
/// @param addend the additive share of the off-point payload
|
|
|
|
|
|
/// @param last_coeff the `last_coeff`
|
|
|
|
|
|
void set_scalars(value_cw_word last, value_cw_word addend,
|
|
|
|
|
|
value_cw_word last_coeff)
|
|
|
|
|
|
{
|
|
|
|
|
|
cw_last_ = last;
|
|
|
|
|
|
cmp_addend_ = addend;
|
|
|
|
|
|
if constexpr (Wild)
|
|
|
|
|
|
wild_.cw_last_coeff = last_coeff;
|
|
|
|
|
|
else
|
|
|
|
|
|
(void)last_coeff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// @brief `base + coeff · δ` in `delta`'s group, then install `addend`.
|
|
|
|
|
|
/// @param delta the payload difference `if_true - if_false`
|
|
|
|
|
|
/// @param addend the additive share of the off-point payload
|
|
|
|
|
|
void assign_group(const detail::group_elem & delta, value_cw_word addend)
|
|
|
|
|
|
{
|
|
|
|
|
|
static_assert(Wild,
|
|
|
|
|
|
"assign_cmp on a key whose comparison payload is not a wildcard");
|
|
|
|
|
|
if constexpr (Wild)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto mix = [&](value_cw_word base_w, value_cw_word coeff_w) {
|
|
|
|
|
|
const auto base = detail::group_from_word(base_w, delta);
|
|
|
|
|
|
const auto coeff = detail::group_from_word(coeff_w, delta);
|
|
|
|
|
|
return detail::group_to_word<value_cw_word>(
|
|
|
|
|
|
detail::group_add(base, detail::group_mul(coeff, delta)));
|
|
|
|
|
|
};
|
|
|
|
|
|
for (std::size_t i = 0; i < Depth; ++i)
|
|
|
|
|
|
value_cw_[i] = mix(value_cw_[i], wild_.value_cw_coeff[i]);
|
|
|
|
|
|
if constexpr (Blocked)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (std::size_t i = 0; i < TailLen; ++i)
|
|
|
|
|
|
tail_[i] = mix(tail_[i], wild_.tail_coeff[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
cw_last_ = mix(cw_last_, wild_.cw_last_coeff);
|
|
|
|
|
|
if constexpr (Idcf)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (std::size_t i = 0; i < prefix_cw_len; ++i)
|
|
|
|
|
|
prefix_cw_[i] = mix(prefix_cw_[i], wild_.prefix_cw_coeff[i]);
|
|
|
|
|
|
}
|
|
|
|
|
|
cmp_addend_ = addend;
|
|
|
|
|
|
wild_.assigned = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// @brief Per-level δ coefficients for a wildcard comparison. Empty when the
|
|
|
|
|
|
/// payload is concrete.
|
|
|
|
|
|
/// @return the coefficient table
|
|
|
|
|
|
const value_cw_array & value_cw_coeff() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
if constexpr (Wild)
|
|
|
|
|
|
return wild_.value_cw_coeff;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
static const value_cw_array empty{};
|
|
|
|
|
|
return empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// @brief Coefficient of δ in `cw_last`. Zero when the payload is concrete.
|
|
|
|
|
|
/// @return the final coefficient word
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
value_cw_word cw_last_coeff_word() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
if constexpr (Wild)
|
|
|
|
|
|
return wild_.cw_last_coeff;
|
|
|
|
|
|
else
|
|
|
|
|
|
return value_cw_word{};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// @brief Tail δ coefficients for a blocked wildcard comparison.
|
|
|
|
|
|
/// @return the tail coefficient table
|
|
|
|
|
|
const tail_array & tail_coeff() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
if constexpr (Wild)
|
|
|
|
|
|
return wild_.tail_coeff;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
static const tail_array empty{};
|
|
|
|
|
|
return empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// @brief Prefix δ coefficients for an iDCF wildcard comparison.
|
|
|
|
|
|
/// @return the prefix coefficient table
|
|
|
|
|
|
const prefix_cw_array & prefix_cw_coeff() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
if constexpr (Wild)
|
|
|
|
|
|
return wild_.prefix_cw_coeff;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
static const prefix_cw_array empty{};
|
|
|
|
|
|
return empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// @brief Mark whether a wildcard comparison payload has been assigned.
|
|
|
|
|
|
/// @param assigned true once `assign_cmp` has run
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
void set_assigned(bool assigned) noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
if constexpr (Wild)
|
|
|
|
|
|
wild_.assigned = assigned;
|
|
|
|
|
|
else
|
|
|
|
|
|
(void)assigned;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-09-24 14:08:32 -06:00
|
|
|
|
private:
|
|
|
|
|
|
detail::cmp_meta cmp_{};
|
|
|
|
|
|
value_cw_array value_cw_{};
|
|
|
|
|
|
value_cw_word cw_last_{0};
|
|
|
|
|
|
value_cw_word cmp_addend_{0};
|
2026-09-24 20:44:07 -06:00
|
|
|
|
tail_array tail_{};
|
|
|
|
|
|
prefix_cw_array prefix_cw_{};
|
|
|
|
|
|
cmp_wild_state<Depth, value_cw_word, Wild, TailLen, Idcf> wild_{};
|
2026-09-24 14:08:32 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Multi-level / comparison DPF key body. `PlacedTuple` is a tuple of
|
2026-09-24 14:08:32 -06:00
|
|
|
|
/// `placed<N, T>` slots; `CmpDepth > 0` activates the comparison channel.
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @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 PlacedTuple placed tuple
|
|
|
|
|
|
/// @tparam CmpDepth cmp depth
|
|
|
|
|
|
/// @tparam CmpOutBits cmp out bits
|
|
|
|
|
|
/// @tparam CmpWild cmp wild
|
|
|
|
|
|
/// @tparam CmpBlock cmp block
|
|
|
|
|
|
/// @tparam CmpIdcf cmp idcf
|
2026-09-24 14:08:32 -06:00
|
|
|
|
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
|
|
|
|
|
typename PlacedTuple, std::size_t CmpDepth = 0,
|
2026-09-24 20:44:07 -06:00
|
|
|
|
std::size_t CmpOutBits = 0, bool CmpWild = false,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
std::size_t CmpBlock = 0, bool CmpIdcf = false,
|
|
|
|
|
|
bool IsVerifiable = false, bool IsExtractable = false>
|
2026-09-24 14:08:32 -06:00
|
|
|
|
struct incr_key_base
|
|
|
|
|
|
{
|
|
|
|
|
|
public:
|
|
|
|
|
|
using interior_prg = InteriorPRG;
|
|
|
|
|
|
using exterior_prg = ExteriorPRG;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
using tree = dpf::tree_traits<InteriorPRG>;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
using interior_node = typename InteriorPRG::block_type;
|
|
|
|
|
|
using exterior_node = typename ExteriorPRG::block_type;
|
|
|
|
|
|
using input_type = dpf::concrete_type_t<InputT>;
|
|
|
|
|
|
using placed_tuple = PlacedTuple;
|
|
|
|
|
|
using node_type = exterior_node;
|
|
|
|
|
|
static constexpr std::size_t cmp_depth = CmpDepth;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Comparison output group width in bits (0 when there is no cmp channel).
|
2026-09-24 14:08:32 -06:00
|
|
|
|
static constexpr std::size_t cmp_out_bits = CmpOutBits;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief True when the comparison payload is an unassigned wildcard.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
static constexpr bool cmp_is_wildcard = CmpWild;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief 0 = per-level path-sum. `B >= 1` = blocked checkpoints of width `B`.
|
2026-09-24 20:44:07 -06:00
|
|
|
|
static constexpr std::size_t cmp_block = CmpBlock;
|
|
|
|
|
|
static constexpr bool cmp_idcf = CmpIdcf;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
static constexpr bool is_verifiable = IsVerifiable;
|
|
|
|
|
|
static constexpr bool is_extractable = IsExtractable;
|
2026-09-24 20:44:07 -06:00
|
|
|
|
static constexpr std::size_t max_output_level =
|
|
|
|
|
|
detail::incr::max_tree_level_v<node_type, PlacedTuple>;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Residual tail width. 2 only when dropping those levels does not cut an
|
2026-09-24 20:44:07 -06:00
|
|
|
|
/// output and the comparison itself is what sets the tree height.
|
|
|
|
|
|
static constexpr std::size_t cmp_q = [] {
|
|
|
|
|
|
if (CmpBlock == 0 || CmpDepth <= 2)
|
|
|
|
|
|
return std::size_t{0};
|
|
|
|
|
|
if (max_output_level > CmpDepth - 2)
|
|
|
|
|
|
return std::size_t{0};
|
|
|
|
|
|
return std::size_t{2};
|
|
|
|
|
|
}();
|
|
|
|
|
|
static constexpr std::size_t cmp_h =
|
|
|
|
|
|
(CmpBlock == 0) ? CmpDepth : (CmpDepth - cmp_q);
|
|
|
|
|
|
static constexpr std::size_t cmp_checkpoints =
|
|
|
|
|
|
(CmpBlock == 0 || cmp_h == 0) ? 0 : (cmp_h + CmpBlock - 1) / CmpBlock;
|
|
|
|
|
|
static constexpr std::size_t cmp_tail =
|
|
|
|
|
|
(CmpBlock == 0 || cmp_q == 0) ? 0 : (std::size_t{1} << cmp_q);
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Narrowest unsigned word that holds `cmp_out_bits` bits (1 byte for a
|
2026-09-24 14:08:32 -06:00
|
|
|
|
/// bit / ≤8-bit payload, 2 for ≤16, 4 for ≤32, 8 for ≤64). Value CWs and
|
|
|
|
|
|
/// the addend share are stored in this word.
|
|
|
|
|
|
using value_cw_word = utils::integral_type_from_bitlength_t<
|
|
|
|
|
|
(CmpOutBits == 0 ? std::size_t{1} : CmpOutBits)>;
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr std::size_t num_outputs = std::tuple_size_v<PlacedTuple>;
|
|
|
|
|
|
static constexpr std::size_t input_bits = utils::bitlength_of_v<input_type>;
|
2026-09-24 20:44:07 -06:00
|
|
|
|
static constexpr std::size_t depth = std::max(max_output_level,
|
|
|
|
|
|
(CmpBlock == 0) ? CmpDepth : cmp_h);
|
|
|
|
|
|
static constexpr std::size_t value_cw_len =
|
|
|
|
|
|
(CmpBlock == 0) ? depth
|
|
|
|
|
|
: (cmp_checkpoints == 0 ? std::size_t{1} : cmp_checkpoints);
|
2026-09-24 14:08:32 -06:00
|
|
|
|
static constexpr auto msb_mask = utils::msb_of_v<input_type>;
|
|
|
|
|
|
using integral_type = utils::integral_type_from_bitlength_t<
|
|
|
|
|
|
input_bits, utils::bitlength_of_v<std::size_t>>;
|
|
|
|
|
|
|
|
|
|
|
|
static_assert(num_outputs > 0 || CmpDepth > 0,
|
|
|
|
|
|
"incremental DPF needs at least one output or a comparison channel");
|
2026-09-24 23:18:10 -06:00
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
2026-09-24 14:08:32 -06:00
|
|
|
|
static_assert(detail::incr::all_prefixes_ok_v<node_type, PlacedTuple>,
|
|
|
|
|
|
"at<N> is shorter than the packing lanes required by an output");
|
|
|
|
|
|
|
|
|
|
|
|
using correction_words_array = std::array<interior_node, depth>;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
2026-09-24 14:08:32 -06:00
|
|
|
|
using correction_advice_array = std::array<psnip_uint8_t, depth>;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
using correction_seeds_array = std::array<cs_block, IsVerifiable ? depth : 0>;
|
2026-09-24 20:44:07 -06:00
|
|
|
|
using value_cw_array = std::array<value_cw_word, value_cw_len>;
|
|
|
|
|
|
using tail_array = std::array<value_cw_word, cmp_tail>;
|
|
|
|
|
|
static constexpr std::size_t prefix_cw_len = CmpIdcf ? depth + 1 : 0;
|
|
|
|
|
|
using prefix_cw_array = std::array<value_cw_word, prefix_cw_len>;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
using meta_array = std::array<detail::incr::slot_meta, num_outputs>;
|
|
|
|
|
|
static constexpr meta_array meta =
|
|
|
|
|
|
detail::incr::build_meta<node_type, PlacedTuple>();
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Multi-level / comparison keys route through the slot-aware eval path.
|
|
|
|
|
|
/// Classic-shaped packs (every slot at full input width, no cmp) keep the
|
|
|
|
|
|
/// classic `eval_*` fast path even when verifiable/extractable phantoms are
|
|
|
|
|
|
/// present.
|
|
|
|
|
|
static constexpr bool is_multilevel = [] {
|
|
|
|
|
|
if constexpr (CmpDepth > 0)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
if constexpr (num_outputs == 0)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
for (std::size_t i = 0; i < num_outputs; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (meta[i].prefix != input_bits)
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}();
|
|
|
|
|
|
|
2026-09-24 14:08:32 -06:00
|
|
|
|
template <std::size_t I, typename = void>
|
|
|
|
|
|
struct output_type_at
|
|
|
|
|
|
{
|
|
|
|
|
|
using type = void;
|
|
|
|
|
|
};
|
|
|
|
|
|
template <std::size_t I>
|
|
|
|
|
|
struct output_type_at<I, std::enable_if_t<(I < num_outputs)>>
|
|
|
|
|
|
{
|
|
|
|
|
|
using type = typename std::tuple_element_t<I, PlacedTuple>::output_type;
|
|
|
|
|
|
};
|
|
|
|
|
|
template <std::size_t I>
|
|
|
|
|
|
using output_type_t = typename output_type_at<I>::type;
|
|
|
|
|
|
template <std::size_t I>
|
|
|
|
|
|
using concrete_output_type = concrete_type_t<output_type_t<I>>;
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t I>
|
|
|
|
|
|
static constexpr std::size_t lg_outputs_per_leaf_of =
|
|
|
|
|
|
(num_outputs > 0) ? meta[I].lg_opl : 0;
|
|
|
|
|
|
template <std::size_t I>
|
|
|
|
|
|
static constexpr std::size_t outputs_per_leaf_of = std::size_t{1}
|
|
|
|
|
|
<< lg_outputs_per_leaf_of<I>;
|
|
|
|
|
|
private:
|
|
|
|
|
|
template <std::size_t... Is>
|
|
|
|
|
|
static auto wrapper_tuple_t(std::index_sequence<Is...>)
|
|
|
|
|
|
-> std::tuple<
|
|
|
|
|
|
dpf::leaf_wrapper<output_type_t<Is>, exterior_node>...>;
|
|
|
|
|
|
template <std::size_t... Is>
|
|
|
|
|
|
static auto leaf_tuple_type(std::index_sequence<Is...>)
|
|
|
|
|
|
-> std::tuple<
|
|
|
|
|
|
dpf::leaf_node_t<exterior_node, concrete_output_type<Is>>...>;
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
using leaf_wrapper_tuple = decltype(wrapper_tuple_t(
|
|
|
|
|
|
std::make_index_sequence<num_outputs>{}));
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Raw leaf shares (pre-wrapper), matching classic `leaf_tuple` for asio.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
using leaf_tuple = decltype(leaf_tuple_type(
|
|
|
|
|
|
std::make_index_sequence<num_outputs>{}));
|
|
|
|
|
|
using offset_type = offset_wrapper<InputT>;
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t... Is>
|
|
|
|
|
|
static constexpr auto wildcard_mask_tuple(std::index_sequence<Is...>)
|
|
|
|
|
|
{
|
|
|
|
|
|
return std::make_tuple(dpf::is_wildcard_v<output_type_t<Is>>...);
|
|
|
|
|
|
}
|
|
|
|
|
|
static constexpr auto wildcard_mask =
|
|
|
|
|
|
wildcard_mask_tuple(std::make_index_sequence<num_outputs>{});
|
|
|
|
|
|
|
|
|
|
|
|
static constexpr std::size_t deepest_prefix = [] {
|
|
|
|
|
|
if constexpr (num_outputs == 0)
|
|
|
|
|
|
return CmpDepth;
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
std::size_t m = 0;
|
|
|
|
|
|
for (std::size_t i = 0; i < num_outputs; ++i)
|
|
|
|
|
|
m = std::max(m, meta[i].prefix);
|
|
|
|
|
|
return m;
|
|
|
|
|
|
}
|
|
|
|
|
|
}();
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief First output (source order) whose prefix equals `deepest_prefix`.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
static constexpr std::size_t deepest_output = [] {
|
|
|
|
|
|
if constexpr (num_outputs == 0)
|
|
|
|
|
|
return std::size_t{0};
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
for (std::size_t i = 0; i < num_outputs; ++i)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (meta[i].prefix == deepest_prefix)
|
|
|
|
|
|
return i;
|
|
|
|
|
|
}
|
|
|
|
|
|
return std::size_t{0};
|
|
|
|
|
|
}
|
|
|
|
|
|
}();
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Classic-shaped packing traits for deepest-group interval/sequence APIs.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
static constexpr std::size_t outputs_per_leaf =
|
|
|
|
|
|
(num_outputs > 0) ? outputs_per_leaf_of<deepest_output> : 1;
|
|
|
|
|
|
static constexpr std::size_t lg_outputs_per_leaf =
|
|
|
|
|
|
(num_outputs > 0) ? lg_outputs_per_leaf_of<deepest_output> : 0;
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t... Is>
|
|
|
|
|
|
static auto addend_tuple_t(std::index_sequence<Is...>)
|
|
|
|
|
|
-> std::tuple<output_type_t<Is>...>;
|
|
|
|
|
|
using addend_tuple = decltype(addend_tuple_t(
|
|
|
|
|
|
std::make_index_sequence<num_outputs>{}));
|
|
|
|
|
|
|
|
|
|
|
|
incr_key_base(interior_node root,
|
|
|
|
|
|
const correction_words_array & correction_words,
|
|
|
|
|
|
const correction_advice_array & correction_advice,
|
|
|
|
|
|
leaf_wrapper_tuple leaves, input_type offset_share,
|
|
|
|
|
|
detail::cmp_meta cmp = {}, value_cw_array value_cws = {},
|
|
|
|
|
|
uint64_t cw_last_in = 0, uint64_t cmp_addend_in = 0,
|
|
|
|
|
|
addend_tuple addends = {}, value_cw_array value_cw_coeff = {},
|
2026-09-24 20:44:07 -06:00
|
|
|
|
uint64_t cw_last_coeff_in = 0, tail_array tail_in = {},
|
|
|
|
|
|
tail_array tail_coeff_in = {}, prefix_cw_array prefix_in = {},
|
2026-09-24 23:18:10 -06:00
|
|
|
|
prefix_cw_array prefix_coeff_in = {},
|
|
|
|
|
|
correction_seeds_array correction_seeds = {})
|
2026-09-24 14:08:32 -06:00
|
|
|
|
: leaf_nodes{std::move(leaves)},
|
|
|
|
|
|
offset_x{offset_share},
|
|
|
|
|
|
cmp_store_{cmp, value_cws,
|
|
|
|
|
|
static_cast<value_cw_word>(cw_last_in),
|
|
|
|
|
|
static_cast<value_cw_word>(cmp_addend_in),
|
|
|
|
|
|
value_cw_coeff,
|
2026-09-24 20:44:07 -06:00
|
|
|
|
static_cast<value_cw_word>(cw_last_coeff_in),
|
|
|
|
|
|
tail_in, tail_coeff_in, prefix_in, prefix_coeff_in},
|
2026-09-24 14:08:32 -06:00
|
|
|
|
public_addends{std::move(addends)},
|
|
|
|
|
|
root_{root},
|
|
|
|
|
|
correction_words_{correction_words},
|
|
|
|
|
|
correction_advice_{correction_advice},
|
2026-09-24 23:18:10 -06:00
|
|
|
|
correction_seeds_{correction_seeds},
|
2026-09-24 14:08:32 -06:00
|
|
|
|
common_part_hash_{utils::get_common_part_hash(correction_words_,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
correction_advice_, leaf_nodes, wildcard_mask, correction_seeds_)}
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
incr_key_base(const incr_key_base &) = default;
|
|
|
|
|
|
incr_key_base(incr_key_base &&) = default;
|
|
|
|
|
|
incr_key_base & operator=(const incr_key_base &) = default;
|
|
|
|
|
|
incr_key_base & operator=(incr_key_base &&) = default;
|
|
|
|
|
|
|
|
|
|
|
|
const interior_node & root() const { return root_; }
|
|
|
|
|
|
const correction_words_array & correction_words() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return correction_words_;
|
|
|
|
|
|
}
|
|
|
|
|
|
const correction_advice_array & correction_advice() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return correction_advice_;
|
|
|
|
|
|
}
|
2026-09-24 23:18:10 -06:00
|
|
|
|
const correction_seeds_array & correction_seeds() const
|
|
|
|
|
|
{
|
|
|
|
|
|
return correction_seeds_;
|
|
|
|
|
|
}
|
2026-09-24 14:08:32 -06:00
|
|
|
|
const value_cw_array & value_cw() const { return cmp_store_.value_cw(); }
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
uint64_t cw_last() const noexcept { return cmp_store_.cw_last(); }
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 23:18:10 -06:00
|
|
|
|
value_cw_word cw_last_word() const noexcept { return cmp_store_.cw_last_word(); }
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
value_cw_word cmp_addend_word() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
return cmp_store_.cmp_addend_word();
|
|
|
|
|
|
}
|
|
|
|
|
|
void set_cmp_scalars(value_cw_word last, value_cw_word addend,
|
|
|
|
|
|
value_cw_word last_coeff)
|
|
|
|
|
|
{
|
|
|
|
|
|
cmp_store_.set_scalars(last, addend, last_coeff);
|
|
|
|
|
|
}
|
|
|
|
|
|
/// @brief Mark whether a wildcard comparison payload has been assigned.
|
|
|
|
|
|
/// @param assigned true once `assign_cmp` has run
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
void set_cmp_assigned(bool assigned) noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
cmp_store_.set_assigned(assigned);
|
|
|
|
|
|
}
|
|
|
|
|
|
const value_cw_array & value_cw_coeff() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
return cmp_store_.value_cw_coeff();
|
|
|
|
|
|
}
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
value_cw_word cw_last_coeff_word() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
return cmp_store_.cw_last_coeff_word();
|
|
|
|
|
|
}
|
|
|
|
|
|
const tail_array & tail_coeff() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
return cmp_store_.tail_coeff();
|
|
|
|
|
|
}
|
|
|
|
|
|
const prefix_cw_array & prefix_cw_coeff() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
return cmp_store_.prefix_cw_coeff();
|
|
|
|
|
|
}
|
|
|
|
|
|
void assign_cmp_group(const detail::group_elem & delta, value_cw_word addend)
|
|
|
|
|
|
{
|
|
|
|
|
|
cmp_store_.assign_group(delta, addend);
|
|
|
|
|
|
}
|
|
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 20:44:07 -06:00
|
|
|
|
const prefix_cw_array & prefix_cws() const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
return cmp_store_.prefix_cws();
|
|
|
|
|
|
}
|
|
|
|
|
|
uint64_t prefix_cw(std::size_t i) const { return cmp_store_.prefix_cw(i); }
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Party-local share of the constant absorb (`if_false`, or
|
2026-09-24 14:08:32 -06:00
|
|
|
|
/// `δ + if_false` when `eval_as_ge`). Reconstructs with the peer share.
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @return Party-local share of the constant absorb (`if_false`, or `δ + if_false` when
|
|
|
|
|
|
/// `eval_as_ge`)
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
uint64_t cmp_addend() const noexcept { return cmp_store_.cmp_addend(); }
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
const detail::cmp_meta & cmp() const noexcept { return cmp_store_.cmp(); }
|
|
|
|
|
|
const digest_type & common_part_hash() const { return common_part_hash_; }
|
|
|
|
|
|
const leaf_wrapper_tuple & leaves() const { return leaf_nodes; }
|
|
|
|
|
|
|
|
|
|
|
|
const interior_node & correction_word(std::size_t level) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return correction_words_[level];
|
|
|
|
|
|
}
|
|
|
|
|
|
psnip_uint8_t correction_advice(std::size_t level) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return correction_advice_[level];
|
|
|
|
|
|
}
|
|
|
|
|
|
auto correction_word(std::size_t level, bool direction) const
|
|
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
return tree::pack_cw(correction_word(level),
|
|
|
|
|
|
correction_advice_[level], direction,
|
|
|
|
|
|
tree::is_last_level(level, depth));
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
uint64_t value_cw(std::size_t level) const { return cmp_store_.value_cw(level); }
|
2026-09-24 20:44:07 -06:00
|
|
|
|
const tail_array & tail_cw() const { return cmp_store_.tail_cw(); }
|
|
|
|
|
|
uint64_t tail_cw(std::size_t i) const { return cmp_store_.tail_cw(i); }
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
|
|
template <std::size_t I = 0>
|
|
|
|
|
|
const auto & leaf() const
|
|
|
|
|
|
{
|
|
|
|
|
|
static_assert(num_outputs > 0, "cmp-only key has no leaves");
|
|
|
|
|
|
if constexpr (dpf::is_wildcard_v<output_type_t<I>>)
|
|
|
|
|
|
return std::get<I>(leaf_nodes).raw_leaf();
|
|
|
|
|
|
else
|
|
|
|
|
|
return std::get<I>(leaf_nodes).get();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t I = 0>
|
|
|
|
|
|
const auto & beaver() const
|
|
|
|
|
|
{
|
|
|
|
|
|
static_assert(num_outputs > 0, "cmp-only key has no beavers");
|
|
|
|
|
|
return std::get<I>(leaf_nodes).beaver();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef LIBDPF_HAS_ASIO
|
|
|
|
|
|
template <std::size_t I = 0,
|
|
|
|
|
|
typename PeerT,
|
|
|
|
|
|
typename OutputType,
|
|
|
|
|
|
typename CompletionToken>
|
|
|
|
|
|
auto async_assign_leaf(PeerT & peer, OutputType && output_share,
|
|
|
|
|
|
CompletionToken && token)
|
|
|
|
|
|
{
|
|
|
|
|
|
static_assert(num_outputs > 0, "cmp-only key has no leaves");
|
|
|
|
|
|
static_assert(dpf::is_wildcard_v<output_type_t<I>>,
|
|
|
|
|
|
"async_assign_leaf requires a wildcard output slot");
|
|
|
|
|
|
return dpf::asio::async_assign_wildcard_output<I>(
|
|
|
|
|
|
peer, *this, std::forward<OutputType>(output_share),
|
|
|
|
|
|
std::forward<CompletionToken>(token));
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
HEDLEY_PURE
|
|
|
|
|
|
static auto traverse_interior(const interior_node & node,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
const interior_node & cw, bool dir, bool is_last = false) noexcept
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
return tree::traverse(node, cw, dir, is_last);
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
HEDLEY_PURE
|
|
|
|
|
|
static auto traverse_interior01(const interior_node & node,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
const interior_node & cw0, const interior_node & cw1,
|
|
|
|
|
|
bool is_last = false) noexcept
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
return tree::traverse01(node, cw0, cw1, is_last);
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
HEDLEY_NO_THROW
|
|
|
|
|
|
HEDLEY_ALWAYS_INLINE
|
|
|
|
|
|
static void traverse_interior01_x4(const interior_node * HEDLEY_RESTRICT parents,
|
|
|
|
|
|
const interior_node & cw0, const interior_node & cw1,
|
|
|
|
|
|
interior_node * HEDLEY_RESTRICT left,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
interior_node * HEDLEY_RESTRICT right, bool is_last = false) noexcept
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
tree::traverse01_x4(parents, cw0, cw1, left, right, is_last);
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t I = 0>
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
auto traverse_exterior(const interior_node & node) const noexcept
|
|
|
|
|
|
{
|
|
|
|
|
|
static_assert(num_outputs > 0, "cmp-only key has no exterior outputs");
|
|
|
|
|
|
using Out = concrete_output_type<I>;
|
|
|
|
|
|
constexpr auto pos =
|
|
|
|
|
|
meta[I].pos_base + meta[I].index_in_group * meta[I].block_len;
|
|
|
|
|
|
constexpr auto count = meta[I].block_len;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
2026-09-24 14:08:32 -06:00
|
|
|
|
using leaf_type = dpf::leaf_node_t<exterior_node, Out>;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
2026-09-24 14:08:32 -06:00
|
|
|
|
leaf_type mask{};
|
|
|
|
|
|
auto seed_ =
|
|
|
|
|
|
utils::to_exterior_node<exterior_node>(unset_lo_2bits(node));
|
2026-09-24 23:18:10 -06:00
|
|
|
|
if constexpr (IsExtractable)
|
|
|
|
|
|
{
|
|
|
|
|
|
detail::vdpf::extractable_leaf_prg<exterior_prg>::eval(seed_,
|
|
|
|
|
|
leaf_blocks<exterior_node>(mask),
|
|
|
|
|
|
static_cast<psnip_uint32_t>(count),
|
|
|
|
|
|
static_cast<psnip_uint32_t>(pos));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
exterior_prg::eval(seed_, leaf_blocks<exterior_node>(mask),
|
|
|
|
|
|
static_cast<psnip_uint32_t>(count),
|
|
|
|
|
|
static_cast<psnip_uint32_t>(pos));
|
|
|
|
|
|
}
|
2026-09-24 14:08:32 -06:00
|
|
|
|
return dpf::subtract_leaf<Out>(
|
|
|
|
|
|
dpf::get_if_lo_bit(std::get<I>(leaf_nodes).get(), node), mask);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
leaf_wrapper_tuple leaf_nodes;
|
|
|
|
|
|
offset_type offset_x;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Public `if_false` addends for `eq` / `eq_at` slots.
|
2026-09-24 14:08:32 -06:00
|
|
|
|
addend_tuple public_addends{};
|
|
|
|
|
|
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
bool has_cmp() const noexcept { return cmp_store_.has_cmp(); }
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief True once a wildcard comparison payload has been assigned (always true
|
2026-09-24 14:08:32 -06:00
|
|
|
|
/// for concrete cmp keys and for keys without a comparison channel).
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @return True once a wildcard comparison payload has been assigned (always true for concrete
|
|
|
|
|
|
/// cmp keys and for keys without a comparison channel)
|
2026-09-24 20:44:07 -06:00
|
|
|
|
HEDLEY_NO_THROW
|
2026-09-24 14:08:32 -06:00
|
|
|
|
bool cmp_assigned() const noexcept { return cmp_store_.cmp_assigned(); }
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @brief Patch the value CWs / `cw_last` for a resolved payload δ and install
|
2026-09-24 14:08:32 -06:00
|
|
|
|
/// this party's `cmp_addend` share. Only valid for wildcard cmp keys; see
|
|
|
|
|
|
/// the free `dpf::assign_cmp`. No tree re-walk / re-PRG.
|
2026-09-24 23:18:10 -06:00
|
|
|
|
/// @param delta the payload difference `if_true - if_false`
|
|
|
|
|
|
/// @param addend_share the `addend_share`
|
2026-09-24 14:08:32 -06:00
|
|
|
|
void assign_cmp_delta(uint64_t delta, uint64_t addend_share)
|
|
|
|
|
|
{
|
|
|
|
|
|
cmp_store_.assign_cmp_delta(delta, addend_share);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
2026-09-24 20:44:07 -06:00
|
|
|
|
cmp_storage<value_cw_len, value_cw_word, CmpWild, cmp_tail, (CmpBlock > 0),
|
|
|
|
|
|
CmpIdcf>
|
|
|
|
|
|
cmp_store_{};
|
2026-09-24 14:08:32 -06:00
|
|
|
|
interior_node root_;
|
|
|
|
|
|
correction_words_array correction_words_;
|
|
|
|
|
|
correction_advice_array correction_advice_;
|
2026-09-24 23:18:10 -06:00
|
|
|
|
correction_seeds_array correction_seeds_{};
|
2026-09-24 14:08:32 -06:00
|
|
|
|
digest_type common_part_hash_;
|
|
|
|
|
|
}; // struct incr_key_base
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace incr
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
// Unified `dpf_key`: one key type for classic, multi-level (`at<N>`), and
|
|
|
|
|
|
// comparison (`cmp_channel_tag`) packs. Each of OutputT/OutputTs is one of:
|
|
|
|
|
|
// - a bare output type (planted at full input bitlength)
|
|
|
|
|
|
// - a `detail::incr::placed<N,T>` (from `at<N>`)
|
|
|
|
|
|
// - a `cmp_channel_tag<Depth>` (phantom: sets the comparison channel depth)
|
|
|
|
|
|
// Classic-shaped packs (all bare) keep the byte-identical single-level layout.
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
namespace detail
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
template <typename Derived, typename InteriorPRG, typename ExteriorPRG,
|
|
|
|
|
|
typename InputT, typename OutputT, typename ...OutputTs>
|
|
|
|
|
|
using dpf_key_base_t = std::conditional_t<
|
|
|
|
|
|
dpf::detail::incr::is_classic_pack_v<OutputT, OutputTs...>,
|
|
|
|
|
|
classic_dpf_key_impl<Derived, InteriorPRG, ExteriorPRG, InputT,
|
|
|
|
|
|
OutputT, OutputTs...>,
|
|
|
|
|
|
dpf::detail::incr::incr_key_base<InteriorPRG, ExteriorPRG, InputT,
|
|
|
|
|
|
typename dpf::detail::incr::normalize_pack<
|
|
|
|
|
|
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
|
|
|
|
|
OutputT, OutputTs...>::placed_tuple,
|
|
|
|
|
|
dpf::detail::incr::normalize_pack<
|
|
|
|
|
|
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
|
|
|
|
|
OutputT, OutputTs...>::cmp_depth,
|
|
|
|
|
|
dpf::detail::incr::normalize_pack<
|
|
|
|
|
|
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
|
|
|
|
|
OutputT, OutputTs...>::cmp_out_bits,
|
|
|
|
|
|
dpf::detail::incr::normalize_pack<
|
|
|
|
|
|
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
2026-09-24 20:44:07 -06:00
|
|
|
|
OutputT, OutputTs...>::cmp_wild,
|
|
|
|
|
|
dpf::detail::incr::normalize_pack<
|
|
|
|
|
|
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
|
|
|
|
|
OutputT, OutputTs...>::cmp_block,
|
|
|
|
|
|
dpf::detail::incr::normalize_pack<
|
|
|
|
|
|
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
OutputT, OutputTs...>::cmp_idcf,
|
|
|
|
|
|
dpf::detail::incr::normalize_pack<
|
|
|
|
|
|
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
|
|
|
|
|
OutputT, OutputTs...>::is_verifiable,
|
|
|
|
|
|
dpf::detail::incr::normalize_pack<
|
|
|
|
|
|
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
|
|
|
|
|
|
OutputT, OutputTs...>::is_extractable>>;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InteriorPRG,
|
|
|
|
|
|
typename ExteriorPRG,
|
|
|
|
|
|
typename InputT,
|
|
|
|
|
|
typename OutputT,
|
|
|
|
|
|
typename ...OutputTs>
|
|
|
|
|
|
struct dpf_key
|
|
|
|
|
|
: detail::dpf_key_base_t<
|
|
|
|
|
|
dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>,
|
|
|
|
|
|
InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>
|
|
|
|
|
|
{
|
|
|
|
|
|
using base_type = detail::dpf_key_base_t<
|
|
|
|
|
|
dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>,
|
|
|
|
|
|
InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>;
|
|
|
|
|
|
using base_type::base_type;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
namespace detail
|
|
|
|
|
|
{
|
|
|
|
|
|
namespace incr
|
|
|
|
|
|
{
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
// Assemble the public dpf_key type for a (PlacedTuple, CmpDepth, flags) pack by
|
|
|
|
|
|
// expanding the placed slots into the output pack and appending phantom tags.
|
|
|
|
|
|
//
|
|
|
|
|
|
// `dpf_key` always takes an output type. A comparison-only key (empty placed
|
|
|
|
|
|
// pack, CmpDepth > 0) is `dpf_key<..., cmp_channel_tag<...>>`. The no-comparison
|
|
|
|
|
|
// form is a separate specialization so an empty pack is not named as
|
|
|
|
|
|
// `dpf_key<Interior, Exterior, Input>` — `std::conditional_t` would require
|
|
|
|
|
|
// that type to be valid even when CmpDepth > 0.
|
|
|
|
|
|
template <bool WithCmp, typename InteriorPRG, typename ExteriorPRG,
|
|
|
|
|
|
typename InputT, std::size_t CmpDepth, std::size_t CmpOutBits,
|
|
|
|
|
|
bool CmpWild, std::size_t CmpBlock, bool CmpIdcf, typename ...Ps>
|
|
|
|
|
|
struct plain_assembled_key;
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
|
|
|
|
|
std::size_t CmpDepth, std::size_t CmpOutBits, bool CmpWild,
|
|
|
|
|
|
std::size_t CmpBlock, bool CmpIdcf, typename ...Ps>
|
|
|
|
|
|
struct plain_assembled_key<true, InteriorPRG, ExteriorPRG, InputT,
|
|
|
|
|
|
CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf, Ps...>
|
|
|
|
|
|
{
|
|
|
|
|
|
using type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...,
|
|
|
|
|
|
dpf::cmp_channel_tag<CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>>;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
|
|
|
|
|
std::size_t CmpDepth, std::size_t CmpOutBits, bool CmpWild,
|
|
|
|
|
|
std::size_t CmpBlock, bool CmpIdcf, typename P0, typename ...Ps>
|
|
|
|
|
|
struct plain_assembled_key<false, InteriorPRG, ExteriorPRG, InputT,
|
|
|
|
|
|
CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf, P0, Ps...>
|
|
|
|
|
|
{
|
|
|
|
|
|
using type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, P0, Ps...>;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t CmpDepth, std::size_t CmpOutBits, bool CmpWild,
|
|
|
|
|
|
std::size_t CmpBlock, bool CmpIdcf, bool IsVerifiable,
|
|
|
|
|
|
bool IsExtractable, typename InteriorPRG, typename ExteriorPRG,
|
|
|
|
|
|
typename InputT, typename ...Ps>
|
|
|
|
|
|
struct assemble_key;
|
|
|
|
|
|
|
2026-09-24 14:08:32 -06:00
|
|
|
|
template <std::size_t CmpDepth, std::size_t CmpOutBits, bool CmpWild,
|
2026-09-24 20:44:07 -06:00
|
|
|
|
std::size_t CmpBlock, bool CmpIdcf, typename InteriorPRG,
|
2026-09-24 14:08:32 -06:00
|
|
|
|
typename ExteriorPRG, typename InputT, typename ...Ps>
|
2026-09-24 23:18:10 -06:00
|
|
|
|
struct assemble_key<CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf, false,
|
|
|
|
|
|
false, InteriorPRG, ExteriorPRG, InputT, Ps...>
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
using type = typename plain_assembled_key<(CmpDepth > 0),
|
|
|
|
|
|
InteriorPRG, ExteriorPRG, InputT, CmpDepth, CmpOutBits, CmpWild,
|
|
|
|
|
|
CmpBlock, CmpIdcf, Ps...>::type;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t CmpDepth, std::size_t CmpOutBits, bool CmpWild,
|
|
|
|
|
|
std::size_t CmpBlock, bool CmpIdcf, typename InteriorPRG,
|
|
|
|
|
|
typename ExteriorPRG, typename InputT, typename ...Ps>
|
|
|
|
|
|
struct assemble_key<CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf, true,
|
|
|
|
|
|
false, InteriorPRG, ExteriorPRG, InputT, Ps...>
|
|
|
|
|
|
{
|
|
|
|
|
|
using with_cmp = std::conditional_t<
|
|
|
|
|
|
(CmpDepth > 0),
|
|
|
|
|
|
dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...,
|
|
|
|
|
|
dpf::cmp_channel_tag<CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>,
|
|
|
|
|
|
dpf::verifiable>,
|
|
|
|
|
|
dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps..., dpf::verifiable>>;
|
|
|
|
|
|
using type = with_cmp;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <std::size_t CmpDepth, std::size_t CmpOutBits, bool CmpWild,
|
|
|
|
|
|
std::size_t CmpBlock, bool CmpIdcf, typename InteriorPRG,
|
|
|
|
|
|
typename ExteriorPRG, typename InputT, typename ...Ps>
|
|
|
|
|
|
struct assemble_key<CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf, false,
|
|
|
|
|
|
true, InteriorPRG, ExteriorPRG, InputT, Ps...>
|
|
|
|
|
|
{
|
|
|
|
|
|
using type = std::conditional_t<
|
|
|
|
|
|
(CmpDepth > 0),
|
|
|
|
|
|
dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...,
|
|
|
|
|
|
dpf::cmp_channel_tag<CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>,
|
|
|
|
|
|
dpf::extractable>,
|
|
|
|
|
|
dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps..., dpf::extractable>>;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
};
|
2026-09-24 23:18:10 -06:00
|
|
|
|
|
|
|
|
|
|
template <std::size_t CmpDepth, std::size_t CmpOutBits, bool CmpWild,
|
|
|
|
|
|
std::size_t CmpBlock, bool CmpIdcf, typename InteriorPRG,
|
2026-09-24 14:08:32 -06:00
|
|
|
|
typename ExteriorPRG, typename InputT, typename ...Ps>
|
2026-09-24 23:18:10 -06:00
|
|
|
|
struct assemble_key<CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf, true,
|
|
|
|
|
|
true, InteriorPRG, ExteriorPRG, InputT, Ps...>
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
using type = std::conditional_t<
|
|
|
|
|
|
(CmpDepth > 0),
|
|
|
|
|
|
dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...,
|
|
|
|
|
|
dpf::cmp_channel_tag<CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>,
|
|
|
|
|
|
dpf::verifiable, dpf::extractable>,
|
|
|
|
|
|
dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...,
|
|
|
|
|
|
dpf::verifiable, dpf::extractable>>;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
|
|
|
|
|
typename PlacedTuple, std::size_t CmpDepth, std::size_t CmpOutBits = 0,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
bool CmpWild = false, std::size_t CmpBlock = 0, bool CmpIdcf = false,
|
|
|
|
|
|
bool IsVerifiable = false, bool IsExtractable = false>
|
2026-09-24 14:08:32 -06:00
|
|
|
|
struct incr_dpf_key_of;
|
|
|
|
|
|
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
|
|
|
|
|
typename ...Ps, std::size_t CmpDepth, std::size_t CmpOutBits,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
bool CmpWild, std::size_t CmpBlock, bool CmpIdcf,
|
|
|
|
|
|
bool IsVerifiable, bool IsExtractable>
|
2026-09-24 14:08:32 -06:00
|
|
|
|
struct incr_dpf_key_of<InteriorPRG, ExteriorPRG, InputT, std::tuple<Ps...>,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf, IsVerifiable, IsExtractable>
|
2026-09-24 14:08:32 -06:00
|
|
|
|
{
|
2026-09-24 20:44:07 -06:00
|
|
|
|
using type = typename assemble_key<CmpDepth, CmpOutBits, CmpWild, CmpBlock,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
CmpIdcf, IsVerifiable, IsExtractable, InteriorPRG, ExteriorPRG, InputT,
|
|
|
|
|
|
Ps...>::type;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
};
|
|
|
|
|
|
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
|
|
|
|
|
|
typename PlacedTuple, std::size_t CmpDepth, std::size_t CmpOutBits = 0,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
bool CmpWild = false, std::size_t CmpBlock = 0, bool CmpIdcf = false,
|
|
|
|
|
|
bool IsVerifiable = false, bool IsExtractable = false>
|
2026-09-24 14:08:32 -06:00
|
|
|
|
using incr_dpf_key_of_t = typename incr_dpf_key_of<InteriorPRG, ExteriorPRG,
|
2026-09-24 23:18:10 -06:00
|
|
|
|
InputT, PlacedTuple, CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf,
|
|
|
|
|
|
IsVerifiable, IsExtractable>::type;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
|
|
} // namespace incr
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
|
|
template <typename PRG>
|
|
|
|
|
|
struct pseudorandom_root_sampler
|
|
|
|
|
|
{
|
|
|
|
|
|
using root_type = typename PRG::block_type;
|
|
|
|
|
|
|
|
|
|
|
|
pseudorandom_root_sampler(
|
|
|
|
|
|
root_type && seed = dpf::uniform_sample<root_type>())
|
|
|
|
|
|
: seed_{seed}, counter_{0} { }
|
|
|
|
|
|
|
|
|
|
|
|
root_type operator()(psnip_uint32_t i) const
|
|
|
|
|
|
{
|
|
|
|
|
|
return PRG::eval(seed_, i);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
root_type operator()()
|
|
|
|
|
|
{
|
|
|
|
|
|
return this->operator()(counter_.fetch_add(1));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const root_type & seed() const { return seed_; }
|
|
|
|
|
|
psnip_uint32_t count() const { return counter_; }
|
|
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
|
root_type seed_;
|
|
|
|
|
|
std::atomic_uint32_t counter_;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
namespace utils
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InteriorPRG,
|
|
|
|
|
|
typename ExteriorPRG,
|
|
|
|
|
|
typename InputT,
|
|
|
|
|
|
typename OutputT,
|
|
|
|
|
|
typename ...OutputTs>
|
|
|
|
|
|
struct dpf_type
|
|
|
|
|
|
{
|
|
|
|
|
|
using type = dpf_key<InteriorPRG, ExteriorPRG,
|
|
|
|
|
|
std::decay_t<InputT>,
|
|
|
|
|
|
std::decay_t<OutputT>,
|
|
|
|
|
|
std::decay_t<OutputTs>...>;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InteriorPRG,
|
|
|
|
|
|
typename ExteriorPRG,
|
|
|
|
|
|
typename InputT,
|
|
|
|
|
|
typename OutputT,
|
|
|
|
|
|
typename ...OutputTs>
|
|
|
|
|
|
using dpf_type_t = typename dpf_type<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>::type;
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace utils
|
|
|
|
|
|
|
|
|
|
|
|
namespace detail
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InteriorPRG,
|
|
|
|
|
|
typename ExteriorPRG,
|
|
|
|
|
|
typename InputT,
|
|
|
|
|
|
typename OutputT,
|
|
|
|
|
|
typename ...OutputTs>
|
|
|
|
|
|
auto make_dpf_impl(dpfargs<InputT, OutputT, OutputTs...> args, root_sampler_t<InteriorPRG> && root_sampler = dpf::uniform_sample<typename InteriorPRG::block_type>)
|
|
|
|
|
|
{
|
|
|
|
|
|
using dpf_type = utils::dpf_type_t<InteriorPRG, ExteriorPRG, InputT,
|
|
|
|
|
|
OutputT, OutputTs...>;
|
|
|
|
|
|
using interior_node = typename dpf_type::interior_node;
|
|
|
|
|
|
using input_type = typename dpf_type::input_type;
|
|
|
|
|
|
using correction_words_array = typename dpf_type::correction_words_array;
|
|
|
|
|
|
using correction_advice_array = typename dpf_type::correction_advice_array;
|
|
|
|
|
|
|
|
|
|
|
|
constexpr auto depth = dpf_type::depth;
|
|
|
|
|
|
auto mask = dpf_type::msb_mask;
|
|
|
|
|
|
|
|
|
|
|
|
input_type x, x0{}, x1{};
|
|
|
|
|
|
if constexpr (dpf::is_wildcard_v<InputT>)
|
|
|
|
|
|
{
|
|
|
|
|
|
auto sampled = args.x();
|
|
|
|
|
|
x = std::get<0>(sampled);
|
|
|
|
|
|
x0 = std::get<1>(sampled).raw();
|
|
|
|
|
|
x1 = std::get<2>(sampled).raw();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
x = args.x;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
utils::flip_msb_if_signed_integral(x);
|
|
|
|
|
|
|
2026-09-24 23:18:10 -06:00
|
|
|
|
using tree = dpf::tree_traits<InteriorPRG>;
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
|
|
|
|
|
interior_node root[2];
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
|
|
|
|
|
tree::root_init(root, root_sampler);
|
2026-09-24 14:08:32 -06:00
|
|
|
|
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic push)
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
|
|
|
|
|
|
correction_words_array correction_words;
|
|
|
|
|
|
HEDLEY_PRAGMA(GCC diagnostic pop)
|
|
|
|
|
|
correction_advice_array correction_advice;
|
|
|
|
|
|
|
|
|
|
|
|
interior_node parent[2] = { root[0], root[1] };
|
|
|
|
|
|
|
|
|
|
|
|
for (std::size_t level = 0; level < depth; ++level, mask >>= 1)
|
|
|
|
|
|
{
|
2026-09-24 23:18:10 -06:00
|
|
|
|
const bool bit = !!(mask & x);
|
|
|
|
|
|
const bool is_last = tree::is_last_level(level, depth);
|
|
|
|
|
|
const bool ctrl0 = static_cast<bool>(dpf::get_lo_bit(parent[0]));
|
|
|
|
|
|
const bool ctrl1 = static_cast<bool>(dpf::get_lo_bit(parent[1]));
|
|
|
|
|
|
|
|
|
|
|
|
const auto child0 = tree::expand(parent[0], is_last);
|
|
|
|
|
|
const auto child1 = tree::expand(parent[1], is_last);
|
|
|
|
|
|
|
|
|
|
|
|
interior_node cw{};
|
|
|
|
|
|
psnip_uint8_t advice = 0;
|
|
|
|
|
|
tree::make_cw(cw, advice, child0, child1, parent[0], parent[1], bit,
|
|
|
|
|
|
is_last);
|
|
|
|
|
|
|
|
|
|
|
|
parent[0] = tree::advance(parent[0], child0, cw, advice, bit, ctrl0,
|
|
|
|
|
|
is_last);
|
|
|
|
|
|
parent[1] = tree::advance(parent[1], child1, cw, advice, bit, ctrl1,
|
|
|
|
|
|
is_last);
|
|
|
|
|
|
|
|
|
|
|
|
correction_words[level] = cw;
|
|
|
|
|
|
correction_advice[level] = advice;
|
2026-09-24 14:08:32 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool sign0 = dpf::get_lo_bit(parent[0]);
|
|
|
|
|
|
// bool sign1 = dpf::get_lo_bit(parent[1]);
|
|
|
|
|
|
|
|
|
|
|
|
auto [pair0, pair1] = std::apply([&x, &parent, &sign0](auto && ...ys)
|
|
|
|
|
|
{
|
|
|
|
|
|
return dpf::make_leaves<ExteriorPRG>(x,
|
|
|
|
|
|
dpf::unset_lo_2bits(parent[0]),
|
|
|
|
|
|
dpf::unset_lo_2bits(parent[1]),
|
|
|
|
|
|
sign0, std::size_t{0}, ys...); }, args.y);
|
|
|
|
|
|
auto && [leaves0, beavers0] = pair0;
|
|
|
|
|
|
auto && [leaves1, beavers1] = pair1;
|
|
|
|
|
|
|
|
|
|
|
|
return std::make_tuple(correction_words, correction_advice,
|
|
|
|
|
|
std::make_tuple(root[0], leaves0, beavers0, x0),
|
|
|
|
|
|
std::make_tuple(root[1], leaves1, beavers1, x1));
|
|
|
|
|
|
} // make_dpf_impl
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InteriorPRG = dpf::prg::aes128,
|
|
|
|
|
|
typename ExteriorPRG = InteriorPRG,
|
|
|
|
|
|
typename InputT,
|
|
|
|
|
|
typename OutputT = dpf::bit,
|
|
|
|
|
|
typename ...OutputTs>
|
|
|
|
|
|
HEDLEY_WARN_UNUSED_RESULT
|
|
|
|
|
|
auto make_dpf(dpfargs<InputT, OutputT, OutputTs...> args, root_sampler_t<InteriorPRG> && root_sampler = dpf::uniform_sample<typename InteriorPRG::block_type>)
|
|
|
|
|
|
{
|
|
|
|
|
|
static_assert(!is_secret_share_v<InputT>,
|
|
|
|
|
|
"make_dpf: domain point must be plaintext");
|
|
|
|
|
|
static_assert(!is_secret_share_v<OutputT>
|
|
|
|
|
|
&& (!is_secret_share_v<OutputTs> && ...),
|
|
|
|
|
|
"make_dpf: payloads must be plaintext");
|
|
|
|
|
|
using dpf_type = utils::dpf_type_t<InteriorPRG, ExteriorPRG, InputT,
|
|
|
|
|
|
OutputT, OutputTs...>;
|
|
|
|
|
|
|
|
|
|
|
|
auto [correction_words, correction_advice,
|
|
|
|
|
|
tuple0, tuple1] = detail::make_dpf_impl<InteriorPRG, ExteriorPRG>(args,
|
|
|
|
|
|
std::forward<root_sampler_t<InteriorPRG>>(root_sampler));
|
|
|
|
|
|
auto & [root0, leaves0, beavers0, offset0] = tuple0;
|
|
|
|
|
|
auto & [root1, leaves1, beavers1, offset1] = tuple1;
|
|
|
|
|
|
|
|
|
|
|
|
return dpf::make_party_key_pair(
|
|
|
|
|
|
dpf_type{root0, correction_words, correction_advice,
|
|
|
|
|
|
leaves0, beavers0, offset0},
|
|
|
|
|
|
dpf_type{root1, correction_words, correction_advice,
|
|
|
|
|
|
leaves1, beavers1, offset1});
|
|
|
|
|
|
} // make_dpf
|
|
|
|
|
|
|
|
|
|
|
|
// Convenience `make_dpf(x, y...)` lives in incremental.hpp so `at<>` and
|
|
|
|
|
|
// mixed-width packs share one entry point with the classic path.
|
|
|
|
|
|
|
|
|
|
|
|
namespace detail
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
template <typename DpfKey,
|
|
|
|
|
|
std::size_t ...Is>
|
|
|
|
|
|
auto make_dpf_random_point_impl(std::index_sequence<Is...>)
|
|
|
|
|
|
{
|
|
|
|
|
|
using input_type = typename DpfKey::input_type;
|
|
|
|
|
|
using interior_prg = typename DpfKey::interior_prg;
|
|
|
|
|
|
using exterior_prg = typename DpfKey::exterior_prg;
|
|
|
|
|
|
|
|
|
|
|
|
input_type x = dpf::uniform_sample<input_type>();
|
|
|
|
|
|
input_type x0 = dpf::uniform_sample<input_type>();
|
|
|
|
|
|
input_type x1 = static_cast<input_type>(x - x0);
|
|
|
|
|
|
|
|
|
|
|
|
auto keys = make_dpf<interior_prg, exterior_prg>(
|
|
|
|
|
|
x, typename DpfKey::concrete_output_type<Is>(1)...);
|
|
|
|
|
|
return std::make_tuple(std::move(keys.first), std::move(keys.second),
|
|
|
|
|
|
x0, x1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
|
|
|
|
|
|
|
template <typename DpfKey>
|
|
|
|
|
|
auto make_dpf_random_point()
|
|
|
|
|
|
{
|
|
|
|
|
|
return detail::make_dpf_random_point_impl<DpfKey>(
|
|
|
|
|
|
std::make_index_sequence<
|
|
|
|
|
|
std::tuple_size_v<typename DpfKey::outputs_tuple>>{});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InteriorPRG = dpf::prg::aes128,
|
|
|
|
|
|
typename ExteriorPRG = InteriorPRG,
|
|
|
|
|
|
typename InputT,
|
|
|
|
|
|
typename OutputT = dpf::bit,
|
|
|
|
|
|
typename ...OutputTs>
|
|
|
|
|
|
auto deduce_dpf_type(InputT x, OutputT y = dpf::bit::one, OutputTs ...ys)
|
|
|
|
|
|
{
|
|
|
|
|
|
return utils::dpf_type<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>{};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template <typename InteriorPRG = dpf::prg::aes128,
|
|
|
|
|
|
typename ExteriorPRG = InteriorPRG,
|
|
|
|
|
|
typename InputT,
|
|
|
|
|
|
typename OutputT = dpf::bit,
|
|
|
|
|
|
typename ...OutputTs>
|
|
|
|
|
|
auto deduce_dpf_type(dpf::dpfargs<InputT, OutputT, OutputTs...> args)
|
|
|
|
|
|
{
|
|
|
|
|
|
return utils::dpf_type<InteriorPRG, ExteriorPRG, InputT, OutputT, OutputTs...>{};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define DEDUCE_DPF_TYPE_T(...) typename decltype(dpf::deduce_dpf_type(__VA_ARGS__))::type;
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace dpf
|
|
|
|
|
|
|
|
|
|
|
|
#endif // LIBDPF_INCLUDE_DPF_DPF_KEY_HPP__
|