Initial import of libdpf.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
commit
e4e666f459
4563 changed files with 1690372 additions and 0 deletions
352
include/dpf/dcf.hpp
Normal file
352
include/dpf/dcf.hpp
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
/// @file dpf/dcf.hpp
|
||||
/// @brief Comparison-channel specs and GGM path-sum helpers for libdpf.
|
||||
/// @details `lt`/`leq`/`gt`/`geq` (+ `_at`) take `(if_true, if_false=0)`.
|
||||
/// Eval walks the same GGM tree as the DPF (per-level value CWs).
|
||||
/// `eq` / `eq_at` are synonyms for ordinary point placements.
|
||||
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
|
||||
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
|
||||
|
||||
#ifndef LIBDPF_INCLUDE_DPF_DCF_HPP__
|
||||
#define LIBDPF_INCLUDE_DPF_DCF_HPP__
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <limits>
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
#include "simde/simde/x86/avx2.h"
|
||||
|
||||
#include "dpf/utils.hpp"
|
||||
#include "dpf/bit.hpp"
|
||||
#include "dpf/xor_wrapper.hpp"
|
||||
#include "dpf/twiddle.hpp"
|
||||
|
||||
namespace dpf
|
||||
{
|
||||
|
||||
/// Comparison kind for the optional DCF channel on a key.
|
||||
enum class cmp_kind : uint8_t
|
||||
{
|
||||
lt = 0,
|
||||
leq = 1,
|
||||
gt = 2,
|
||||
geq = 3
|
||||
};
|
||||
|
||||
enum class cmp_trivial : uint8_t
|
||||
{
|
||||
none = 0,
|
||||
always_true = 1,
|
||||
always_false = 2
|
||||
};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
namespace dcf_impl
|
||||
{
|
||||
|
||||
template <typename Beta>
|
||||
Beta default_false() noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
||||
return dpf::bit::zero;
|
||||
else
|
||||
return Beta{};
|
||||
}
|
||||
|
||||
template <typename Beta>
|
||||
uint64_t beta_delta_u64(const Beta & if_true, const Beta & if_false,
|
||||
uint64_t mask) noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
||||
{
|
||||
const uint64_t t = static_cast<bool>(if_true) ? 1ULL : 0ULL;
|
||||
const uint64_t f = static_cast<bool>(if_false) ? 1ULL : 0ULL;
|
||||
return (t ^ f) & mask;
|
||||
}
|
||||
else if constexpr (dpf::utils::is_xor_wrapper_v<Beta>)
|
||||
{
|
||||
return (static_cast<uint64_t>(if_true) ^ static_cast<uint64_t>(if_false))
|
||||
& mask;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (static_cast<uint64_t>(if_true)
|
||||
- static_cast<uint64_t>(if_false)) & mask;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Beta>
|
||||
uint64_t beta_to_u64_simple(const Beta & beta, uint64_t mask) noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
||||
return (static_cast<bool>(beta) ? 1ULL : 0ULL) & mask;
|
||||
else
|
||||
return static_cast<uint64_t>(beta) & mask;
|
||||
}
|
||||
|
||||
template <typename Beta>
|
||||
Beta sub_beta(const Beta & a, const Beta & b) noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
||||
return dpf::bit{static_cast<bool>(a) ^ static_cast<bool>(b)};
|
||||
else if constexpr (dpf::utils::is_xor_wrapper_v<Beta>)
|
||||
return Beta{static_cast<uint64_t>(a) ^ static_cast<uint64_t>(b)};
|
||||
else
|
||||
return static_cast<Beta>(a - b);
|
||||
}
|
||||
|
||||
template <typename Beta>
|
||||
Beta u64_to_beta(uint64_t v) noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<Beta, dpf::bit>)
|
||||
return dpf::bit{static_cast<bool>(v & 1u)};
|
||||
else
|
||||
return static_cast<Beta>(v);
|
||||
}
|
||||
|
||||
inline uint64_t default_mask_for_bits(std::size_t out_bits) noexcept
|
||||
{
|
||||
if (out_bits >= 64)
|
||||
return ~0ULL;
|
||||
if (out_bits == 0)
|
||||
return 0ULL;
|
||||
return (1ULL << out_bits) - 1ULL;
|
||||
}
|
||||
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
uint64_t neg_m(uint64_t x, uint64_t mask) noexcept
|
||||
{
|
||||
return (0ULL - x) & mask;
|
||||
}
|
||||
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
uint64_t sgn_m(uint8_t t1, uint64_t x, uint64_t mask) noexcept
|
||||
{
|
||||
return t1 ? neg_m(x, mask) : x;
|
||||
}
|
||||
|
||||
/// Convert a GGM node to a group element (low 64 bits, control bits cleared).
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
uint64_t convert_node(simde__m128i n, uint64_t mask) noexcept
|
||||
{
|
||||
return static_cast<uint64_t>(
|
||||
simde_mm_cvtsi128_si64(dpf::unset_lo_2bits(n))) & mask;
|
||||
}
|
||||
|
||||
/// Draw the group-width blind `r` used to split the `cmp_addend` share.
|
||||
/// `sample` yields one interior block; only `popcount(mask)` live bits are
|
||||
/// kept, so the blind (and thus the addend share) never needs a full padded
|
||||
/// `uint64_t` on the wire. Dealer and Doerner–Shelat gen call this with the
|
||||
/// same block source so their keys stay byte-identical (matched tapes).
|
||||
template <typename BlockSampler>
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
uint64_t sample_addend_blind(uint64_t mask, BlockSampler && sample) noexcept
|
||||
{
|
||||
return convert_node(dpf::unset_lo_2bits(sample()), mask);
|
||||
}
|
||||
|
||||
/// One level of value CW on GGM children. Updates running `Va`.
|
||||
/// `ai` is the keep-path bit of the (effective) threshold.
|
||||
inline uint64_t make_value_cw(simde__m128i c0L, simde__m128i c0R,
|
||||
simde__m128i c1L, simde__m128i c1R, uint8_t t0, uint8_t t1, int ai,
|
||||
uint64_t & Va, uint64_t beta, uint64_t mask) noexcept
|
||||
{
|
||||
(void)t0;
|
||||
uint64_t v0K, v1K, v0Lo, v1Lo;
|
||||
if (ai == 0)
|
||||
{
|
||||
v0K = convert_node(c0L, mask);
|
||||
v1K = convert_node(c1L, mask);
|
||||
v0Lo = convert_node(c0R, mask);
|
||||
v1Lo = convert_node(c1R, mask);
|
||||
}
|
||||
else
|
||||
{
|
||||
v0K = convert_node(c0R, mask);
|
||||
v1K = convert_node(c1R, mask);
|
||||
v0Lo = convert_node(c0L, mask);
|
||||
v1Lo = convert_node(c1L, mask);
|
||||
}
|
||||
uint64_t vcw = sgn_m(t1,
|
||||
(v1Lo + neg_m(v0Lo, mask) + neg_m(Va, mask)) & mask, mask);
|
||||
// Lose-left (ai==1) is the x<α diverge: plant β there.
|
||||
if (ai == 1)
|
||||
vcw = (vcw + sgn_m(t1, beta, mask)) & mask;
|
||||
Va = (Va + neg_m(v1K, mask) + v0K + sgn_m(t1, vcw, mask)) & mask;
|
||||
return vcw;
|
||||
}
|
||||
|
||||
/// Final leaf value CW. `on_path` is the payload reconstructed when the query
|
||||
/// stays on α's path through all levels (0 for strict lt/geq; β for leq/gt).
|
||||
inline uint64_t make_final_cw(simde__m128i s0, simde__m128i s1, uint8_t t1,
|
||||
uint64_t Va, uint64_t mask, uint64_t on_path = 0) noexcept
|
||||
{
|
||||
uint64_t c0 = convert_node(s0, mask);
|
||||
uint64_t c1 = convert_node(s1, mask);
|
||||
return sgn_m(t1,
|
||||
(c1 + neg_m(c0, mask) + neg_m(Va, mask) + on_path) & mask, mask);
|
||||
}
|
||||
|
||||
} // namespace dcf_impl
|
||||
|
||||
/// Comparison metadata on an incremental key (value CWs live on the key).
|
||||
/// Payload δ = if_true − if_false is dealer-known and baked into `value_cw` /
|
||||
/// `cw_last` only — never stored clear on the key (traditional DPF hiding).
|
||||
/// The second output value (`if_false`) is held as a per-party additive share
|
||||
/// on the key (`cmp_addend`), not as a public constant.
|
||||
struct cmp_meta
|
||||
{
|
||||
int nbits = 0; // comparison prefix length
|
||||
uint64_t mask = 0;
|
||||
cmp_kind kind = cmp_kind::lt;
|
||||
cmp_trivial trivial = cmp_trivial::none;
|
||||
bool eval_as_ge = false; // invert path-sum (geq / gt)
|
||||
bool include_eq = false; // plant δ on the α-path leaf (leq / gt)
|
||||
bool active = false;
|
||||
|
||||
bool empty() const noexcept { return !active; }
|
||||
};
|
||||
|
||||
/// Backward-compatible alias while call sites migrate.
|
||||
using cmp_channel = cmp_meta;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Comparison specs: lt/leq/gt/geq (+ _at)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template <cmp_kind Kind, typename Beta>
|
||||
struct cmp_pack
|
||||
{
|
||||
static constexpr bool is_cmp = true;
|
||||
static constexpr cmp_kind kind = Kind;
|
||||
static constexpr std::size_t prefix = 0;
|
||||
using beta_type = Beta;
|
||||
Beta if_true;
|
||||
Beta if_false;
|
||||
|
||||
explicit cmp_pack(Beta t, Beta f = detail::dcf_impl::default_false<Beta>())
|
||||
: if_true{std::move(t)}, if_false{std::move(f)} { }
|
||||
};
|
||||
|
||||
template <std::size_t N, cmp_kind Kind, typename Beta>
|
||||
struct cmp_at_pack
|
||||
{
|
||||
static constexpr bool is_cmp = true;
|
||||
static constexpr cmp_kind kind = Kind;
|
||||
static constexpr std::size_t prefix = N;
|
||||
using beta_type = Beta;
|
||||
Beta if_true;
|
||||
Beta if_false;
|
||||
|
||||
explicit cmp_at_pack(Beta t, Beta f = detail::dcf_impl::default_false<Beta>())
|
||||
: if_true{std::move(t)}, if_false{std::move(f)} { }
|
||||
};
|
||||
|
||||
template <typename Beta>
|
||||
inline auto lt(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>())
|
||||
{
|
||||
return cmp_pack<cmp_kind::lt, std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||
}
|
||||
template <typename Beta>
|
||||
inline auto leq(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>())
|
||||
{
|
||||
return cmp_pack<cmp_kind::leq, std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||
}
|
||||
template <typename Beta>
|
||||
inline auto gt(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>())
|
||||
{
|
||||
return cmp_pack<cmp_kind::gt, std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||
}
|
||||
template <typename Beta>
|
||||
inline auto geq(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>())
|
||||
{
|
||||
return cmp_pack<cmp_kind::geq, std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||
}
|
||||
|
||||
template <std::size_t N, typename Beta>
|
||||
inline auto lt_at(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>())
|
||||
{
|
||||
return cmp_at_pack<N, cmp_kind::lt, std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||
}
|
||||
template <std::size_t N, typename Beta>
|
||||
inline auto leq_at(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>())
|
||||
{
|
||||
return cmp_at_pack<N, cmp_kind::leq, std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||
}
|
||||
template <std::size_t N, typename Beta>
|
||||
inline auto gt_at(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>())
|
||||
{
|
||||
return cmp_at_pack<N, cmp_kind::gt, std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||
}
|
||||
template <std::size_t N, typename Beta>
|
||||
inline auto geq_at(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>())
|
||||
{
|
||||
return cmp_at_pack<N, cmp_kind::geq, std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Equality specs: eq / eq_at
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
template <typename Beta>
|
||||
struct eq_pack
|
||||
{
|
||||
static constexpr bool is_eq = true;
|
||||
static constexpr std::size_t prefix = 0;
|
||||
using beta_type = Beta;
|
||||
Beta if_true;
|
||||
Beta if_false;
|
||||
|
||||
explicit eq_pack(Beta t, Beta f = detail::dcf_impl::default_false<Beta>())
|
||||
: if_true{std::move(t)}, if_false{std::move(f)} { }
|
||||
};
|
||||
|
||||
template <std::size_t N, typename Beta>
|
||||
struct eq_at_pack
|
||||
{
|
||||
static constexpr bool is_eq = true;
|
||||
static constexpr std::size_t prefix = N;
|
||||
using beta_type = Beta;
|
||||
Beta if_true;
|
||||
Beta if_false;
|
||||
|
||||
explicit eq_at_pack(Beta t, Beta f = detail::dcf_impl::default_false<Beta>())
|
||||
: if_true{std::move(t)}, if_false{std::move(f)} { }
|
||||
};
|
||||
|
||||
template <typename Beta>
|
||||
inline auto eq(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>())
|
||||
{
|
||||
return eq_pack<std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||
}
|
||||
template <std::size_t N, typename Beta>
|
||||
inline auto eq_at(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>())
|
||||
{
|
||||
return eq_at_pack<N, std::decay_t<Beta>>(std::move(t), std::move(f));
|
||||
}
|
||||
|
||||
template <typename T> struct is_cmp_spec : std::false_type {};
|
||||
template <cmp_kind K, typename B> struct is_cmp_spec<cmp_pack<K, B>> : std::true_type {};
|
||||
template <std::size_t N, cmp_kind K, typename B>
|
||||
struct is_cmp_spec<cmp_at_pack<N, K, B>> : std::true_type {};
|
||||
template <typename T>
|
||||
inline constexpr bool is_cmp_spec_v = is_cmp_spec<T>::value;
|
||||
|
||||
template <typename T> struct is_eq_spec : std::false_type {};
|
||||
template <typename B> struct is_eq_spec<eq_pack<B>> : std::true_type {};
|
||||
template <std::size_t N, typename B>
|
||||
struct is_eq_spec<eq_at_pack<N, B>> : std::true_type {};
|
||||
template <typename T>
|
||||
inline constexpr bool is_eq_spec_v = is_eq_spec<T>::value;
|
||||
|
||||
template <typename T>
|
||||
inline constexpr bool is_dcf_spec_v = is_cmp_spec_v<T>;
|
||||
|
||||
} // namespace dpf
|
||||
|
||||
#endif // LIBDPF_INCLUDE_DPF_DCF_HPP__
|
||||
Loading…
Add table
Add a link
Reference in a new issue