Document the new DPF surfaces in one command set, and test the field, half-tree, and multipoint edges.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Ryan Henry 2026-09-24 23:18:10 -06:00
parent 0d8a5a8131
commit 0dff6df8ed
250 changed files with 12199 additions and 1981 deletions

View file

@ -1,7 +1,6 @@
/// @file grotto/prefix_parity.hpp
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @brief
/// @details
/// @brief Prefix-parity and signed-prefix shares from a comparison key.
/// @copyright Copyright (c) 2019-2023 Ryan Henry and others
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
/// see [LICENSE.md](@ref GPLv2) for details.
@ -77,8 +76,11 @@ static auto prefix_parities(const DpfKey & dpf, const std::array<InputT, NumPart
// above a partial leaf. `parity_of_substring_prefix` indexes
// bits inside the exterior node, which is wider than
// `lg_outputs_per_leaf` when several outputs share that node.
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
constexpr std::size_t leaf_bit_lg
= dpf::lg_outputs_per_leaf_v<dpf::bit, exterior_node>;
HEDLEY_PRAGMA(GCC diagnostic pop)
constexpr std::size_t align = std::max(
DpfKey::lg_outputs_per_leaf, leaf_bit_lg);
const std::size_t tz = dpf::utils::countr_zero<input_type>{}(current_endpoint);
@ -103,7 +105,7 @@ static auto prefix_parities(const DpfKey & dpf, const std::array<InputT, NumPart
{
bool bit = !!(mask & current_endpoint);
direction[level_index+1] = bit;
path[level_index+1] = DpfKey::traverse_interior(path[level_index], dpf.correction_word(level_index, direction[level_index+1]), direction[level_index+1]);
path[level_index+1] = DpfKey::traverse_interior(path[level_index], dpf.correction_word(level_index, direction[level_index+1]), direction[level_index+1], DpfKey::tree::is_last_level(level_index, DpfKey::depth));
parity[level_index+1] = parity[level_index] ^ ((direction[level_index] ^ direction[level_index+1]) & dpf::get_lo_bit(path[level_index]));
}
}
@ -224,7 +226,7 @@ uint64_t cmp_addend_raw(const KeyT & key) noexcept
} // namespace detail
/// Additive prefix indicators from the key's DCF value sums.
/// @brief Additive prefix indicators from the key's DCF value sums.
///
/// Same multi-knot walk as `prefix_parities` (shared path, MSB-first), but each
/// level contributes the Boyle/Guo value correction instead of an advice-bit
@ -237,6 +239,14 @@ uint64_t cmp_addend_raw(const KeyT & key) noexcept
/// Endpoints are visited in the order given. The running sum is reused from the
/// longest common prefix. The zero-suffix early stop of the XOR walk is not
/// applied: later levels still add a Convert word.
/// @tparam InputT input domain type
/// @tparam DpfKey DPF key type
/// @tparam NumParts num parts
/// @tparam input_type input type
/// @param dpf the DPF key
/// @param endpoints the `endpoints`
/// @return Additive prefix indicators from the key's DCF value sums
/// @throws std::invalid_argument if `key has no comparison channel`
template <typename InputT,
typename DpfKey,
std::size_t NumParts,
@ -306,7 +316,8 @@ static auto signed_prefix_parities(const DpfKey & dpf,
const auto bit_mask = key_type::msb_mask >> shift;
const bool bit = !!(bit_mask & tx);
path[level] = DpfKey::traverse_interior(path[level - 1],
dpf.correction_word(level - 1, bit), bit);
dpf.correction_word(level - 1, bit), bit,
DpfKey::tree::is_last_level(level - 1, DpfKey::depth));
}
dpf::detail::path_note_filled_to(path, nbits);
@ -341,8 +352,15 @@ static auto signed_prefix_parities(const DpfKey & dpf,
}
}
/// Runtime-length form of `signed_prefix_parities`. `out[i]` receives the same
/// @brief Runtime-length form of `signed_prefix_parities`. `out[i]` receives the same
/// share a one-element call would return for `endpoints[i]`.
/// @tparam InputT input domain type
/// @tparam DpfKey DPF key type
/// @param dpf the DPF key
/// @param endpoints the `endpoints`
/// @param n the `n`
/// @param out the output buffer
/// @throws std::invalid_argument if `key has no comparison channel`
template <typename InputT,
typename DpfKey>
static void signed_prefix_parities_into(const DpfKey & dpf,
@ -414,7 +432,8 @@ static void signed_prefix_parities_into(const DpfKey & dpf,
const auto bit_mask = key_type::msb_mask >> shift;
const bool bit = !!(bit_mask & tx);
path[level] = DpfKey::traverse_interior(path[level - 1],
dpf.correction_word(level - 1, bit), bit);
dpf.correction_word(level - 1, bit), bit,
DpfKey::tree::is_last_level(level - 1, DpfKey::depth));
}
dpf::detail::path_note_filled_to(path, nbits);
@ -448,12 +467,20 @@ static void signed_prefix_parities_into(const DpfKey & dpf,
}
}
/// One-hot segment shares for a unit `gt` comparison (`if_true = 1`, `if_false = 0`).
/// @brief One-hot segment shares for a unit `gt` comparison (`if_true = 1`, `if_false = 0`).
///
/// `endpoints` is sorted ascending. Piece `i < n-1` is `[endpoints[i], endpoints[i+1])`
/// and the last piece wraps. Party 0 + party 1 is `1` on the piece that contains
/// the key's target and `0` on the others, so a public dot product with LUT
/// constants is an additive share of the function value (not of its negation).
/// @tparam InputT input domain type
/// @tparam DpfKey DPF key type
/// @tparam NumParts num parts
/// @tparam input_type input type
/// @param dpf the DPF key
/// @param endpoints the `endpoints`
/// @return One-hot segment shares for a unit `gt` comparison (`if_true = 1`, `if_false = 0`)
/// @throws std::invalid_argument if `key has no comparison channel`
template <typename InputT,
typename DpfKey,
std::size_t NumParts,