Annotate noexcept and constexpr with HEDLEY, and add interval containment, ChaCha, and the dyadic range tables.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
875f09fec1
commit
0d8a5a8131
97 changed files with 9212 additions and 1159 deletions
|
|
@ -10,6 +10,8 @@
|
|||
#ifndef LIBDPF_INCLUDE_GROTTO_CONSTANT_LUT_HPP__
|
||||
#define LIBDPF_INCLUDE_GROTTO_CONSTANT_LUT_HPP__
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
|
@ -57,11 +59,13 @@ struct constant_lut
|
|||
/// `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
|
||||
/// parts for `zero` and `nonzero` (2, not 3).
|
||||
HEDLEY_NO_THROW
|
||||
std::size_t wrapped_parts() const noexcept
|
||||
{
|
||||
if (values.size() >= 2 && values.front() == values.back())
|
||||
|
|
@ -69,6 +73,7 @@ struct constant_lut
|
|||
return values.size();
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
std::int64_t operator()(Raw x) const noexcept
|
||||
{
|
||||
const auto it = std::upper_bound(bounds.begin(), bounds.end(), x);
|
||||
|
|
@ -86,6 +91,7 @@ namespace detail
|
|||
using u128 = unsigned __int128;
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_NO_THROW
|
||||
constexpr u128 magnitude(Raw raw) noexcept
|
||||
{
|
||||
if (raw >= 0)
|
||||
|
|
@ -95,6 +101,7 @@ constexpr u128 magnitude(Raw raw) noexcept
|
|||
return static_cast<u128>(-static_cast<__int128>(raw));
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr int floor_log2(u128 mag) noexcept
|
||||
{
|
||||
if (mag <= std::uint64_t(-1))
|
||||
|
|
@ -102,6 +109,7 @@ constexpr int floor_log2(u128 mag) noexcept
|
|||
return 127 - __builtin_clzll(static_cast<std::uint64_t>(mag >> 64));
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr bool shift_fits(u128 value, unsigned shift) noexcept
|
||||
{
|
||||
return shift < 128 && value <= (~u128{0} >> shift);
|
||||
|
|
@ -131,6 +139,7 @@ inline constexpr std::uint64_t pow10[] = {
|
|||
10000000000000000000ull,
|
||||
};
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
inline bool magnitude_ge_pow10(u128 mag, int k, unsigned fractional_bits) noexcept
|
||||
{
|
||||
if (mag == 0 || fractional_bits >= 128)
|
||||
|
|
@ -151,6 +160,7 @@ inline bool magnitude_ge_pow10(u128 mag, int k, unsigned fractional_bits) noexce
|
|||
}
|
||||
|
||||
/// 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
|
||||
{
|
||||
if (fractional_bits >= 128)
|
||||
|
|
@ -253,6 +263,7 @@ struct sign_program
|
|||
static constexpr std::int64_t canonical[] = { Neg, Zero, Pos };
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_NO_THROW
|
||||
static std::int64_t eval(Raw raw, unsigned) noexcept
|
||||
{
|
||||
if (raw < 0)
|
||||
|
|
@ -310,6 +321,7 @@ template <>
|
|||
struct exact_lut<exact_constant::ilogb>
|
||||
{
|
||||
template <typename Raw>
|
||||
HEDLEY_NO_THROW
|
||||
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
||||
{
|
||||
const detail::u128 mag = detail::magnitude(raw);
|
||||
|
|
@ -341,6 +353,7 @@ template <>
|
|||
struct exact_lut<exact_constant::ceil_ilogb>
|
||||
{
|
||||
template <typename Raw>
|
||||
HEDLEY_NO_THROW
|
||||
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
||||
{
|
||||
const detail::u128 mag = detail::magnitude(raw);
|
||||
|
|
@ -379,6 +392,7 @@ template <>
|
|||
struct exact_lut<exact_constant::ilog10>
|
||||
{
|
||||
template <typename Raw>
|
||||
HEDLEY_NO_THROW
|
||||
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
||||
{
|
||||
const detail::u128 mag = detail::magnitude(raw);
|
||||
|
|
@ -416,6 +430,7 @@ template <>
|
|||
struct exact_lut<exact_constant::clz>
|
||||
{
|
||||
template <typename Raw>
|
||||
HEDLEY_NO_THROW
|
||||
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
||||
{
|
||||
if (raw < 0)
|
||||
|
|
@ -452,6 +467,7 @@ template <>
|
|||
struct exact_lut<exact_constant::clrsb>
|
||||
{
|
||||
template <typename Raw>
|
||||
HEDLEY_NO_THROW
|
||||
static std::int64_t eval(Raw raw, unsigned fractional_bits) noexcept
|
||||
{
|
||||
const detail::u128 mag = detail::magnitude(raw);
|
||||
|
|
@ -559,6 +575,7 @@ enum class threshold_cmp
|
|||
};
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_NO_THROW
|
||||
std::int64_t evaluate_threshold(Raw raw, Raw bound, threshold_cmp kind) noexcept
|
||||
{
|
||||
switch (kind)
|
||||
|
|
|
|||
433
include/grotto/dyadic_lut.hpp
Normal file
433
include/grotto/dyadic_lut.hpp
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
/// @file grotto/dyadic_lut.hpp
|
||||
/// @brief Exact dyadic step functions from the Grotto gadget list.
|
||||
/// @details Integer results and boolean 1s are raw fixed-point values:
|
||||
/// an integer n is stored as `n << fractional_bits`. `ilogb(0)` and
|
||||
/// `ilog10(0)` return `ilog_of_zero`.
|
||||
///
|
||||
/// `make_msb_lut(i)` is bit `i` counting from the most significant
|
||||
/// bit. It is constant on `2^{i+1}` intervals, so `i` must be less
|
||||
/// than `msb_bit_limit`.
|
||||
/// @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_GROTTO_DYADIC_LUT_HPP__
|
||||
#define LIBDPF_INCLUDE_GROTTO_DYADIC_LUT_HPP__
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
#include "grotto/easy_lut.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace grotto
|
||||
{
|
||||
|
||||
/// Sentinel raw value for `ilogb(0)` and `ilog10(0)`.
|
||||
inline constexpr std::int64_t ilog_of_zero =
|
||||
std::numeric_limits<std::int64_t>::min();
|
||||
|
||||
/// `make_msb_lut(i)` allows `i` in `[0, msb_bit_limit)`.
|
||||
/// Bit 0 is two intervals; bit 7 is 256.
|
||||
inline constexpr unsigned msb_bit_limit = 8;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
using u128 = unsigned __int128;
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_CONST
|
||||
HEDLEY_NO_THROW
|
||||
constexpr int raw_width() noexcept
|
||||
{
|
||||
return std::numeric_limits<Raw>::digits + 1;
|
||||
}
|
||||
|
||||
inline std::int64_t encode_units(std::int64_t units, unsigned fractional_bits)
|
||||
{
|
||||
if (fractional_bits >= 63)
|
||||
throw std::invalid_argument("dyadic lut: fractional width does not fit");
|
||||
const __int128 scaled = static_cast<__int128>(units) << fractional_bits;
|
||||
if (scaled > std::numeric_limits<std::int64_t>::max()
|
||||
|| scaled < std::numeric_limits<std::int64_t>::min())
|
||||
throw std::overflow_error("dyadic lut: encoded value does not fit int64");
|
||||
return static_cast<std::int64_t>(scaled);
|
||||
}
|
||||
|
||||
inline easy_poly unit_poly(std::int64_t units, unsigned fractional_bits)
|
||||
{
|
||||
return easy_poly{encode_units(units, fractional_bits), 0, 0, 1};
|
||||
}
|
||||
|
||||
inline easy_poly indicator_poly(bool on, unsigned fractional_bits)
|
||||
{
|
||||
return unit_poly(on ? 1 : 0, fractional_bits);
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_CONST
|
||||
HEDLEY_NO_THROW
|
||||
constexpr std::uint64_t raw_bits(std::int64_t raw) noexcept
|
||||
{
|
||||
constexpr int width = raw_width<Raw>();
|
||||
const auto masked = static_cast<std::uint64_t>(raw);
|
||||
if constexpr (width >= 64)
|
||||
return masked;
|
||||
else
|
||||
return masked & ((std::uint64_t{1} << width) - 1);
|
||||
}
|
||||
|
||||
inline int countl_zero_width(std::uint64_t bits, int width)
|
||||
{
|
||||
if (width <= 0 || width > 64)
|
||||
throw std::invalid_argument("dyadic lut: width must be 1..64");
|
||||
if (width < 64)
|
||||
bits &= (std::uint64_t{1} << width) - 1;
|
||||
if (bits == 0)
|
||||
return width;
|
||||
return __builtin_clzll(bits) - (64 - width);
|
||||
}
|
||||
|
||||
inline int countl_one_width(std::uint64_t bits, int width)
|
||||
{
|
||||
const std::uint64_t flipped = width >= 64 ? ~bits : (~bits & ((std::uint64_t{1} << width) - 1));
|
||||
return countl_zero_width(flipped, width);
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
int clz_of(std::int64_t raw)
|
||||
{
|
||||
return countl_zero_width(raw_bits<Raw>(raw), raw_width<Raw>());
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
int clrsb_of(std::int64_t raw)
|
||||
{
|
||||
constexpr int width = raw_width<Raw>();
|
||||
const std::uint64_t bits = raw_bits<Raw>(raw);
|
||||
const bool neg = ((bits >> (width - 1)) & 1u) != 0;
|
||||
const int matched = neg ? countl_one_width(bits, width)
|
||||
: countl_zero_width(bits, width);
|
||||
return matched - 1;
|
||||
}
|
||||
|
||||
HEDLEY_CONST
|
||||
HEDLEY_NO_THROW
|
||||
constexpr int floor_log2_u128(u128 mag) noexcept
|
||||
{
|
||||
if (mag == 0)
|
||||
return -1;
|
||||
int n = 0;
|
||||
while (mag > 1)
|
||||
{
|
||||
mag >>= 1;
|
||||
++n;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_CONST
|
||||
HEDLEY_NO_THROW
|
||||
constexpr u128 magnitude(std::int64_t raw) noexcept
|
||||
{
|
||||
using lim = std::numeric_limits<Raw>;
|
||||
if (raw == static_cast<std::int64_t>(lim::min()))
|
||||
return u128{1} << (raw_width<Raw>() - 1);
|
||||
const auto abs = raw < 0 ? -raw : raw;
|
||||
return static_cast<u128>(abs);
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_CONST
|
||||
HEDLEY_NO_THROW
|
||||
constexpr int ilogb_units(std::int64_t raw, unsigned fractional_bits) noexcept
|
||||
{
|
||||
if (raw == 0)
|
||||
return 0;
|
||||
return floor_log2_u128(magnitude<Raw>(raw)) - static_cast<int>(fractional_bits);
|
||||
}
|
||||
|
||||
inline u128 pow10_u128(int exponent)
|
||||
{
|
||||
if (exponent < 0 || exponent > 38)
|
||||
throw std::invalid_argument("dyadic lut: power of ten is out of range");
|
||||
u128 p = 1;
|
||||
for (int i = 0; i < exponent; ++i)
|
||||
p *= 10;
|
||||
return p;
|
||||
}
|
||||
|
||||
/// `mag / 2^k >= 10^e`.
|
||||
inline bool magnitude_ge_pow10(u128 mag, unsigned fractional_bits, int exponent)
|
||||
{
|
||||
if (mag == 0)
|
||||
return false;
|
||||
if (exponent >= 0)
|
||||
{
|
||||
const u128 decade = pow10_u128(exponent);
|
||||
if (fractional_bits > 0 && decade > (~u128{0} >> fractional_bits))
|
||||
return false;
|
||||
return mag >= (decade << fractional_bits);
|
||||
}
|
||||
const u128 decade = pow10_u128(-exponent);
|
||||
const u128 scale = u128{1} << fractional_bits;
|
||||
u128 threshold = scale / decade;
|
||||
if (scale % decade != 0)
|
||||
++threshold;
|
||||
return mag >= threshold;
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
int ilog10_units(std::int64_t raw, unsigned fractional_bits)
|
||||
{
|
||||
if (raw == 0)
|
||||
return 0;
|
||||
const u128 mag = magnitude<Raw>(raw);
|
||||
int lo = -static_cast<int>(fractional_bits) - 2;
|
||||
int hi = raw_width<Raw>();
|
||||
while (lo < hi)
|
||||
{
|
||||
const int mid = lo + (hi - lo + 1) / 2;
|
||||
if (magnitude_ge_pow10(mag, fractional_bits, mid))
|
||||
lo = mid;
|
||||
else
|
||||
hi = mid - 1;
|
||||
}
|
||||
return lo;
|
||||
}
|
||||
|
||||
template <typename Raw, typename At>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> steps_from_cuts(std::vector<std::int64_t> cuts, At && at)
|
||||
{
|
||||
return assemble_easy<Raw>(std::move(cuts), [&](std::int64_t raw) {
|
||||
return at(raw);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> predicate_lut(unsigned fractional_bits, bool at_or_below_zero,
|
||||
bool at_zero, bool above_zero)
|
||||
{
|
||||
std::vector<std::int64_t> cuts{0, 1};
|
||||
return steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||
const bool on = raw < 0 ? at_or_below_zero : (raw == 0 ? at_zero : above_zero);
|
||||
return indicator_poly(on, fractional_bits);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_positive_lut(unsigned fractional_bits = 0)
|
||||
{
|
||||
return detail::predicate_lut<Raw>(fractional_bits, false, false, true);
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_negative_lut(unsigned fractional_bits = 0)
|
||||
{
|
||||
return detail::predicate_lut<Raw>(fractional_bits, true, false, false);
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_nonnegative_lut(unsigned fractional_bits = 0)
|
||||
{
|
||||
return detail::predicate_lut<Raw>(fractional_bits, false, true, true);
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_nonpositive_lut(unsigned fractional_bits = 0)
|
||||
{
|
||||
return detail::predicate_lut<Raw>(fractional_bits, true, true, false);
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_zero_lut(unsigned fractional_bits = 0)
|
||||
{
|
||||
return detail::predicate_lut<Raw>(fractional_bits, false, true, false);
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_nonzero_lut(unsigned fractional_bits = 0)
|
||||
{
|
||||
return detail::predicate_lut<Raw>(fractional_bits, true, false, true);
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_signum_lut(unsigned fractional_bits = 0)
|
||||
{
|
||||
const std::int64_t one = detail::encode_units(1, fractional_bits);
|
||||
return detail::steps_from_cuts<Raw>({0, 1}, [=](std::int64_t raw) {
|
||||
if (raw < 0)
|
||||
return detail::easy_poly{-one, 0, 0, 1};
|
||||
if (raw == 0)
|
||||
return detail::kZero;
|
||||
return detail::easy_poly{one, 0, 0, 1};
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_clz_lut(unsigned fractional_bits = 0)
|
||||
{
|
||||
constexpr int width = detail::raw_width<Raw>();
|
||||
std::vector<std::int64_t> cuts{0, 1};
|
||||
for (int b = 1; b <= width - 2; ++b)
|
||||
cuts.push_back(std::int64_t{1} << b);
|
||||
return detail::steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||
return detail::unit_poly(detail::clz_of<Raw>(raw), fractional_bits);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_clrsb_lut(unsigned fractional_bits = 0)
|
||||
{
|
||||
constexpr int width = detail::raw_width<Raw>();
|
||||
std::vector<std::int64_t> cuts{0, -1, -2};
|
||||
for (int exp = 0; exp <= width - 2; ++exp)
|
||||
cuts.push_back(std::int64_t{1} << exp);
|
||||
for (int exp = 2; exp <= width - 1; ++exp)
|
||||
{
|
||||
if (exp >= 63)
|
||||
cuts.push_back(static_cast<std::int64_t>(std::numeric_limits<Raw>::min()));
|
||||
else
|
||||
cuts.push_back(-(std::int64_t{1} << exp));
|
||||
}
|
||||
return detail::steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||
return detail::unit_poly(detail::clrsb_of<Raw>(raw), fractional_bits);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_ilogb_lut(unsigned fractional_bits = 0)
|
||||
{
|
||||
constexpr int width = detail::raw_width<Raw>();
|
||||
std::vector<std::int64_t> cuts{0, 1};
|
||||
for (int b = 1; b <= width - 2; ++b)
|
||||
cuts.push_back(std::int64_t{1} << b);
|
||||
for (int exp = 1; exp <= width - 1; ++exp)
|
||||
{
|
||||
if (exp >= width - 1)
|
||||
cuts.push_back(static_cast<std::int64_t>(std::numeric_limits<Raw>::min()) + 1);
|
||||
else
|
||||
cuts.push_back(-(std::int64_t{1} << exp) + 1);
|
||||
}
|
||||
return detail::steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||
if (raw == 0)
|
||||
return detail::easy_poly{ilog_of_zero, 0, 0, 1};
|
||||
return detail::unit_poly(detail::ilogb_units<Raw>(raw, fractional_bits),
|
||||
fractional_bits);
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_ilog10_lut(unsigned fractional_bits = 0)
|
||||
{
|
||||
using lim = std::numeric_limits<Raw>;
|
||||
const std::int64_t maxv = static_cast<std::int64_t>(lim::max());
|
||||
const int lo = detail::ilog10_units<Raw>(1, fractional_bits);
|
||||
const int hi_pos = detail::ilog10_units<Raw>(maxv, fractional_bits);
|
||||
const int hi_neg = detail::ilog10_units<Raw>(static_cast<std::int64_t>(lim::min()),
|
||||
fractional_bits);
|
||||
const int hi = hi_pos > hi_neg ? hi_pos : hi_neg;
|
||||
|
||||
std::vector<std::int64_t> cuts{0, 1};
|
||||
std::int64_t prev = 0;
|
||||
for (int e = lo; e <= hi; ++e)
|
||||
{
|
||||
std::int64_t left = 1;
|
||||
std::int64_t right = maxv;
|
||||
while (left < right)
|
||||
{
|
||||
const std::int64_t mid = left + (right - left) / 2;
|
||||
if (detail::ilog10_units<Raw>(mid, fractional_bits) >= e)
|
||||
right = mid;
|
||||
else
|
||||
left = mid + 1;
|
||||
}
|
||||
if (left == prev)
|
||||
continue;
|
||||
prev = left;
|
||||
cuts.push_back(left);
|
||||
if (left < maxv)
|
||||
{
|
||||
std::int64_t end = left;
|
||||
std::int64_t scan_left = left;
|
||||
std::int64_t scan_right = maxv;
|
||||
while (scan_left < scan_right)
|
||||
{
|
||||
const std::int64_t mid = scan_left + (scan_right - scan_left + 1) / 2;
|
||||
if (detail::ilog10_units<Raw>(mid, fractional_bits) == e)
|
||||
scan_left = mid;
|
||||
else
|
||||
scan_right = mid - 1;
|
||||
}
|
||||
end = scan_left;
|
||||
const std::int64_t neg = -end;
|
||||
if (neg > static_cast<std::int64_t>(lim::min()))
|
||||
cuts.push_back(neg);
|
||||
}
|
||||
}
|
||||
return detail::steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||
if (raw == 0)
|
||||
return detail::easy_poly{ilog_of_zero, 0, 0, 1};
|
||||
return detail::unit_poly(detail::ilog10_units<Raw>(raw, fractional_bits),
|
||||
fractional_bits);
|
||||
});
|
||||
}
|
||||
|
||||
/// Bit `index` counting down from the most significant bit of `Raw`.
|
||||
/// Index 0 is the sign bit. Larger indexes are refused: the bit is constant
|
||||
/// on `2^{index+1}` intervals.
|
||||
template <typename Raw>
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
easy_lut<Raw> make_msb_lut(unsigned index, unsigned fractional_bits = 0)
|
||||
{
|
||||
constexpr int width = detail::raw_width<Raw>();
|
||||
if (index >= msb_bit_limit || static_cast<int>(index) >= width)
|
||||
throw std::invalid_argument(
|
||||
"msb lut: only the most significant bits are piecewise-cheap");
|
||||
const int shift = width - 1 - static_cast<int>(index);
|
||||
if (shift >= 63)
|
||||
{
|
||||
return detail::steps_from_cuts<Raw>({0}, [=](std::int64_t raw) {
|
||||
const bool on = ((detail::raw_bits<Raw>(raw) >> shift) & 1u) != 0;
|
||||
return detail::indicator_poly(on, fractional_bits);
|
||||
});
|
||||
}
|
||||
const std::int64_t step = std::int64_t{1} << shift;
|
||||
std::vector<std::int64_t> cuts;
|
||||
const auto minv = static_cast<std::int64_t>(std::numeric_limits<Raw>::min());
|
||||
for (std::int64_t boundary = minv; ; )
|
||||
{
|
||||
cuts.push_back(boundary);
|
||||
if (boundary > static_cast<std::int64_t>(std::numeric_limits<Raw>::max()) - step)
|
||||
break;
|
||||
boundary += step;
|
||||
}
|
||||
return detail::steps_from_cuts<Raw>(std::move(cuts), [=](std::int64_t raw) {
|
||||
const bool on = ((detail::raw_bits<Raw>(raw) >> shift) & 1u) != 0;
|
||||
return detail::indicator_poly(on, fractional_bits);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace grotto
|
||||
|
||||
#endif // LIBDPF_INCLUDE_GROTTO_DYADIC_LUT_HPP__
|
||||
|
|
@ -9,6 +9,8 @@
|
|||
#ifndef LIBDPF_INCLUDE_GROTTO_EASY_LUT_HPP__
|
||||
#define LIBDPF_INCLUDE_GROTTO_EASY_LUT_HPP__
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
|
|
@ -35,6 +37,7 @@ struct easy_lut
|
|||
std::vector<std::int64_t> c2;
|
||||
std::vector<std::int64_t> den;
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
std::size_t parts() const noexcept { return c0.size(); }
|
||||
|
||||
std::int64_t operator()(Raw x) const
|
||||
|
|
@ -82,6 +85,7 @@ struct easy_poly
|
|||
std::int64_t den = 1;
|
||||
};
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
inline bool operator==(easy_poly a, easy_poly b) noexcept
|
||||
{
|
||||
return a.c0 == b.c0 && a.c1 == b.c1 && a.c2 == b.c2 && a.den == b.den;
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ namespace detail
|
|||
|
||||
/// @brief Integer value of an already-rounded finite double, as a 256-bit word.
|
||||
/// Values that do not fit saturate to all-ones.
|
||||
HEDLEY_NO_THROW
|
||||
inline uint256_t uint256_from_rounded_double(double rounded) noexcept
|
||||
{
|
||||
if (!(rounded > 0.0) || !std::isfinite(rounded))
|
||||
|
|
@ -99,6 +100,7 @@ struct is_static_castable<To, From,
|
|||
|
||||
/// Low `bits` of `wide`, saturated to all-ones when `wide` does not fit.
|
||||
template <typename Raw, std::size_t Bits>
|
||||
HEDLEY_NO_THROW
|
||||
Raw saturate_low_bits(uint256_t wide) noexcept
|
||||
{
|
||||
static_assert(Bits > 0 && Bits <= 256);
|
||||
|
|
@ -151,6 +153,7 @@ Raw saturate_low_bits(uint256_t wide) noexcept
|
|||
}
|
||||
|
||||
template <typename IntegralType>
|
||||
HEDLEY_NO_THROW
|
||||
inline IntegralType rounded_double_to_integral(double rounded) noexcept
|
||||
{
|
||||
if constexpr (std::is_integral_v<IntegralType>
|
||||
|
|
@ -206,6 +209,7 @@ template <typename IntegralType,
|
|||
typename T>
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
HEDLEY_CONST
|
||||
HEDLEY_NO_THROW
|
||||
constexpr IntegralType scale_integer_to_fixed_raw(T integer_value) noexcept
|
||||
{
|
||||
using unsigned_type = dpf::utils::make_unsigned_t<IntegralType>;
|
||||
|
|
@ -228,6 +232,7 @@ inline constexpr bool is_signed_rep_v =
|
|||
template <typename IntegralType>
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
HEDLEY_CONST
|
||||
HEDLEY_NO_THROW
|
||||
constexpr IntegralType raw_neg(IntegralType x) noexcept
|
||||
{
|
||||
using unsigned_type = dpf::utils::make_unsigned_t<IntegralType>;
|
||||
|
|
@ -238,6 +243,7 @@ constexpr IntegralType raw_neg(IntegralType x) noexcept
|
|||
template <typename IntegralType>
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
HEDLEY_CONST
|
||||
HEDLEY_NO_THROW
|
||||
constexpr IntegralType raw_abs(IntegralType x) noexcept
|
||||
{
|
||||
if constexpr (is_signed_rep_v<IntegralType>)
|
||||
|
|
@ -252,6 +258,7 @@ constexpr IntegralType raw_abs(IntegralType x) noexcept
|
|||
template <typename IntegralType>
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
HEDLEY_CONST
|
||||
HEDLEY_NO_THROW
|
||||
constexpr IntegralType raw_fmod(IntegralType a, IntegralType b) noexcept
|
||||
{
|
||||
if (b == IntegralType{})
|
||||
|
|
@ -265,6 +272,7 @@ constexpr IntegralType raw_fmod(IntegralType a, IntegralType b) noexcept
|
|||
|
||||
template <unsigned FractionalBits,
|
||||
typename IntegralType>
|
||||
HEDLEY_NO_THROW
|
||||
auto constexpr make_fixed_from_integral_type(IntegralType value) noexcept;
|
||||
|
||||
/// @tparam FractionalBits Number of fractional bits used in the fixed-point
|
||||
|
|
@ -378,6 +386,7 @@ public:
|
|||
~fixedpoint() = default;
|
||||
|
||||
/// @brief Cast to `double`
|
||||
HEDLEY_NO_THROW
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
HEDLEY_PURE
|
||||
explicit constexpr operator double() const noexcept
|
||||
|
|
@ -728,6 +737,7 @@ public:
|
|||
template <unsigned FractionalBits,
|
||||
typename IntegralType,
|
||||
typename Mask>
|
||||
HEDLEY_NO_THROW
|
||||
constexpr bool operator&(const Mask & mask,
|
||||
const fixedpoint<FractionalBits, IntegralType> & x) noexcept
|
||||
{
|
||||
|
|
@ -739,6 +749,7 @@ template <class CharT,
|
|||
class Traits,
|
||||
unsigned FractionalBits,
|
||||
typename IntegralType>
|
||||
HEDLEY_NO_THROW
|
||||
std::basic_ostream<CharT, Traits> &
|
||||
operator<<(std::basic_ostream<CharT, Traits> & os,
|
||||
const fixedpoint<FractionalBits, IntegralType> & f) noexcept
|
||||
|
|
@ -762,6 +773,7 @@ operator>>(std::basic_istream<CharT, Traits> & is,
|
|||
|
||||
template <unsigned FractionalBits,
|
||||
typename IntegralType>
|
||||
HEDLEY_NO_THROW
|
||||
auto constexpr make_fixed_from_integral_type(IntegralType value) noexcept
|
||||
{
|
||||
return fixedpoint<FractionalBits, IntegralType>::from_raw(value);
|
||||
|
|
@ -811,6 +823,7 @@ static auto make_fixed_safe(double d)
|
|||
template <unsigned ToFractionalBits,
|
||||
unsigned FromFractionalBits,
|
||||
typename IntegralType>
|
||||
HEDLEY_NO_THROW
|
||||
constexpr auto precision_cast(const fixedpoint<FromFractionalBits, IntegralType> & f) noexcept
|
||||
{
|
||||
auto value = f.integral_representation();
|
||||
|
|
@ -826,6 +839,7 @@ constexpr auto precision_cast(const fixedpoint<FromFractionalBits, IntegralType>
|
|||
|
||||
template <unsigned FractionalBits,
|
||||
typename IntegralType>
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr auto precision_of(fixedpoint<FractionalBits, IntegralType>) noexcept
|
||||
{
|
||||
return FractionalBits;
|
||||
|
|
@ -1466,6 +1480,7 @@ struct countl_zero_symmetric_difference<grotto::fixedpoint<FractionalBits, Integ
|
|||
using T = grotto::fixedpoint<FractionalBits, IntegralType>;
|
||||
static constexpr auto clz = dpf::utils::countl_zero_symmetric_difference<typename T::integral_type>{};
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
HEDLEY_PURE
|
||||
HEDLEY_ALWAYS_INLINE
|
||||
constexpr std::size_t operator()(const T & lhs, const T & rhs) const noexcept
|
||||
|
|
@ -1499,6 +1514,7 @@ struct mod_pow_2<grotto::fixedpoint<FractionalBits, IntegralType>>
|
|||
using fixed_type = grotto::fixedpoint<FractionalBits, IntegralType>;
|
||||
static constexpr auto mod = mod_pow_2<IntegralType>{};
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
std::size_t operator()(fixed_type val, std::size_t n) const noexcept
|
||||
{
|
||||
return mod(val.integral_representation(), n);
|
||||
|
|
@ -1512,6 +1528,7 @@ struct make_from_integral_value<grotto::fixedpoint<FractionalBits, IntegralType>
|
|||
using fixed_type = grotto::fixedpoint<FractionalBits, IntegralType>;
|
||||
using integral_type = typename to_integral_type<fixed_type>::integral_type;
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr fixed_type operator()(integral_type val) const noexcept
|
||||
{
|
||||
return grotto::make_fixed_from_integral_type<FractionalBits, IntegralType>(
|
||||
|
|
@ -1617,6 +1634,7 @@ class numeric_limits<grotto::fixedpoint<FractionalBits, IntegralType>>
|
|||
static constexpr int bitwidth =
|
||||
static_cast<int>(dpf::utils::bitlength_of_v<I>);
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr I raw_lowest() noexcept
|
||||
{
|
||||
if constexpr (signed_rep)
|
||||
|
|
@ -1627,6 +1645,7 @@ class numeric_limits<grotto::fixedpoint<FractionalBits, IntegralType>>
|
|||
return I{};
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr I raw_max() noexcept
|
||||
{
|
||||
if constexpr (signed_rep)
|
||||
|
|
@ -1664,8 +1683,11 @@ class numeric_limits<grotto::fixedpoint<FractionalBits, IntegralType>>
|
|||
static constexpr bool traps = false;
|
||||
static constexpr bool tinyness_before = false;
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr T lowest() noexcept { return T::from_raw(raw_lowest()); }
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr T max() noexcept { return T::from_raw(raw_max()); }
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr T min() noexcept
|
||||
{
|
||||
if constexpr (FractionalBits == 0)
|
||||
|
|
@ -1674,17 +1696,23 @@ class numeric_limits<grotto::fixedpoint<FractionalBits, IntegralType>>
|
|||
}
|
||||
return T::from_raw(I{1});
|
||||
}
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr T epsilon() noexcept
|
||||
{
|
||||
return FractionalBits ? T::from_raw(I{1}) : T::from_raw(I{});
|
||||
}
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr T round_error() noexcept
|
||||
{
|
||||
return FractionalBits ? T(0.5) : T(0);
|
||||
}
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr T infinity() noexcept { return max(); }
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr T quiet_NaN() noexcept { return T::from_raw(I{}); }
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr T signalling_NaN() noexcept { return T::from_raw(I{}); }
|
||||
HEDLEY_NO_THROW
|
||||
static constexpr T denorm_min() noexcept { return min(); }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
#ifndef LIBDPF_INCLUDE_GROTTO_FIXEDPOINT_MUL_HPP__
|
||||
#define LIBDPF_INCLUDE_GROTTO_FIXEDPOINT_MUL_HPP__
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
#ifndef LIBDPF_INCLUDE_DPF_FIXEDPOINT_HPP__
|
||||
#include "grotto/fixedpoint.hpp"
|
||||
#endif
|
||||
|
|
@ -106,6 +108,7 @@ namespace detail
|
|||
|
||||
inline constexpr std::size_t fixed_mul_buf_limbs = 12;
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr void mask_to_bits(std::uint64_t * limbs, std::size_t nlimbs, unsigned bits) noexcept
|
||||
{
|
||||
if (bits >= nlimbs * 64u)
|
||||
|
|
@ -129,11 +132,13 @@ constexpr void mask_to_bits(std::uint64_t * limbs, std::size_t nlimbs, unsigned
|
|||
}
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr bool test_bit(const std::uint64_t * limbs, unsigned bit) noexcept
|
||||
{
|
||||
return ((limbs[bit / 64u] >> (bit % 64u)) & 1u) != 0u;
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr void fill_ones(std::uint64_t * limbs, unsigned from, unsigned to) noexcept
|
||||
{
|
||||
for (unsigned bit = from; bit < to; )
|
||||
|
|
@ -149,6 +154,7 @@ constexpr void fill_ones(std::uint64_t * limbs, unsigned from, unsigned to) noex
|
|||
}
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr void sign_extend_range(std::uint64_t * limbs, unsigned from_bits, unsigned to_bits) noexcept
|
||||
{
|
||||
if (to_bits <= from_bits || from_bits == 0u)
|
||||
|
|
@ -162,6 +168,7 @@ constexpr void sign_extend_range(std::uint64_t * limbs, unsigned from_bits, unsi
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
constexpr void store_raw_limbs(const T & value, std::uint64_t out[4]) noexcept
|
||||
{
|
||||
out[0] = out[1] = out[2] = out[3] = 0;
|
||||
|
|
@ -192,6 +199,7 @@ constexpr void store_raw_limbs(const T & value, std::uint64_t out[4]) noexcept
|
|||
|
||||
/// Low `dest_bits` of `value`, sign-extended when `value` is a narrower signed integer.
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
constexpr void reduce_operand(const T & value, unsigned src_bits, bool is_signed,
|
||||
unsigned dest_bits, std::uint64_t * dest, unsigned nlimbs) noexcept
|
||||
{
|
||||
|
|
@ -215,6 +223,7 @@ constexpr void reduce_operand(const T & value, unsigned src_bits, bool is_signed
|
|||
}
|
||||
|
||||
/// Product modulo `2^(64*nlimbs)`, using exactly `nlimbs` limbs of each operand.
|
||||
HEDLEY_NO_THROW
|
||||
constexpr void mul_low_limbs(std::uint64_t * out, const std::uint64_t * lhs,
|
||||
const std::uint64_t * rhs, unsigned nlimbs) noexcept
|
||||
{
|
||||
|
|
@ -235,6 +244,7 @@ constexpr void mul_low_limbs(std::uint64_t * out, const std::uint64_t * lhs,
|
|||
}
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr void shift_left_limbs(std::uint64_t * limbs, std::size_t nlimbs, unsigned shift) noexcept
|
||||
{
|
||||
if (shift == 0u)
|
||||
|
|
@ -261,6 +271,7 @@ constexpr void shift_left_limbs(std::uint64_t * limbs, std::size_t nlimbs, unsig
|
|||
}
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr void shift_right_limbs(std::uint64_t * limbs, std::size_t nlimbs, unsigned shift) noexcept
|
||||
{
|
||||
if (shift == 0u)
|
||||
|
|
@ -288,6 +299,7 @@ constexpr void shift_right_limbs(std::uint64_t * limbs, std::size_t nlimbs, unsi
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
constexpr T limbs_to_integral(const std::uint64_t * limbs) noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<T, uint256_t>)
|
||||
|
|
@ -331,6 +343,7 @@ template <unsigned IntegerBits,
|
|||
typename LhsIntegral,
|
||||
unsigned RhsFractionalBits,
|
||||
typename RhsIntegral>
|
||||
HEDLEY_NO_THROW
|
||||
constexpr auto fixed_mul(
|
||||
fixedpoint<LhsFractionalBits, LhsIntegral> lhs,
|
||||
fixedpoint<RhsFractionalBits, RhsIntegral> rhs) noexcept
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@
|
|||
#ifndef LIBDPF_INCLUDE_GROTTO_HEXFLOAT_HPP__
|
||||
#define LIBDPF_INCLUDE_GROTTO_HEXFLOAT_HPP__
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
|
|
@ -109,6 +111,7 @@ constexpr bool is_hexfloat_fixedpoint = false;
|
|||
template <unsigned FractionalBits, typename Integral>
|
||||
constexpr bool is_hexfloat_fixedpoint<fixedpoint<FractionalBits, Integral>> = true;
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr void mask_low_bits(std::uint64_t * limbs, unsigned width) noexcept
|
||||
{
|
||||
if (width >= 256u)
|
||||
|
|
@ -132,11 +135,13 @@ constexpr void mask_low_bits(std::uint64_t * limbs, unsigned width) noexcept
|
|||
}
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr bool bit_is_set(const std::uint64_t * limbs, unsigned bit) noexcept
|
||||
{
|
||||
return ((limbs[bit / 64u] >> (bit % 64u)) & 1u) != 0u;
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr void negate_low_bits(std::uint64_t * limbs, unsigned width) noexcept
|
||||
{
|
||||
for (unsigned i = 0; i < 4u; ++i)
|
||||
|
|
@ -154,6 +159,7 @@ constexpr void negate_low_bits(std::uint64_t * limbs, unsigned width) noexcept
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
constexpr void store_integer_bits(const T & value, std::uint64_t out[4]) noexcept
|
||||
{
|
||||
out[0] = out[1] = out[2] = out[3] = 0;
|
||||
|
|
@ -183,6 +189,7 @@ constexpr void store_integer_bits(const T & value, std::uint64_t out[4]) noexcep
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
constexpr T load_integer_bits(const std::uint64_t * limbs) noexcept
|
||||
{
|
||||
if constexpr (std::is_same_v<T, uint256_t>)
|
||||
|
|
@ -205,6 +212,7 @@ constexpr T load_integer_bits(const std::uint64_t * limbs) noexcept
|
|||
}
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
inline int highest_bit(const std::uint64_t * limbs) noexcept
|
||||
{
|
||||
for (int i = 3; i >= 0; --i)
|
||||
|
|
@ -268,6 +276,7 @@ std::string format_hexfloat(const T & value, int fractional_bits)
|
|||
return std::string(negative ? "-" : "+") + "0x1." + fraction + "p" + expbuf;
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
inline int hex_value(char c) noexcept
|
||||
{
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
|
|
|
|||
194
include/grotto/nmod.hpp
Normal file
194
include/grotto/nmod.hpp
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
/// @file grotto/nmod.hpp
|
||||
/// @brief Reduction modulo an arbitrary public modulus.
|
||||
/// @details `nmod` multiplies by a rounded reciprocal `1/M` and splits that
|
||||
/// product with a floor. The quotient is `floor(x/M)`. The residue
|
||||
/// is `{x/M}` truncated onto `residue_bits` fractional bits, so it
|
||||
/// lies in `[0, 2^residue_bits)`. A negative product borrows, which
|
||||
/// keeps the residue non-negative.
|
||||
///
|
||||
/// The reciprocal magnitude is 128 bits, so `1/M` can be carried
|
||||
/// wider than the input. A power-of-two modulus is the same split
|
||||
/// with an exact shift (`nmod_pow2`).
|
||||
/// @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_GROTTO_NMOD_HPP__
|
||||
#define LIBDPF_INCLUDE_GROTTO_NMOD_HPP__
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
namespace grotto
|
||||
{
|
||||
|
||||
namespace nmod_detail
|
||||
{
|
||||
using u128 = unsigned __int128;
|
||||
}
|
||||
|
||||
struct nmod_result
|
||||
{
|
||||
/// `floor({x/M} * 2^residue_bits)`, in `[0, 2^residue_bits)`.
|
||||
std::int64_t residue = 0;
|
||||
/// `floor(x/M)`.
|
||||
std::int64_t quotient = 0;
|
||||
};
|
||||
|
||||
/// `x_raw / 2^x_bits` modulo `M`, with `1/M ≈ recip_raw / 2^recip_bits`.
|
||||
/// `recip_raw` is a positive magnitude of at most 128 bits.
|
||||
/// @throws std::invalid_argument if the reciprocal is zero or a width is illegal.
|
||||
/// @throws std::overflow_error if the quotient does not fit in `int64_t`.
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
inline nmod_result nmod(std::int64_t x_raw, unsigned x_bits,
|
||||
unsigned __int128 recip_raw, unsigned recip_bits, unsigned residue_bits)
|
||||
{
|
||||
if (recip_raw == 0)
|
||||
throw std::invalid_argument("nmod: reciprocal must be positive");
|
||||
if (residue_bits > 63)
|
||||
throw std::invalid_argument("nmod: residue must fit in int64");
|
||||
if (x_bits > 100000u || recip_bits > 100000u)
|
||||
throw std::invalid_argument("nmod: fractional width is too large");
|
||||
|
||||
const bool neg = x_raw < 0;
|
||||
const auto x_mag = static_cast<std::uint64_t>(
|
||||
neg ? -static_cast<__int128>(x_raw) : x_raw);
|
||||
const auto recip_lo = static_cast<std::uint64_t>(recip_raw);
|
||||
const auto recip_hi = static_cast<std::uint64_t>(recip_raw >> 64);
|
||||
|
||||
unsigned __int128 low = static_cast<unsigned __int128>(x_mag) * recip_lo;
|
||||
unsigned __int128 high = static_cast<unsigned __int128>(x_mag) * recip_hi;
|
||||
std::uint64_t limb[4] = {};
|
||||
limb[0] = static_cast<std::uint64_t>(low);
|
||||
const unsigned __int128 mid = (low >> 64) + static_cast<std::uint64_t>(high);
|
||||
limb[1] = static_cast<std::uint64_t>(mid);
|
||||
const unsigned __int128 top = (high >> 64) + (mid >> 64);
|
||||
limb[2] = static_cast<std::uint64_t>(top);
|
||||
limb[3] = static_cast<std::uint64_t>(top >> 64);
|
||||
|
||||
const auto bit_set_at_or_above = [&](unsigned bit) {
|
||||
if (bit >= 256)
|
||||
return false;
|
||||
const unsigned index = bit / 64u;
|
||||
const unsigned offset = bit % 64u;
|
||||
if ((limb[index] >> offset) != 0)
|
||||
return true;
|
||||
for (unsigned i = index + 1; i < 4; ++i)
|
||||
{
|
||||
if (limb[i] != 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const auto extract = [&](unsigned low_bit, unsigned count) -> std::uint64_t {
|
||||
if (count == 0 || low_bit >= 256)
|
||||
return 0;
|
||||
const unsigned index = low_bit / 64u;
|
||||
const unsigned offset = low_bit % 64u;
|
||||
unsigned __int128 chunk = limb[index];
|
||||
if (index + 1 < 4)
|
||||
chunk |= static_cast<unsigned __int128>(limb[index + 1]) << 64;
|
||||
chunk >>= offset;
|
||||
if (count == 64)
|
||||
return static_cast<std::uint64_t>(chunk);
|
||||
return static_cast<std::uint64_t>(chunk) & ((std::uint64_t{1} << count) - 1);
|
||||
};
|
||||
const auto low_bits_set = [&](unsigned width) {
|
||||
if (width == 0)
|
||||
return false;
|
||||
if (width >= 256)
|
||||
return limb[0] != 0 || limb[1] != 0 || limb[2] != 0 || limb[3] != 0;
|
||||
const unsigned index = width / 64u;
|
||||
const unsigned offset = width % 64u;
|
||||
for (unsigned i = 0; i < index; ++i)
|
||||
{
|
||||
if (limb[i] != 0)
|
||||
return true;
|
||||
}
|
||||
if (offset == 0)
|
||||
return false;
|
||||
const std::uint64_t mask = (std::uint64_t{1} << offset) - 1;
|
||||
return (limb[index] & mask) != 0;
|
||||
};
|
||||
|
||||
const unsigned scale = x_bits + recip_bits;
|
||||
const bool remainder = low_bits_set(scale);
|
||||
std::uint64_t quotient_mag = 0;
|
||||
if (scale < 256)
|
||||
{
|
||||
if (bit_set_at_or_above(scale + 64))
|
||||
throw std::overflow_error("nmod: quotient does not fit int64");
|
||||
quotient_mag = extract(scale, 64);
|
||||
}
|
||||
|
||||
nmod_result out;
|
||||
if (!neg)
|
||||
{
|
||||
if (quotient_mag > static_cast<std::uint64_t>(INT64_MAX))
|
||||
throw std::overflow_error("nmod: quotient does not fit int64");
|
||||
out.quotient = static_cast<std::int64_t>(quotient_mag);
|
||||
}
|
||||
else if (!remainder)
|
||||
{
|
||||
if (quotient_mag > (static_cast<std::uint64_t>(INT64_MAX) + 1))
|
||||
throw std::overflow_error("nmod: quotient does not fit int64");
|
||||
out.quotient = quotient_mag == (std::uint64_t{1} << 63)
|
||||
? INT64_MIN
|
||||
: -static_cast<std::int64_t>(quotient_mag);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (quotient_mag > static_cast<std::uint64_t>(INT64_MAX))
|
||||
throw std::overflow_error("nmod: quotient does not fit int64");
|
||||
out.quotient = -static_cast<std::int64_t>(quotient_mag) - 1;
|
||||
}
|
||||
|
||||
if (residue_bits == 0 || (!neg && !remainder) || (neg && !remainder))
|
||||
return out;
|
||||
|
||||
// A 128-bit reciprocal times an int64 magnitude stays under 2^192.
|
||||
// With a residue of at most 63 bits, a scale at or above 256 puts every
|
||||
// product bit strictly below the residue window.
|
||||
if (scale >= 256)
|
||||
{
|
||||
if (neg)
|
||||
out.residue = static_cast<std::int64_t>((std::uint64_t{1} << residue_bits) - 1);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::uint64_t field = 0;
|
||||
if (scale >= residue_bits)
|
||||
field = extract(scale - residue_bits, residue_bits);
|
||||
else
|
||||
field = extract(0, scale) << (residue_bits - scale);
|
||||
|
||||
if (neg)
|
||||
{
|
||||
const std::uint64_t unit = std::uint64_t{1} << residue_bits;
|
||||
const unsigned discarded = scale >= residue_bits ? scale - residue_bits : 0;
|
||||
const bool borrow = discarded > 0 && low_bits_set(discarded);
|
||||
field = borrow ? unit - field - 1 : unit - field;
|
||||
}
|
||||
out.residue = static_cast<std::int64_t>(field);
|
||||
return out;
|
||||
}
|
||||
|
||||
/// Modulus `2^{-exp}` by an exact shift. Positive `exp` multiplies by
|
||||
/// `2^exp` (`exp`'s `2^{-13}` split). Zero splits at the integer (`2^x`).
|
||||
/// Negative `exp` is a modulus above one.
|
||||
/// @throws std::overflow_error if `exp` does not fit the reciprocal width.
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
inline nmod_result nmod_pow2(std::int64_t x_raw, unsigned x_bits, int exp,
|
||||
unsigned residue_bits)
|
||||
{
|
||||
if (exp >= 128 || exp < -100000)
|
||||
throw std::overflow_error("nmod: power-of-two reciprocal does not fit");
|
||||
if (exp >= 0)
|
||||
return nmod(x_raw, x_bits, nmod_detail::u128{1} << static_cast<unsigned>(exp), 0, residue_bits);
|
||||
return nmod(x_raw, x_bits, 1, static_cast<unsigned>(-exp), residue_bits);
|
||||
}
|
||||
|
||||
} // namespace grotto
|
||||
|
||||
#endif // LIBDPF_INCLUDE_GROTTO_NMOD_HPP__
|
||||
|
|
@ -22,6 +22,8 @@
|
|||
#ifndef LIBDPF_INCLUDE_GROTTO_OFFSET_HORNER_HPP__
|
||||
#define LIBDPF_INCLUDE_GROTTO_OFFSET_HORNER_HPP__
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
|
|
@ -43,6 +45,7 @@ namespace grotto
|
|||
inline constexpr std::size_t offset_horner_max_degree = 3;
|
||||
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
T offset_horner_group_add(T a, T b) noexcept
|
||||
{
|
||||
using u = std::make_unsigned_t<T>;
|
||||
|
|
@ -50,6 +53,7 @@ T offset_horner_group_add(T a, T b) noexcept
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
T offset_horner_group_sub(T a, T b) noexcept
|
||||
{
|
||||
using u = std::make_unsigned_t<T>;
|
||||
|
|
@ -65,6 +69,7 @@ struct offset_horner_x_plus_r
|
|||
};
|
||||
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
offset_horner_x_plus_r<T> offset_horner_at_x_plus_r(T x, T r) noexcept
|
||||
{
|
||||
return offset_horner_x_plus_r<T>{
|
||||
|
|
@ -86,6 +91,7 @@ inline constexpr uint64_t binom[4][4] = {
|
|||
};
|
||||
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
uint64_t lift(T v) noexcept
|
||||
{
|
||||
if constexpr (std::is_signed_v<T>)
|
||||
|
|
@ -95,6 +101,7 @@ uint64_t lift(T v) noexcept
|
|||
}
|
||||
|
||||
template <std::size_t Degree>
|
||||
HEDLEY_NO_THROW
|
||||
uint64_t horner_at(const std::array<uint64_t, Degree + 1> & coeff, uint64_t point) noexcept
|
||||
{
|
||||
uint64_t acc = coeff[Degree];
|
||||
|
|
@ -104,6 +111,7 @@ uint64_t horner_at(const std::array<uint64_t, Degree + 1> & coeff, uint64_t poin
|
|||
}
|
||||
|
||||
template <std::size_t Degree>
|
||||
HEDLEY_NO_THROW
|
||||
void fill_payloads(uint64_t base, uint64_t (&payload)[Degree + 1]) noexcept
|
||||
{
|
||||
uint64_t pow = 1;
|
||||
|
|
@ -191,6 +199,7 @@ std::vector<uint64_t> segments_of(const Key & key, const std::vector<InputT> & k
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
int64_t math_lift(T value) noexcept
|
||||
{
|
||||
if constexpr (std::is_signed_v<T>)
|
||||
|
|
@ -200,6 +209,7 @@ int64_t math_lift(T value) noexcept
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
T domain_min() noexcept
|
||||
{
|
||||
if constexpr (std::is_signed_v<T>)
|
||||
|
|
@ -211,6 +221,7 @@ T domain_min() noexcept
|
|||
/// Public center-space cut where `center + eta` crosses the domain end.
|
||||
/// Empty when that cut is outside the domain, including `eta == 0`.
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
std::optional<T> carry_threshold(T eta) noexcept
|
||||
{
|
||||
constexpr unsigned bits = dpf::utils::bitlength_of_v<T>;
|
||||
|
|
@ -237,6 +248,7 @@ std::optional<T> carry_threshold(T eta) noexcept
|
|||
|
||||
/// `center + kappa` is the wrapped representative, as a mathematical integer.
|
||||
template <typename T>
|
||||
HEDLEY_NO_THROW
|
||||
int64_t kappa_for(T left, T eta) noexcept
|
||||
{
|
||||
constexpr unsigned bits = dpf::utils::bitlength_of_v<T>;
|
||||
|
|
@ -528,7 +540,7 @@ namespace offset_horner_detail
|
|||
|
||||
template <std::size_t Degree, typename InputT, typename Rng>
|
||||
geneval_offset_horner_result<Degree, InputT> geneval_at(
|
||||
InputT center0, InputT center1, InputT center, InputT eta,
|
||||
bool arith, InputT center0, InputT center1, InputT center, InputT eta,
|
||||
const std::vector<InputT> & knots,
|
||||
const std::vector<std::array<uint64_t, Degree + 1>> & coeff,
|
||||
Rng rng)
|
||||
|
|
@ -553,8 +565,11 @@ geneval_offset_horner_result<Degree, InputT> geneval_at(
|
|||
std::array<std::vector<uint64_t>, Degree + 1> seg1;
|
||||
for (std::size_t m = 0; m <= Degree; ++m)
|
||||
{
|
||||
const auto opened = dpf::geneval_cmp(center0, center1,
|
||||
shifted.begin(), shifted.end(), rng, payload[m]);
|
||||
const auto opened = arith
|
||||
? dpf::geneval_cmp(dpf::arith_input, center0, center1,
|
||||
shifted.begin(), shifted.end(), rng, payload[m])
|
||||
: dpf::geneval_cmp(center0, center1,
|
||||
shifted.begin(), shifted.end(), rng, payload[m]);
|
||||
const uint64_t blind = dpf::uniform_sample<uint64_t>();
|
||||
wrap[m][0] = blind;
|
||||
wrap[m][1] = payload[m] - blind;
|
||||
|
|
@ -574,6 +589,17 @@ geneval_offset_horner_result<Degree, InputT> geneval_at(
|
|||
return out;
|
||||
}
|
||||
|
||||
template <std::size_t Degree, typename InputT, typename Rng>
|
||||
geneval_offset_horner_result<Degree, InputT> geneval_at(
|
||||
InputT center0, InputT center1, InputT center, InputT eta,
|
||||
const std::vector<InputT> & knots,
|
||||
const std::vector<std::array<uint64_t, Degree + 1>> & coeff,
|
||||
Rng rng)
|
||||
{
|
||||
return geneval_at<Degree>(false, center0, center1, center, eta, knots, coeff,
|
||||
std::move(rng));
|
||||
}
|
||||
|
||||
} // namespace offset_horner_detail
|
||||
|
||||
/// Geneval-style offset Horner. The center is XOR-shared as in `geneval_point`.
|
||||
|
|
@ -609,10 +635,25 @@ geneval_offset_horner_result<Degree, InputT> geneval_offset_horner(
|
|||
center0, center1, eta, knots, coeff, std::move(rng));
|
||||
}
|
||||
|
||||
/// Additive shares of the center: `center0 + center1` is the comparison point.
|
||||
template <std::size_t Degree = offset_horner_max_degree,
|
||||
typename InputT,
|
||||
typename Rng>
|
||||
geneval_offset_horner_result<Degree, InputT> geneval_offset_horner(
|
||||
dpf::arith_input_t, InputT center0, InputT center1, InputT eta,
|
||||
const std::vector<InputT> & knots,
|
||||
const std::vector<std::array<uint64_t, Degree + 1>> & coeff,
|
||||
Rng rng)
|
||||
{
|
||||
const InputT center = offset_horner_group_add(center0, center1);
|
||||
return offset_horner_detail::geneval_at<Degree>(
|
||||
true, center0, center1, center, eta, knots, coeff, std::move(rng));
|
||||
}
|
||||
|
||||
/// Additive shares of the input `x` and the mask `r`. Reconstructs
|
||||
/// `eta = x - r` and `center = 2r`, XOR-shares that center as `(center, 0)`,
|
||||
/// and returns both parties' Horner shares of the cubic at `x + r`
|
||||
/// (the group element `x + r`).
|
||||
/// `eta = x - r` and passes additive shares of `center = 2r` (`2·r0`, `2·r1`)
|
||||
/// to arithmetic `geneval_cmp`. Returns both parties' Horner shares of the
|
||||
/// cubic at `x + r` (the group element `x + r`).
|
||||
template <std::size_t Degree = offset_horner_max_degree,
|
||||
typename InputT,
|
||||
typename Rng>
|
||||
|
|
@ -625,10 +666,11 @@ geneval_offset_horner_result<Degree, InputT> geneval_offset_horner(
|
|||
const InputT x = offset_horner_group_add(x0, x1);
|
||||
const InputT r = offset_horner_group_add(r0, r1);
|
||||
const InputT eta = offset_horner_group_sub(x, r);
|
||||
const InputT center = offset_horner_group_add(r, r);
|
||||
InputT zero{};
|
||||
const InputT center0 = offset_horner_group_add(r0, r0);
|
||||
const InputT center1 = offset_horner_group_add(r1, r1);
|
||||
const InputT center = offset_horner_group_add(center0, center1);
|
||||
return offset_horner_detail::geneval_at<Degree>(
|
||||
center, zero, center, eta, knots, coeff, std::move(rng));
|
||||
true, center0, center1, center, eta, knots, coeff, std::move(rng));
|
||||
}
|
||||
|
||||
template <std::size_t Degree = offset_horner_max_degree, typename InputT>
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ struct offset_iterator_base
|
|||
std::add_const_t<reference>>;
|
||||
using size_type = std::size_t;
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
constexpr offset_iterator_base(wrapped_iterator iter, size_type offset) noexcept
|
||||
: it{iter}, offset_{offset} { }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
/// @file grotto/piecewise.hpp
|
||||
/// @brief Horner evaluation of a cubic and a bound-selected piece.
|
||||
/// @details `eval_horner` evaluates one polynomial. `piecewise_eval` selects
|
||||
/// the piece whose upper bound is the first entry of `bounds`
|
||||
/// strictly greater than `x`.
|
||||
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
|
||||
/// @brief
|
||||
/// @details
|
||||
/// @copyright Copyright (c) 2019-2023 Ryan Henry and others
|
||||
/// @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 GPLv2) for details.
|
||||
/// see [LICENSE.md](@ref license) for details.
|
||||
|
||||
#ifndef LIBDPF_INCLUDE_GROTTO_PIECEWISE_HPP__
|
||||
#define LIBDPF_INCLUDE_GROTTO_PIECEWISE_HPP__
|
||||
|
|
@ -13,6 +15,8 @@
|
|||
#include <iterator>
|
||||
#include <algorithm>
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
namespace grotto
|
||||
{
|
||||
|
||||
|
|
@ -25,16 +29,26 @@ template <typename T> using poly_quadratic = std::array<T, 3>;
|
|||
template <typename T> using poly_cubic = std::array<T, 4>;
|
||||
|
||||
template <typename T>
|
||||
constexpr auto eval_horner(const poly_constant<T> & f, T x) { return f[0]; }
|
||||
HEDLEY_PURE
|
||||
HEDLEY_NO_THROW
|
||||
constexpr auto eval_horner(const poly_constant<T> & f, T x) noexcept { return f[0]; }
|
||||
template <typename T>
|
||||
constexpr auto eval_horner(const poly_linear<T> & f, T x) { return f[1] * x + f[0]; }
|
||||
HEDLEY_PURE
|
||||
HEDLEY_NO_THROW
|
||||
constexpr auto eval_horner(const poly_linear<T> & f, T x) noexcept { return f[1] * x + f[0]; }
|
||||
template <typename T>
|
||||
constexpr auto eval_horner(const poly_quadratic<T> & f, T x) { return (f[2] * x + f[1]) * x + f[0]; }
|
||||
HEDLEY_PURE
|
||||
HEDLEY_NO_THROW
|
||||
constexpr auto eval_horner(const poly_quadratic<T> & f, T x) noexcept { return (f[2] * x + f[1]) * x + f[0]; }
|
||||
template <typename T>
|
||||
constexpr auto eval_horner(const poly_cubic<T> & f, T x) { return ((f[3] * x + f[2]) * x + f[1]) * x + f[0]; }
|
||||
HEDLEY_PURE
|
||||
HEDLEY_NO_THROW
|
||||
constexpr auto eval_horner(const poly_cubic<T> & f, T x) noexcept { return ((f[3] * x + f[2]) * x + f[1]) * x + f[0]; }
|
||||
|
||||
template <typename T, std::size_t D, std::size_t N1, std::size_t N2>
|
||||
auto piecewise_eval(const std::array<std::array<T, D>, N1> & polys, const std::array<T, N2> & bounds, T x)
|
||||
HEDLEY_PURE
|
||||
HEDLEY_NO_THROW
|
||||
auto piecewise_eval(const std::array<std::array<T, D>, N1> & polys, const std::array<T, N2> & bounds, T x) noexcept
|
||||
{
|
||||
auto it = std::upper_bound(std::cbegin(bounds), std::cend(bounds), x,
|
||||
[](const T & lhs, const T & rhs){ return lhs < rhs; });
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
#include "dpf/twiddle.hpp"
|
||||
#include "dpf/leaf_node.hpp"
|
||||
#include "dpf/dcf.hpp"
|
||||
#include "dpf/blocked_dcf.hpp"
|
||||
#include "grotto/offset_iterable.hpp"
|
||||
#include "dpf/path_memoizer.hpp"
|
||||
#include "dpf/utils.hpp"
|
||||
|
|
@ -29,6 +30,7 @@ namespace grotto
|
|||
|
||||
template <typename NodeT,
|
||||
typename InputT>
|
||||
HEDLEY_NO_THROW
|
||||
auto parity_of_substring_prefix(const NodeT & node, InputT x) noexcept
|
||||
{
|
||||
static constexpr auto bits_per_limb = dpf::utils::bitlength_of_v<decltype(node[0])>;
|
||||
|
|
@ -211,6 +213,7 @@ struct key_has_cmp<T, std::void_t<decltype(std::declval<const T &>().has_cmp())>
|
|||
: std::true_type {};
|
||||
|
||||
template <typename KeyT>
|
||||
HEDLEY_NO_THROW
|
||||
uint64_t cmp_addend_raw(const KeyT & key) noexcept
|
||||
{
|
||||
if constexpr (dpf::is_party_key_v<KeyT>)
|
||||
|
|
@ -263,7 +266,12 @@ static auto signed_prefix_parities(const DpfKey & dpf,
|
|||
constexpr std::size_t depth = key_type::depth;
|
||||
const auto & ch = dpf.cmp();
|
||||
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
|
||||
if (nbits > depth)
|
||||
if constexpr (key_type::cmp_block > 0)
|
||||
{
|
||||
if (key_type::cmp_h > depth)
|
||||
throw std::invalid_argument("signed_prefix_parities: comparison is deeper than the key");
|
||||
}
|
||||
else if (nbits > depth)
|
||||
throw std::invalid_argument("signed_prefix_parities: comparison is deeper than the key");
|
||||
|
||||
const uint64_t mask = ch.mask;
|
||||
|
|
@ -284,6 +292,11 @@ static auto signed_prefix_parities(const DpfKey & dpf,
|
|||
prefixes[which] = addend & mask;
|
||||
continue;
|
||||
}
|
||||
if constexpr (key_type::cmp_block > 0)
|
||||
{
|
||||
prefixes[which] = dpf::detail::blocked::eval_share(dpf, tx, path);
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::size_t resume = dpf::detail::path_resume_for_level(
|
||||
path, dpf, tx, nbits);
|
||||
|
|
@ -363,7 +376,12 @@ static void signed_prefix_parities_into(const DpfKey & dpf,
|
|||
constexpr std::size_t depth = key_type::depth;
|
||||
const auto & ch = dpf.cmp();
|
||||
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
|
||||
if (nbits > depth)
|
||||
if constexpr (key_type::cmp_block > 0)
|
||||
{
|
||||
if (key_type::cmp_h > depth)
|
||||
throw std::invalid_argument("signed_prefix_parities: comparison is deeper than the key");
|
||||
}
|
||||
else if (nbits > depth)
|
||||
throw std::invalid_argument("signed_prefix_parities: comparison is deeper than the key");
|
||||
|
||||
const uint64_t mask = ch.mask;
|
||||
|
|
@ -382,6 +400,11 @@ static void signed_prefix_parities_into(const DpfKey & dpf,
|
|||
out[which] = addend & mask;
|
||||
continue;
|
||||
}
|
||||
if constexpr (key_type::cmp_block > 0)
|
||||
{
|
||||
out[which] = dpf::detail::blocked::eval_share(dpf, tx, path);
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::size_t resume = dpf::detail::path_resume_for_level(
|
||||
path, dpf, tx, nbits);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
#ifndef LIBDPF_INCLUDE_GROTTO_PRINCIPAL_LUT_HPP__
|
||||
#define LIBDPF_INCLUDE_GROTTO_PRINCIPAL_LUT_HPP__
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
|
|
@ -44,6 +46,7 @@ enum class principal : unsigned
|
|||
|
||||
inline constexpr unsigned principal_precisions[] = {8u, 12u, 16u, 20u, 24u, 28u, 32u};
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
inline constexpr bool principal_precision(unsigned fractional_bits) noexcept
|
||||
{
|
||||
for (unsigned k : principal_precisions)
|
||||
|
|
@ -150,6 +153,7 @@ inline w256 w_mul_u64(w256 value, std::uint64_t factor)
|
|||
return out;
|
||||
}
|
||||
|
||||
HEDLEY_NO_THROW
|
||||
inline bool w_negative(w256 value) noexcept
|
||||
{
|
||||
return value.hi < 0;
|
||||
|
|
@ -311,7 +315,10 @@ inline w256 w_mul_i64(w256 value, std::int64_t factor)
|
|||
{
|
||||
if (factor >= 0)
|
||||
return w_mul_u64(value, static_cast<std::uint64_t>(factor));
|
||||
return w_neg(w_mul_u64(value, static_cast<std::uint64_t>(-factor)));
|
||||
// `-factor` is undefined at INT64_MIN. The magnitude is the unsigned wrap.
|
||||
const auto mag = static_cast<std::uint64_t>(0)
|
||||
- static_cast<std::uint64_t>(factor);
|
||||
return w_neg(w_mul_u64(value, mag));
|
||||
}
|
||||
|
||||
inline std::int64_t horner(const cubic_bits & piece, unsigned q, std::int64_t raw, unsigned fractional_bits)
|
||||
|
|
|
|||
676
include/grotto/range_lut.hpp
Normal file
676
include/grotto/range_lut.hpp
Normal file
|
|
@ -0,0 +1,676 @@
|
|||
/// @file grotto/range_lut.hpp
|
||||
/// @brief Full-domain maps built from the principal-domain cubics.
|
||||
/// @details Each reduced map is one of the elementary range reductions, and
|
||||
/// the polynomial it evaluates is the matching principal table:
|
||||
/// `ln` / `lg` / `log10` share the mantissa logarithm;
|
||||
/// `exp` / `exp2` / `exp10` share the `2^{-13}` exponential;
|
||||
/// `sin` / `cos` share the quarter-turn sine;
|
||||
/// `tan` / `cot` share `tanf` and `tang`;
|
||||
/// `sec` / `csc` share `sec` and `gsec`;
|
||||
/// `sinh` / `cosh` / `tanh` / `sech` share the hyperbolic addition;
|
||||
/// `coth` and `csch` use their principal small-argument tables;
|
||||
/// `sqrt` / `inv` / `rsqrt` / `invsq` are dyadic lifts of `[1/2, 1]`.
|
||||
/// @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_GROTTO_RANGE_LUT_HPP__
|
||||
#define LIBDPF_INCLUDE_GROTTO_RANGE_LUT_HPP__
|
||||
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "hedley/hedley.h"
|
||||
|
||||
#include "grotto/principal_lut.hpp"
|
||||
|
||||
namespace grotto
|
||||
{
|
||||
|
||||
enum class reduced : unsigned
|
||||
{
|
||||
ln = 0,
|
||||
lg,
|
||||
log10,
|
||||
exp,
|
||||
exp2,
|
||||
exp10,
|
||||
sin,
|
||||
cos,
|
||||
tan,
|
||||
cot,
|
||||
sec,
|
||||
csc,
|
||||
sinh,
|
||||
cosh,
|
||||
tanh,
|
||||
coth,
|
||||
sech,
|
||||
csch,
|
||||
sqrt,
|
||||
inv,
|
||||
rsqrt,
|
||||
invsq,
|
||||
};
|
||||
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
inline std::int64_t eval_reduced(reduced which, unsigned fractional_bits, std::int64_t raw);
|
||||
|
||||
namespace range_detail
|
||||
{
|
||||
|
||||
using u128 = unsigned __int128;
|
||||
|
||||
constexpr u128 words(std::uint64_t hi, std::uint64_t lo)
|
||||
{
|
||||
return (u128{hi} << 64) | lo;
|
||||
}
|
||||
|
||||
/// `value * 2^64`, rounded half away from zero. Values above `2^64` keep the
|
||||
/// high limb so the constant is not truncated.
|
||||
inline constexpr u128 ln2_64 = words(0, 12786308645202655660ULL);
|
||||
inline constexpr u128 inv_ln2_64 = words(1, 8166282121979093367ULL);
|
||||
inline constexpr u128 log10_2_64 = words(0, 5553023288523357132ULL);
|
||||
inline constexpr u128 ln10_64 = words(2, 5581709770980765788ULL);
|
||||
inline constexpr u128 inv_ln10_64 = words(0, 8011319160293570763ULL);
|
||||
inline constexpr u128 sqrt2_64 = words(1, 7640891576956012809ULL);
|
||||
inline constexpr u128 rsqrt2_64 = words(0, 13043817825332782212ULL);
|
||||
inline constexpr u128 two_over_pi_64 = words(0, 11743562013128004906ULL);
|
||||
inline constexpr u128 four_over_pi_64 = words(1, 5040379952546458196ULL);
|
||||
inline constexpr u128 pi_over_4_64 = words(0, 14488038916154245685ULL);
|
||||
|
||||
/// `exp(2^{i-13}) * 2^64`.
|
||||
inline constexpr u128 exp_chunk_64[13] = {
|
||||
words(1, 2251937258231296ULL),
|
||||
words(1, 4504149427926357ULL),
|
||||
words(1, 9009398635954180ULL),
|
||||
words(1, 18023197466514910ULL),
|
||||
words(1, 36064004308734226ULL),
|
||||
words(1, 72198514957318099ULL),
|
||||
words(1, 144679606912572172ULL),
|
||||
words(1, 290493950045950331ULL),
|
||||
words(1, 585562514163419534ULL),
|
||||
words(1, 1189712777830127574ULL),
|
||||
words(1, 2456155437534072733ULL),
|
||||
words(1, 5239344172067481206ULL),
|
||||
words(1, 11966795255776918679ULL),
|
||||
};
|
||||
|
||||
inline std::int64_t round_mag(u128 mag, unsigned shift, bool neg)
|
||||
{
|
||||
if (shift >= 128)
|
||||
return 0;
|
||||
if (shift > 0)
|
||||
{
|
||||
mag += u128{1} << (shift - 1);
|
||||
mag >>= shift;
|
||||
}
|
||||
if (mag > static_cast<u128>(INT64_MAX))
|
||||
throw std::overflow_error("range lut: value does not fit int64");
|
||||
const auto out = static_cast<std::int64_t>(mag);
|
||||
return neg ? -out : out;
|
||||
}
|
||||
|
||||
inline std::int64_t round_i128(__int128 value, unsigned shift)
|
||||
{
|
||||
const bool neg = value < 0;
|
||||
const auto mag = static_cast<u128>(neg ? -value : value);
|
||||
return round_mag(mag, shift, neg);
|
||||
}
|
||||
|
||||
inline std::int64_t scale_unit(u128 mag64, unsigned fractional_bits)
|
||||
{
|
||||
return round_mag(mag64, 64u - fractional_bits, false);
|
||||
}
|
||||
|
||||
inline std::int64_t mul_raw(std::int64_t lhs, std::int64_t rhs, unsigned fractional_bits)
|
||||
{
|
||||
return round_i128(static_cast<__int128>(lhs) * rhs, fractional_bits);
|
||||
}
|
||||
|
||||
inline std::int64_t div_raw(std::int64_t num, std::int64_t den, unsigned fractional_bits)
|
||||
{
|
||||
if (den == 0)
|
||||
throw std::domain_error("range lut: division by zero");
|
||||
const bool neg = (num < 0) != (den < 0);
|
||||
auto n = static_cast<u128>(num < 0 ? -static_cast<__int128>(num) : num);
|
||||
auto d = static_cast<u128>(den < 0 ? -static_cast<__int128>(den) : den);
|
||||
n <<= fractional_bits;
|
||||
const u128 quot = (n + d / 2) / d;
|
||||
return round_mag(quot, 0, neg);
|
||||
}
|
||||
|
||||
inline std::int64_t shift_pow2(std::int64_t value, int places)
|
||||
{
|
||||
if (places == 0 || value == 0)
|
||||
return value;
|
||||
if (places > 0)
|
||||
{
|
||||
if (places >= 62)
|
||||
throw std::overflow_error("range lut: exponent overflow");
|
||||
const __int128 wide = static_cast<__int128>(value) << places;
|
||||
if (wide > INT64_MAX || wide < INT64_MIN)
|
||||
throw std::overflow_error("range lut: exponent overflow");
|
||||
return static_cast<std::int64_t>(wide);
|
||||
}
|
||||
return round_i128(value, static_cast<unsigned>(-places));
|
||||
}
|
||||
|
||||
HEDLEY_CONST
|
||||
HEDLEY_NO_THROW
|
||||
constexpr std::int64_t one_raw(unsigned fractional_bits) noexcept
|
||||
{
|
||||
return std::int64_t{1} << fractional_bits;
|
||||
}
|
||||
|
||||
HEDLEY_CONST
|
||||
HEDLEY_NO_THROW
|
||||
constexpr u128 magnitude_of(std::int64_t raw) noexcept
|
||||
{
|
||||
if (raw >= 0)
|
||||
return static_cast<u128>(raw);
|
||||
return static_cast<u128>(-static_cast<__int128>(raw));
|
||||
}
|
||||
|
||||
inline std::int64_t abs_raw(std::int64_t raw)
|
||||
{
|
||||
const u128 mag = magnitude_of(raw);
|
||||
if (mag > static_cast<u128>(INT64_MAX))
|
||||
throw std::overflow_error("range lut: magnitude does not fit int64");
|
||||
return static_cast<std::int64_t>(mag);
|
||||
}
|
||||
|
||||
struct dyadic
|
||||
{
|
||||
std::int64_t mantissa_raw;
|
||||
int power;
|
||||
};
|
||||
|
||||
inline dyadic split_positive(std::int64_t raw, unsigned fractional_bits)
|
||||
{
|
||||
if (raw <= 0)
|
||||
throw std::domain_error("range lut: reduction requires a positive input");
|
||||
const auto mag = static_cast<unsigned long long>(raw);
|
||||
const int floor_log = 63 - __builtin_clzll(mag);
|
||||
const int shift = static_cast<int>(fractional_bits) - floor_log - 1;
|
||||
std::int64_t mantissa = shift >= 0
|
||||
? raw << shift
|
||||
: round_i128(raw, static_cast<unsigned>(-shift));
|
||||
int power = floor_log + 1 - static_cast<int>(fractional_bits);
|
||||
const std::int64_t one = one_raw(fractional_bits);
|
||||
const std::int64_t half = one >> 1;
|
||||
if (mantissa >= one)
|
||||
{
|
||||
mantissa >>= 1;
|
||||
++power;
|
||||
}
|
||||
if (mantissa < half)
|
||||
mantissa = half;
|
||||
return dyadic{mantissa, power};
|
||||
}
|
||||
|
||||
inline std::int64_t ln2_raw(unsigned fractional_bits)
|
||||
{
|
||||
return scale_unit(ln2_64, fractional_bits);
|
||||
}
|
||||
|
||||
inline std::int64_t eval_ln_positive(unsigned fractional_bits, std::int64_t raw)
|
||||
{
|
||||
const dyadic part = split_positive(raw, fractional_bits);
|
||||
const std::int64_t ln_m = eval_principal(principal::ln, fractional_bits, part.mantissa_raw);
|
||||
return ln_m + static_cast<std::int64_t>(part.power) * ln2_raw(fractional_bits);
|
||||
}
|
||||
|
||||
inline std::int64_t eval_exp_at_scale(unsigned fractional_bits, std::int64_t raw)
|
||||
{
|
||||
if (fractional_bits < 13)
|
||||
{
|
||||
const int lift = static_cast<int>(16u - fractional_bits);
|
||||
const __int128 lifted_arg = static_cast<__int128>(raw) << lift;
|
||||
if (lifted_arg > INT64_MAX || lifted_arg < INT64_MIN)
|
||||
throw std::overflow_error("range lut: exponent overflow");
|
||||
const std::int64_t lifted = eval_exp_at_scale(16, static_cast<std::int64_t>(lifted_arg));
|
||||
return round_i128(lifted, static_cast<unsigned>(lift));
|
||||
}
|
||||
const std::int64_t ln2 = ln2_raw(fractional_bits);
|
||||
if (ln2 <= 0)
|
||||
throw std::logic_error("range lut: ln 2 constant");
|
||||
std::int64_t n_bin = raw / ln2;
|
||||
std::int64_t remainder = raw - n_bin * ln2;
|
||||
if (remainder < 0)
|
||||
{
|
||||
remainder += ln2;
|
||||
--n_bin;
|
||||
}
|
||||
while (remainder >= ln2)
|
||||
{
|
||||
remainder -= ln2;
|
||||
++n_bin;
|
||||
}
|
||||
|
||||
const std::int64_t step = std::int64_t{1} << (fractional_bits - 13);
|
||||
const std::int64_t chunks = remainder / step;
|
||||
const std::int64_t tiny = remainder - chunks * step;
|
||||
std::int64_t table_raw = tiny << 13;
|
||||
const std::int64_t one = one_raw(fractional_bits);
|
||||
if (table_raw > one)
|
||||
table_raw = one;
|
||||
std::int64_t exp_s = eval_principal(principal::exp, fractional_bits, table_raw);
|
||||
for (unsigned bit = 0; bit < 13; ++bit)
|
||||
{
|
||||
if ((static_cast<unsigned long long>(chunks) & (1ull << bit)) == 0)
|
||||
continue;
|
||||
// `chunk` is `exp(2^{i-13}) * 2^64`, so the product's high limb is the raw product.
|
||||
const u128 prod = static_cast<u128>(exp_s) * exp_chunk_64[bit];
|
||||
exp_s = round_mag(prod, 64, false);
|
||||
}
|
||||
return shift_pow2(exp_s, static_cast<int>(n_bin));
|
||||
}
|
||||
|
||||
inline std::int64_t fractional_raw(std::int64_t raw, unsigned fractional_bits, std::int64_t & whole)
|
||||
{
|
||||
const std::int64_t one = one_raw(fractional_bits);
|
||||
std::int64_t q = raw / one;
|
||||
std::int64_t f = raw - q * one;
|
||||
if (f < 0)
|
||||
{
|
||||
f += one;
|
||||
--q;
|
||||
}
|
||||
whole = q;
|
||||
return f;
|
||||
}
|
||||
|
||||
inline std::int64_t pow10_raw(int exponent, unsigned fractional_bits)
|
||||
{
|
||||
const std::int64_t one = one_raw(fractional_bits);
|
||||
if (exponent == 0)
|
||||
return one;
|
||||
if (exponent < 0)
|
||||
return div_raw(one, pow10_raw(-exponent, fractional_bits), fractional_bits);
|
||||
u128 acc = static_cast<u128>(one);
|
||||
for (int i = 0; i < exponent; ++i)
|
||||
{
|
||||
if (acc > static_cast<u128>(INT64_MAX) / 10)
|
||||
throw std::overflow_error("range lut: exponent overflow");
|
||||
acc *= 10;
|
||||
}
|
||||
return static_cast<std::int64_t>(acc);
|
||||
}
|
||||
|
||||
struct angle
|
||||
{
|
||||
unsigned index;
|
||||
std::int64_t frac_raw;
|
||||
};
|
||||
|
||||
/// `{ |x| * multiplier }` at this precision, with the integer part reduced
|
||||
/// only as far as the low bits the quadrant logic reads.
|
||||
inline angle reduce_positive(unsigned fractional_bits, std::int64_t raw, u128 multiplier_64)
|
||||
{
|
||||
const u128 scaled = magnitude_of(raw) * multiplier_64;
|
||||
const u128 rounded = (scaled + (u128{1} << 63)) >> 64;
|
||||
const u128 one = u128{1} << fractional_bits;
|
||||
return angle{
|
||||
static_cast<unsigned>(rounded >> fractional_bits),
|
||||
static_cast<std::int64_t>(rounded & (one - 1)),
|
||||
};
|
||||
}
|
||||
|
||||
inline std::int64_t principal_sin_fraction(unsigned fractional_bits, std::int64_t fraction_raw, bool complement)
|
||||
{
|
||||
const std::int64_t one = one_raw(fractional_bits);
|
||||
std::int64_t argument = complement ? one - fraction_raw : fraction_raw;
|
||||
if (argument < 0)
|
||||
argument = 0;
|
||||
if (argument > one)
|
||||
argument = one;
|
||||
return eval_principal(principal::sin, fractional_bits, argument);
|
||||
}
|
||||
|
||||
inline std::int64_t sin_from_angle(unsigned fractional_bits, const angle & turned, int sign)
|
||||
{
|
||||
const unsigned which = turned.index & 3u;
|
||||
const bool complement = which == 1 || which == 3;
|
||||
const int quadrant_sign = (which == 2 || which == 3) ? -1 : 1;
|
||||
const std::int64_t magnitude = principal_sin_fraction(
|
||||
fractional_bits, turned.frac_raw, complement);
|
||||
return magnitude * quadrant_sign * sign;
|
||||
}
|
||||
|
||||
inline std::int64_t cos_from_angle(unsigned fractional_bits, const angle & turned)
|
||||
{
|
||||
angle shifted = turned;
|
||||
shifted.index += 1;
|
||||
return sin_from_angle(fractional_bits, shifted, 1);
|
||||
}
|
||||
|
||||
inline std::int64_t pi_over_4_raw(unsigned fractional_bits)
|
||||
{
|
||||
return scale_unit(pi_over_4_64, fractional_bits);
|
||||
}
|
||||
|
||||
inline std::int64_t tan_positive(unsigned fractional_bits, std::int64_t magnitude, int quarter_shift)
|
||||
{
|
||||
const angle turned = reduce_positive(fractional_bits, magnitude, four_over_pi_64);
|
||||
const unsigned q = (turned.index + static_cast<unsigned>(quarter_shift)) & 3u;
|
||||
const std::int64_t one = one_raw(fractional_bits);
|
||||
std::int64_t t = (q == 0 || q == 2) ? turned.frac_raw : one - turned.frac_raw;
|
||||
if (t < 0)
|
||||
t = 0;
|
||||
if (t > one)
|
||||
t = one;
|
||||
const std::int64_t z = mul_raw(t, pi_over_4_raw(fractional_bits), fractional_bits);
|
||||
if (q == 0 || q == 3)
|
||||
{
|
||||
const std::int64_t tanf = eval_principal(principal::tanf, fractional_bits, t);
|
||||
const std::int64_t y = mul_raw(z, tanf, fractional_bits);
|
||||
return q == 3 ? -y : y;
|
||||
}
|
||||
if (z == 0)
|
||||
throw std::domain_error("range lut: tan pole");
|
||||
const std::int64_t tang = eval_principal(principal::tang, fractional_bits, t);
|
||||
const std::int64_t y = div_raw(one, z, fractional_bits) + tang;
|
||||
return q == 2 ? -y : y;
|
||||
}
|
||||
|
||||
inline std::int64_t sec_positive(unsigned fractional_bits, std::int64_t magnitude, int octant_shift)
|
||||
{
|
||||
const angle turned = reduce_positive(fractional_bits, magnitude, four_over_pi_64);
|
||||
const unsigned q8 = (turned.index + static_cast<unsigned>(octant_shift)) & 7u;
|
||||
const unsigned q = q8 & 3u;
|
||||
const int sigma = (q8 & 4u) == 0 ? 1 : -1;
|
||||
const std::int64_t one = one_raw(fractional_bits);
|
||||
std::int64_t t = (q == 0 || q == 2) ? turned.frac_raw : one - turned.frac_raw;
|
||||
if (t < 0)
|
||||
t = 0;
|
||||
if (t > one)
|
||||
t = one;
|
||||
if (q == 0 || q == 3)
|
||||
{
|
||||
const std::int64_t sec = eval_principal(principal::sec, fractional_bits, t);
|
||||
const int sign = (q == 3 ? -1 : 1) * sigma;
|
||||
return sec * sign;
|
||||
}
|
||||
const std::int64_t z = mul_raw(t, pi_over_4_raw(fractional_bits), fractional_bits);
|
||||
if (z == 0)
|
||||
throw std::domain_error("range lut: sec pole");
|
||||
const std::int64_t gsec = eval_principal(principal::gsec, fractional_bits, t);
|
||||
std::int64_t y = div_raw(one, z, fractional_bits) + gsec;
|
||||
if (q == 2)
|
||||
y = -y;
|
||||
return y * sigma;
|
||||
}
|
||||
|
||||
inline void quotient_2_13(unsigned fractional_bits, std::int64_t magnitude,
|
||||
std::int64_t & quotient, std::int64_t & remainder)
|
||||
{
|
||||
if (fractional_bits >= 13)
|
||||
{
|
||||
const unsigned shift = fractional_bits - 13;
|
||||
quotient = magnitude >> shift;
|
||||
const std::int64_t mask = shift >= 63 ? INT64_MAX : (std::int64_t{1} << shift) - 1;
|
||||
remainder = shift == 0 ? 0 : magnitude & mask;
|
||||
return;
|
||||
}
|
||||
const int lift = static_cast<int>(13u - fractional_bits);
|
||||
const __int128 wide = static_cast<__int128>(magnitude) << lift;
|
||||
if (wide > INT64_MAX)
|
||||
throw std::overflow_error("range lut: exponent overflow");
|
||||
quotient = static_cast<std::int64_t>(wide);
|
||||
remainder = 0;
|
||||
}
|
||||
|
||||
inline std::int64_t exp_of_quotient(unsigned fractional_bits, std::int64_t quotient, std::int64_t magnitude)
|
||||
{
|
||||
if (quotient == 0)
|
||||
return one_raw(fractional_bits);
|
||||
__int128 argument;
|
||||
if (fractional_bits >= 13)
|
||||
argument = static_cast<__int128>(quotient) << (fractional_bits - 13);
|
||||
else
|
||||
argument = magnitude;
|
||||
if (argument > INT64_MAX)
|
||||
throw std::overflow_error("range lut: exponent overflow");
|
||||
return eval_exp_at_scale(fractional_bits, static_cast<std::int64_t>(argument));
|
||||
}
|
||||
|
||||
struct hyp
|
||||
{
|
||||
std::int64_t sinh_raw;
|
||||
std::int64_t cosh_raw;
|
||||
};
|
||||
|
||||
inline hyp sinh_cosh(unsigned fractional_bits, std::int64_t raw)
|
||||
{
|
||||
const bool neg = raw < 0;
|
||||
const auto mag_wide = magnitude_of(raw);
|
||||
if (mag_wide > static_cast<u128>(INT64_MAX))
|
||||
throw std::overflow_error("range lut: exponent overflow");
|
||||
const std::int64_t mag = static_cast<std::int64_t>(mag_wide);
|
||||
std::int64_t quotient = 0;
|
||||
std::int64_t remainder = 0;
|
||||
quotient_2_13(fractional_bits, mag, quotient, remainder);
|
||||
|
||||
const std::int64_t one = one_raw(fractional_bits);
|
||||
std::int64_t table = 0;
|
||||
if (fractional_bits >= 13 && remainder != 0)
|
||||
{
|
||||
const __int128 lifted = static_cast<__int128>(remainder) << 13;
|
||||
table = lifted > one ? one : static_cast<std::int64_t>(lifted);
|
||||
}
|
||||
const std::int64_t sr = eval_principal(principal::sinh, fractional_bits, table);
|
||||
const std::int64_t cr = eval_principal(principal::cosh, fractional_bits, table);
|
||||
std::int64_t sh = sr;
|
||||
std::int64_t ch = cr;
|
||||
if (quotient != 0)
|
||||
{
|
||||
const std::int64_t grown = exp_of_quotient(fractional_bits, quotient, mag);
|
||||
std::int64_t inv = 0;
|
||||
if (grown != 0)
|
||||
inv = div_raw(one, grown, fractional_bits);
|
||||
const std::int64_t sq = round_i128(static_cast<__int128>(grown) - inv, 1);
|
||||
const std::int64_t cq = round_i128(static_cast<__int128>(grown) + inv, 1);
|
||||
const __int128 sinh_sum = static_cast<__int128>(sq) * cr + static_cast<__int128>(cq) * sr;
|
||||
const __int128 cosh_sum = static_cast<__int128>(cq) * cr + static_cast<__int128>(sq) * sr;
|
||||
sh = round_i128(sinh_sum, fractional_bits);
|
||||
ch = round_i128(cosh_sum, fractional_bits);
|
||||
}
|
||||
if (neg)
|
||||
sh = -sh;
|
||||
return hyp{sh, ch};
|
||||
}
|
||||
|
||||
/// `ln(2^{k+1} ± 1) / 2`, the saturation threshold used by `tanh` and `coth`.
|
||||
inline std::int64_t beta_raw(unsigned fractional_bits, bool plus)
|
||||
{
|
||||
u128 ln = u128{fractional_bits + 1} * ln2_64;
|
||||
const u128 eps = u128{1} << (63u - fractional_bits);
|
||||
if (plus)
|
||||
ln += eps;
|
||||
else
|
||||
ln -= eps;
|
||||
return round_mag(ln, 65u - fractional_bits, false);
|
||||
}
|
||||
|
||||
inline int half_pow_of(int power)
|
||||
{
|
||||
return (power & 1) != 0 ? (power - 1) / 2 : power / 2;
|
||||
}
|
||||
|
||||
} // namespace range_detail
|
||||
|
||||
HEDLEY_WARN_UNUSED_RESULT
|
||||
inline std::int64_t eval_reduced(reduced which, unsigned fractional_bits, std::int64_t raw)
|
||||
{
|
||||
using namespace range_detail;
|
||||
if (!principal_precision(fractional_bits))
|
||||
throw std::invalid_argument("range lut: precision must be 8, 12, ..., 32");
|
||||
const std::int64_t one = one_raw(fractional_bits);
|
||||
switch (which)
|
||||
{
|
||||
case reduced::ln:
|
||||
return eval_ln_positive(fractional_bits, raw);
|
||||
case reduced::lg:
|
||||
{
|
||||
const dyadic part = split_positive(raw, fractional_bits);
|
||||
const std::int64_t ln_m = eval_principal(principal::ln, fractional_bits, part.mantissa_raw);
|
||||
const std::int64_t lg_m = mul_raw(
|
||||
ln_m, scale_unit(inv_ln2_64, fractional_bits), fractional_bits);
|
||||
return lg_m + (static_cast<std::int64_t>(part.power) << fractional_bits);
|
||||
}
|
||||
case reduced::log10:
|
||||
{
|
||||
const dyadic part = split_positive(raw, fractional_bits);
|
||||
const std::int64_t ln_m = eval_principal(principal::ln, fractional_bits, part.mantissa_raw);
|
||||
const std::int64_t mantissa = mul_raw(
|
||||
ln_m, scale_unit(inv_ln10_64, fractional_bits), fractional_bits);
|
||||
const std::int64_t lift = static_cast<std::int64_t>(part.power)
|
||||
* scale_unit(log10_2_64, fractional_bits);
|
||||
return mantissa + lift;
|
||||
}
|
||||
case reduced::exp:
|
||||
return eval_exp_at_scale(fractional_bits, raw);
|
||||
case reduced::exp2:
|
||||
{
|
||||
std::int64_t whole = 0;
|
||||
const std::int64_t frac = fractional_raw(raw, fractional_bits, whole);
|
||||
const std::int64_t natural = mul_raw(frac, ln2_raw(fractional_bits), fractional_bits);
|
||||
return shift_pow2(eval_exp_at_scale(fractional_bits, natural), static_cast<int>(whole));
|
||||
}
|
||||
case reduced::exp10:
|
||||
{
|
||||
std::int64_t whole = 0;
|
||||
const std::int64_t frac = fractional_raw(raw, fractional_bits, whole);
|
||||
const std::int64_t natural = mul_raw(
|
||||
frac, scale_unit(ln10_64, fractional_bits), fractional_bits);
|
||||
if (whole > 18 || whole < -18)
|
||||
throw std::overflow_error("range lut: exponent overflow");
|
||||
return mul_raw(
|
||||
eval_exp_at_scale(fractional_bits, natural),
|
||||
pow10_raw(static_cast<int>(whole), fractional_bits),
|
||||
fractional_bits);
|
||||
}
|
||||
case reduced::sin:
|
||||
return sin_from_angle(
|
||||
fractional_bits,
|
||||
reduce_positive(fractional_bits, raw, two_over_pi_64),
|
||||
raw < 0 ? -1 : 1);
|
||||
case reduced::cos:
|
||||
return cos_from_angle(
|
||||
fractional_bits, reduce_positive(fractional_bits, raw, two_over_pi_64));
|
||||
case reduced::tan:
|
||||
{
|
||||
const std::int64_t y = tan_positive(fractional_bits, abs_raw(raw), 0);
|
||||
return raw < 0 ? -y : y;
|
||||
}
|
||||
case reduced::cot:
|
||||
{
|
||||
if (raw == 0)
|
||||
throw std::domain_error("range lut: cot pole");
|
||||
const std::int64_t y = -tan_positive(fractional_bits, abs_raw(raw), 2);
|
||||
return raw < 0 ? -y : y;
|
||||
}
|
||||
case reduced::sec:
|
||||
return sec_positive(fractional_bits, abs_raw(raw), 0);
|
||||
case reduced::csc:
|
||||
{
|
||||
if (raw == 0)
|
||||
throw std::domain_error("range lut: csc pole");
|
||||
const std::int64_t y = sec_positive(fractional_bits, abs_raw(raw), -2);
|
||||
return raw < 0 ? -y : y;
|
||||
}
|
||||
case reduced::sinh:
|
||||
return sinh_cosh(fractional_bits, raw).sinh_raw;
|
||||
case reduced::cosh:
|
||||
return sinh_cosh(fractional_bits, raw).cosh_raw;
|
||||
case reduced::tanh:
|
||||
{
|
||||
if (raw == 0)
|
||||
return 0;
|
||||
const std::int64_t limit = beta_raw(fractional_bits, false);
|
||||
const std::int64_t mag = abs_raw(raw);
|
||||
if (mag >= limit)
|
||||
return raw < 0 ? -one : one;
|
||||
const hyp pair = sinh_cosh(fractional_bits, raw);
|
||||
return div_raw(pair.sinh_raw, pair.cosh_raw, fractional_bits);
|
||||
}
|
||||
case reduced::coth:
|
||||
{
|
||||
if (raw == 0)
|
||||
throw std::domain_error("range lut: coth pole");
|
||||
const std::int64_t limit = beta_raw(fractional_bits, true);
|
||||
const std::int64_t mag = abs_raw(raw);
|
||||
std::int64_t y;
|
||||
if (mag >= limit)
|
||||
y = one;
|
||||
else
|
||||
{
|
||||
const std::int64_t t = div_raw(mag, limit, fractional_bits);
|
||||
const std::int64_t argument = t > one ? one : t;
|
||||
const std::int64_t removed = eval_principal(principal::coth, fractional_bits, argument);
|
||||
y = removed + div_raw(one, mag, fractional_bits);
|
||||
}
|
||||
return raw < 0 ? -y : y;
|
||||
}
|
||||
case reduced::sech:
|
||||
{
|
||||
const std::int64_t ch = sinh_cosh(fractional_bits, raw).cosh_raw;
|
||||
return div_raw(one, ch, fractional_bits);
|
||||
}
|
||||
case reduced::csch:
|
||||
{
|
||||
if (raw == 0)
|
||||
throw std::domain_error("range lut: csch pole");
|
||||
const bool neg = raw < 0;
|
||||
const std::int64_t mag = abs_raw(raw);
|
||||
std::int64_t y;
|
||||
if (mag <= one)
|
||||
{
|
||||
const std::int64_t removed = eval_principal(principal::csch, fractional_bits, mag);
|
||||
y = removed + div_raw(one, mag, fractional_bits);
|
||||
}
|
||||
else
|
||||
{
|
||||
y = div_raw(one, sinh_cosh(fractional_bits, mag).sinh_raw, fractional_bits);
|
||||
}
|
||||
return neg ? -y : y;
|
||||
}
|
||||
case reduced::sqrt:
|
||||
{
|
||||
if (raw == 0)
|
||||
return 0;
|
||||
const dyadic part = split_positive(raw, fractional_bits);
|
||||
std::int64_t root = eval_principal(principal::sqrt, fractional_bits, part.mantissa_raw);
|
||||
if ((part.power & 1) != 0)
|
||||
root = mul_raw(root, scale_unit(sqrt2_64, fractional_bits), fractional_bits);
|
||||
return shift_pow2(root, half_pow_of(part.power));
|
||||
}
|
||||
case reduced::inv:
|
||||
{
|
||||
const dyadic part = split_positive(raw, fractional_bits);
|
||||
const std::int64_t reciprocal = eval_principal(
|
||||
principal::inv, fractional_bits, part.mantissa_raw);
|
||||
return shift_pow2(reciprocal, -part.power);
|
||||
}
|
||||
case reduced::rsqrt:
|
||||
{
|
||||
const dyadic part = split_positive(raw, fractional_bits);
|
||||
std::int64_t root = eval_principal(principal::rsqrt, fractional_bits, part.mantissa_raw);
|
||||
if ((part.power & 1) != 0)
|
||||
root = mul_raw(root, scale_unit(rsqrt2_64, fractional_bits), fractional_bits);
|
||||
return shift_pow2(root, -half_pow_of(part.power));
|
||||
}
|
||||
case reduced::invsq:
|
||||
{
|
||||
const dyadic part = split_positive(raw, fractional_bits);
|
||||
const std::int64_t square = eval_principal(
|
||||
principal::invsq, fractional_bits, part.mantissa_raw);
|
||||
return shift_pow2(square, -2 * part.power);
|
||||
}
|
||||
}
|
||||
throw std::invalid_argument("range lut: unknown map");
|
||||
}
|
||||
|
||||
} // namespace grotto
|
||||
|
||||
#endif // LIBDPF_INCLUDE_GROTTO_RANGE_LUT_HPP__
|
||||
Loading…
Add table
Add a link
Reference in a new issue