Record Grotto half-ulp tables and comparison geneval, and factor shared beaver terms before the quotient.

Horner and window evaluation need those tables in the tree. Comparison geneval opens the same value words as a Doerner–Shelat key. A factor common to every polynomial term is multiplied first so that preprocessing stays smaller.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ryan Henry 2026-09-24 15:16:21 -06:00
parent 3f10e05176
commit 875f09fec1
14 changed files with 42668 additions and 184 deletions

View file

@ -14,6 +14,13 @@
/// a public query. It samples a random target, runs geneval there,
/// and shifts the query by `target - x`, which is what
/// `offset_x` does after a wildcard key is bound to `x`.
///
/// `geneval_cmp` is the comparison-channel form. The value-correction
/// word is a function of the secret path at every level, so the walk
/// stays live for the whole depth and the opened words match a
/// Doerner–Shelat comparison key. Prefix shares are
/// `eval_point(cmp, ...)` at each endpoint. Piecewise-cubic evaluation
/// on top of that is `grotto::geneval_offset_horner`.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
/// see [LICENSE.md](@ref license) for details.
@ -37,6 +44,7 @@
#include "dpf/aligned_allocator.hpp"
#include "dpf/doerner_shelat.hpp"
#include "dpf/eval_target.hpp"
#include "dpf/leaf_node.hpp"
namespace dpf
@ -633,6 +641,83 @@ auto geneval_sequence(wildcard_input_t, InputT x0, InputT x1,
begin, end, std::move(rng), [] { return dpf::uniform_sample<InputT>(); }, y);
}
/// Opened comparison key material and one prefix share per endpoint.
/// `live_levels` is the full depth: a comparison value word depends on the
/// secret path at every level, so there is no early dummy-word tail.
struct geneval_cmp_result
{
std::vector<uint64_t> party0;
std::vector<uint64_t> party1;
std::vector<simde__m128i, aligned_allocator<simde__m128i>> correction_words;
std::vector<uint8_t> correction_advice;
std::vector<uint64_t> value_cw;
uint64_t cw_last = 0;
uint64_t addend0 = 0;
uint64_t addend1 = 0;
uint64_t mask = 0;
std::size_t live_levels = 0;
};
/// Doerner–Shelat comparison geneval. `x0 XOR x1` is the secret point, in the
/// same share convention as `geneval_point`. `spec` is an `lt` / `leq` / `gt`
/// / `geq` pack. Each endpoint is returned in order as the two parties'
/// `eval_point(cmp, ...)` shares. An empty range opens nothing.
template <typename InputT,
typename ForwardIterator,
typename RootSampler,
typename PadRng,
typename Spec>
HEDLEY_WARN_UNUSED_RESULT
geneval_cmp_result geneval_cmp(InputT x0, InputT x1,
ForwardIterator begin, ForwardIterator end,
ds_randomness<RootSampler, PadRng> rng, Spec spec)
{
geneval_cmp_result out;
if (begin == end)
return out;
auto keys = make_dpf_doerner_shelat(std::move(x0), std::move(x1),
std::move(rng), std::move(spec));
const auto & k0 = keys.first;
const auto & k1 = keys.second;
using key_type = std::decay_t<decltype(k0)>;
constexpr std::size_t depth = key_type::depth;
out.live_levels = depth;
out.mask = k0.cmp().mask;
out.cw_last = k0.cw_last();
out.addend0 = k0.cmp_addend().raw();
out.addend1 = k1.cmp_addend().raw();
out.correction_words.resize(depth);
out.correction_advice.resize(depth);
out.value_cw.resize(depth);
for (std::size_t level = 0; level < depth; ++level)
{
out.correction_words[level] = k0.correction_word(level);
out.correction_advice[level] = static_cast<uint8_t>(k0.correction_advice(level));
out.value_cw[level] = k0.value_cw(level);
}
for (auto it = begin; it != end; ++it)
{
out.party0.push_back(eval_point(dpf::cmp, k0, *it).raw());
out.party1.push_back(eval_point(dpf::cmp, k1, *it).raw());
}
return out;
}
/// `gt(beta)` comparison geneval. `if_false` is 0.
template <typename InputT,
typename ForwardIterator,
typename RootSampler,
typename PadRng>
HEDLEY_WARN_UNUSED_RESULT
geneval_cmp_result geneval_cmp(InputT x0, InputT x1,
ForwardIterator begin, ForwardIterator end,
ds_randomness<RootSampler, PadRng> rng, uint64_t beta)
{
return geneval_cmp(std::move(x0), std::move(x1), begin, end,
std::move(rng), dpf::gt(beta));
}
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_GENEVAL_HPP__