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

@ -25,7 +25,7 @@
namespace grotto
{
/// Appendix D gadgets whose polynomial degree is 0 and whose max error is 0.
/// @brief Appendix D gadgets whose polynomial degree is 0 and whose max error is 0.
enum class exact_constant
{
signum,
@ -36,7 +36,7 @@ enum class exact_constant
zero,
nonzero,
ilogb,
/// `ceil(log2(|x|))`. Exact powers of two agree with `ilogb`; every other
/// @brief `ceil(log2(|x|))`. Exact powers of two agree with `ilogb`; every other
/// positive magnitude is one larger. Zero uses the same `-64` sentinel.
ceil_ilogb,
ilog10,
@ -51,20 +51,21 @@ struct constant_lut
using raw_type = Raw;
/// Signed piece starts. `bounds.front()` is `numeric_limits<Raw>::min()`,
/// @brief Signed piece starts. `bounds.front()` is `numeric_limits<Raw>::min()`,
/// and the starts are strictly increasing.
std::vector<Raw> bounds;
/// `values[i]` is the function on `[bounds[i], next)`, where `next` is
/// @brief `values[i]` is the function on `[bounds[i], next)`, where `next` is
/// `bounds[i + 1]` or one past `numeric_limits<Raw>::max()` for the last piece.
std::vector<std::int64_t> values;
HEDLEY_NO_THROW
std::size_t linear_parts() const noexcept { return values.size(); }
/// Pieces after joining the first and last when they carry the same value.
/// Those two meet across the signed wrap, which is how the paper counts
/// @brief Pieces after joining the first and last when they carry the same value.
/// @details Those two meet across the signed wrap, which is how the paper counts
/// parts for `zero` and `nonzero` (2, not 3).
/// @return Pieces after joining the first and last when they carry the same value
HEDLEY_NO_THROW
std::size_t wrapped_parts() const noexcept
{
@ -115,7 +116,7 @@ constexpr bool shift_fits(u128 value, unsigned shift) noexcept
return shift < 128 && value <= (~u128{0} >> shift);
}
/// 10^0 .. 10^19. Every ilog10 projection reads this one table.
/// @brief 10^0 .. 10^19. Every ilog10 projection reads this one table.
inline constexpr std::uint64_t pow10[] = {
1ull,
10ull,
@ -159,7 +160,10 @@ inline bool magnitude_ge_pow10(u128 mag, int k, unsigned fractional_bits) noexce
return mag * scale >= (u128{1} << fractional_bits);
}
/// Smallest positive magnitude whose base-10 log is at least `k`.
/// @brief Smallest positive magnitude whose base-10 log is at least `k`.
/// @param k the `k`
/// @param fractional_bits the number of fractional bits
/// @return Smallest positive magnitude whose base-10 log is at least `k`
HEDLEY_NO_THROW
inline u128 first_magnitude_at_least_pow10(int k, unsigned fractional_bits) noexcept
{
@ -315,8 +319,8 @@ void push_pow2_cuts(std::vector<Raw> & cuts)
detail::push_both_signs<Raw>(detail::u128{1} << k, cuts);
}
/// floor(log2(|raw|)) - F, with -64 on the |x| <= 2^{-64} class (including 0).
/// One exponent program; fractional precision only shifts the stored exponent.
/// @brief floor(log2(|raw|)) - F, with -64 on the |x| <= 2^{-64} class (including 0).
/// @details One exponent program; fractional precision only shifts the stored exponent.
template <>
struct exact_lut<exact_constant::ilogb>
{
@ -347,7 +351,7 @@ struct exact_lut<exact_constant::ilogb>
}
};
/// ceil(log2(|x|)). Same powers-of-two cuts as `ilogb`; exact powers keep the
/// @brief ceil(log2(|x|)). Same powers-of-two cuts as `ilogb`; exact powers keep the
/// floor exponent and every other magnitude steps up by one.
template <>
struct exact_lut<exact_constant::ceil_ilogb>
@ -387,7 +391,7 @@ struct exact_lut<exact_constant::ceil_ilogb>
}
};
/// floor(log10(|x|)), with -19 on |x| <= 10^{-19}. Thresholds come from `pow10`.
/// @brief floor(log10(|x|)), with -19 on |x| <= 10^{-19}. Thresholds come from `pow10`.
template <>
struct exact_lut<exact_constant::ilog10>
{
@ -424,8 +428,8 @@ struct exact_lut<exact_constant::ilog10>
}
};
/// 64-bit leading-zero count of trunc(x). Negatives are 0; a zero integer part is 64.
/// Exponent k of the integer part maps to 63-k after a shift of `fractional_bits`.
/// @brief 64-bit leading-zero count of trunc(x). Negatives are 0; a zero integer part is 64.
/// @details Exponent k of the integer part maps to 63-k after a shift of `fractional_bits`.
template <>
struct exact_lut<exact_constant::clz>
{
@ -461,8 +465,8 @@ struct exact_lut<exact_constant::clz>
}
};
/// 64-bit redundant sign bits of trunc(x) toward zero.
/// Positive q uses 62-floor(log2(q)); negative q uses 62-floor(log2(q-1)).
/// @brief 64-bit redundant sign bits of trunc(x) toward zero.
/// @details Positive q uses 62-floor(log2(q)); negative q uses 62-floor(log2(q-1)).
template <>
struct exact_lut<exact_constant::clrsb>
{
@ -564,7 +568,7 @@ constant_lut<Raw> make_exact_constant_lut(exact_constant which, unsigned fractio
throw std::invalid_argument("unknown exact constant");
}
/// Comparison against a public threshold. Two pieces; the cut sits on `bound`
/// @brief Comparison against a public threshold. Two pieces; the cut sits on `bound`
/// (`lt` / `geq`) or just after it (`leq` / `gt`).
enum class threshold_cmp
{
@ -600,7 +604,12 @@ constant_lut<Raw> make_threshold_lut(Raw bound, threshold_cmp kind)
[=](Raw raw) { return evaluate_threshold(raw, bound, kind); });
}
/// `1` on the inclusive clip window `[low, high]`, `0` outside it.
/// @brief `1` on the inclusive clip window `[low, high]`, `0` outside it.
/// @tparam Raw underlying representation
/// @param low the lower endpoint
/// @param high the upper endpoint
/// @return `1` on the inclusive clip window `[low, high]`, `0` outside it
/// @throws std::invalid_argument if `low > high`
template <typename Raw>
constant_lut<Raw> make_interval_lut(Raw low, Raw high)
{
@ -614,13 +623,20 @@ constant_lut<Raw> make_interval_lut(Raw low, Raw high)
[=](Raw raw) { return raw >= low && raw <= high ? std::int64_t{1} : std::int64_t{0}; });
}
/// `floor(min(max(raw, low), high) / modulus)`, division toward -infinity.
/// @brief `floor(min(max(raw, low), high) / modulus)`, division toward -infinity.
///
/// `modulus`, `low`, and `high` are in the same raw units as the domain, so
/// one program covers every fractional precision: a mathematical step `M`
/// with `F` fractional bits is the raw modulus `M << F`. The paper's
/// `quot(M, T1, T2)` is this function. Piece count is about `(high-low)/modulus`;
/// the build rejects windows that would need more than 2^16 pieces.
/// @tparam Raw underlying representation
/// @param raw the underlying integer
/// @param modulus the public modulus
/// @param low the lower endpoint
/// @param high the upper endpoint
/// @return `floor(min(max(raw, low), high) / modulus)`, division toward -infinity
/// @throws std::invalid_argument if `modulus must be positive`
template <typename Raw>
std::int64_t evaluate_clipped_quotient(Raw raw, Raw modulus, Raw low, Raw high)
{