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:
Ryan Henry 2026-09-24 20:44:07 -06:00
parent 875f09fec1
commit 0d8a5a8131
97 changed files with 9212 additions and 1159 deletions

View file

@ -116,4 +116,6 @@
#include "dpf/uint256_t.hpp"
#include "dpf/interval.hpp"
#endif // LIBDPF_INCLUDE_DPF_HPP__

View file

@ -143,6 +143,7 @@ class advice_bit_iterable_const_iterator
using difference_type = std::ptrdiff_t;
using node_type = typename iterator_traits::value_type;
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr
explicit advice_bit_iterable_const_iterator(const wrapped_type & it) noexcept
@ -213,28 +214,33 @@ class advice_bit_iterable_const_iterator
return *this;
}
HEDLEY_NO_THROW
advice_bit_iterable_const_iterator operator+(std::size_t n) const noexcept
{
return advice_bit_iterable_const_iterator(it_ + n);
}
HEDLEY_NO_THROW
advice_bit_iterable_const_iterator & operator-=(std::size_t n) noexcept
{
it_ -= n;
return *this;
}
HEDLEY_NO_THROW
advice_bit_iterable_const_iterator operator-(std::size_t n) const noexcept
{
return advice_bit_iterable_const_iterator(it_ - n);
}
HEDLEY_NO_THROW
difference_type
operator-(advice_bit_iterable_const_iterator rhs) const noexcept
{
return it_ - rhs.it_;
}
HEDLEY_NO_THROW
reference operator[](std::size_t i) const noexcept
{
return bit(it_ + i);

View file

@ -57,6 +57,7 @@ class aligned_allocator
template <typename Pointer>
struct deleter
{
HEDLEY_NO_THROW
constexpr void operator()(Pointer p) const noexcept { free(p); }
};
public:
@ -125,6 +126,7 @@ class aligned_allocator
/// @note This function returns the maximum number of elements that can
/// be allocated, not the maximum allocation size in bytes
/// @return The maximum supported allocation size.
HEDLEY_NO_THROW
constexpr size_type max_size() const noexcept
{
return std::numeric_limits<size_type>::max() / sizeof(value_type);

View file

@ -166,17 +166,22 @@ class wire
friend class session<Ring>;
public:
HEDLEY_NO_THROW
constexpr wire() noexcept = default;
HEDLEY_NO_THROW
constexpr std::uint32_t id() const noexcept { return id_; }
HEDLEY_NO_THROW
constexpr session<Ring> * owner() const noexcept { return sess_; }
HEDLEY_NO_THROW
friend bool operator==(wire a, wire b) noexcept
{
return a.sess_ == b.sess_ && a.id_ == b.id_;
}
HEDLEY_NO_THROW
friend bool operator!=(wire a, wire b) noexcept
{
return !(a == b);
@ -240,13 +245,16 @@ public:
static constexpr std::uint32_t mono_role_base = 0x40000000u;
static constexpr std::uint32_t dot_role_base = 0x80000000u;
HEDLEY_NO_THROW
static constexpr std::uint32_t wire_role(std::uint32_t id) noexcept { return id; }
HEDLEY_NO_THROW
static constexpr std::uint32_t mono_role(std::uint32_t i) noexcept
{
return mono_role_base + i;
}
HEDLEY_NO_THROW
static constexpr std::uint32_t dot_role(std::uint32_t gate) noexcept
{
return dot_role_base + gate;
@ -255,6 +263,7 @@ public:
/// Fused within-polynomial λ combinations (Appendix E groupings).
static constexpr std::uint32_t bundle_role_base = 0xC0000000u;
HEDLEY_NO_THROW
static constexpr std::uint32_t bundle_role(std::uint32_t i) noexcept
{
return bundle_role_base + i;
@ -268,6 +277,7 @@ public:
: lanes_(std::move(seed), window)
{ }
HEDLEY_NO_THROW
const seed_type & seed() const noexcept { return lanes_.seed(); }
Ring blind(std::uint32_t role, std::uint64_t index) const
@ -822,11 +832,13 @@ public:
return wires_[check(w)].ready_round;
}
HEDLEY_NO_THROW
std::size_t wire_count() const noexcept { return wires_.size(); }
/// Product shares beyond the per-wire blinds: subset monomials from
/// `product` gates, plus one fused bundle per public-δ class in a
/// polynomial (Appendix E). A lone mask is not counted.
HEDLEY_NO_THROW
std::size_t monomial_count() const noexcept
{
return monos_.size() + bundles_.size();

View file

@ -183,12 +183,14 @@ operator>>(std::basic_istream<CharT, Traits> & is, dpf::bit & value)
/// @}
HEDLEY_NO_THROW
inline constexpr dpf::bit operator+(dpf::bit lhs, dpf::bit rhs) noexcept
{
return static_cast<dpf::bit>(static_cast<bool>(lhs) ^ static_cast<bool>(rhs));
}
/// @brief GF(2) subtraction. Identical to `operator+`.
HEDLEY_NO_THROW
inline constexpr dpf::bit operator-(dpf::bit lhs, dpf::bit rhs) noexcept
{
return lhs + rhs;
@ -216,6 +218,7 @@ struct packed_lane_bits<dpf::bit>
template <>
struct make_from_integral_value<dpf::bit>
{
HEDLEY_NO_THROW
constexpr dpf::bit operator()(bool val) const noexcept
{
return val ? dpf::bit::one : dpf::bit::zero;

View file

@ -41,6 +41,7 @@ namespace detail
{
template <typename Word>
HEDLEY_NO_THROW
constexpr void check_one_bit(Word mask) noexcept
{
#if defined(__GNUC__) || defined(__clang__)
@ -135,6 +136,7 @@ class bit_array_base
inline constexpr bit_array_base(const bit_array_base &) = default;
/// @brief default move constructor
HEDLEY_NO_THROW
inline constexpr bit_array_base(bit_array_base &&) noexcept = default;
/// @brief default destructor
@ -145,6 +147,7 @@ class bit_array_base
bit_array_base & operator=(const bit_array_base &) = default;
/// @brief defaulted move assignment
HEDLEY_NO_THROW
inline constexpr
bit_array_base & operator=(bit_array_base &&) noexcept = default;
@ -163,6 +166,7 @@ class bit_array_base
/// @note Does not perform bounds checking; behaviour is undefined if
/// `pos` is out of bounds
/// @return `data()[pos]`
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr word_type data(size_type pos) const noexcept
{
@ -187,6 +191,7 @@ class bit_array_base
return data()[pos];
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr size_type data_length() const noexcept { return derived_from_this()->data_length(); }
@ -261,6 +266,7 @@ class bit_array_base
/// @{
/// @returns iterator to the first element
/// @complexity `O(1)`
HEDLEY_NO_THROW
constexpr iterator begin() noexcept
{
auto *p = data();
@ -269,6 +275,7 @@ class bit_array_base
}
/// @returns iterator to the first element
/// @complexity `O(1)`
HEDLEY_NO_THROW
constexpr const_iterator begin() const noexcept
{
auto *p = data();
@ -277,6 +284,7 @@ class bit_array_base
}
/// @returns iterator to the first element
/// @complexity `O(1)`
HEDLEY_NO_THROW
constexpr const_iterator cbegin() const noexcept
{
return begin();
@ -287,6 +295,7 @@ class bit_array_base
/// @{
/// @returns iterator to the element following the last element
/// @complexity `O(1)`
HEDLEY_NO_THROW
constexpr iterator end() noexcept
{
auto *p = data();
@ -296,6 +305,7 @@ class bit_array_base
}
/// @returns iterator to the element following the last element
/// @complexity `O(1)`
HEDLEY_NO_THROW
constexpr const_iterator end() const noexcept
{
auto *p = data();
@ -305,6 +315,7 @@ class bit_array_base
}
/// @returns iterator to the element following the last element
/// @complexity `O(1)`
HEDLEY_NO_THROW
constexpr const_iterator cend() const noexcept
{
return end();
@ -326,6 +337,7 @@ class bit_array_base
/// @details checks if all bits are set to `true`
/// @return `true` if all of the bits are set to `true`, otherwise `false`
/// @complexity `O(size())`
HEDLEY_NO_THROW
bool all() const noexcept
{
if (size() == 0) return true;
@ -351,6 +363,7 @@ class bit_array_base
/// `true`, otherwise `false`
/// @complexity `O(last-first)`
template <typename Iterator>
HEDLEY_NO_THROW
bool all(Iterator first, Iterator last) const noexcept
{
bool ok = true;
@ -365,6 +378,7 @@ class bit_array_base
/// @details checks if any bits are set to `true`
/// @return `true` if any of the bits are set to `true`, otherwise `false`
/// @complexity `O(size())`
HEDLEY_NO_THROW
bool any() const noexcept
{
const size_type n = data_length();
@ -386,6 +400,7 @@ class bit_array_base
/// `true`, otherwise `false`
/// @complexity `O(last-first)`
template <typename Iterator>
HEDLEY_NO_THROW
bool any(Iterator first, Iterator last) const noexcept
{
bool found = false;
@ -400,6 +415,7 @@ class bit_array_base
/// @details checks if none of the bits are set to `true`
/// @return `true` if none of the bits are set to `true`, otherwise `false`
/// @complexity `O(size())`
HEDLEY_NO_THROW
bool none() const noexcept
{
return !any();
@ -412,6 +428,7 @@ class bit_array_base
/// `true`, otherwise `false`
/// @complexity `O(last-first)`
template <typename Iterator>
HEDLEY_NO_THROW
bool none(Iterator first, Iterator last) const noexcept
{
return !any(first, last);
@ -423,6 +440,7 @@ class bit_array_base
/// @details counts the number of bits that are set to `true`
/// @returns the number of bits set to `true`
/// @complexity `O(size())`
HEDLEY_NO_THROW
size_type count() const noexcept
{
const size_type n = data_length();
@ -443,6 +461,7 @@ class bit_array_base
/// @return the number of bits in the given range that are set to `true`
/// @complexity `O(last-first)`
template <typename Iterator>
HEDLEY_NO_THROW
size_type count(Iterator first, Iterator last) const noexcept
{
size_type sum = 0;
@ -460,6 +479,7 @@ class bit_array_base
/// @details counts the parity of all stored bits
/// @returns the parity of all stored bits
/// @complexity `O(size())`
HEDLEY_NO_THROW
size_type parity() const noexcept
{
const size_type n = data_length();
@ -481,6 +501,7 @@ class bit_array_base
/// @return the parity of all bits in the given range
/// @complexity `O(last-first)`
template <typename Iterator>
HEDLEY_NO_THROW
size_type parity(Iterator first, Iterator last) const noexcept
{
word_type x = word_type{0};
@ -496,6 +517,7 @@ class bit_array_base
/// @brief returns the number of bits
/// @returns number of bits that the `bit_array_base` holds
/// @complexity `O(1)`
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr size_type size() const noexcept
@ -507,6 +529,7 @@ class bit_array_base
/// @{
/// @brief sets all bits to `true`
/// @complexity `O(size())`
HEDLEY_NO_THROW
constexpr void set() noexcept
{
const size_type n = data_length();
@ -551,6 +574,7 @@ class bit_array_base
/// @{
/// @brief sets all bits to `false'
/// @complexity `O(size())`
HEDLEY_NO_THROW
constexpr void unset() noexcept
{
word_type *p = data();
@ -578,6 +602,7 @@ class bit_array_base
/// @{
/// @brief flips all bits (like `operator~`, but in-place)
/// @complexity `O(size())`
HEDLEY_NO_THROW
constexpr void flip() noexcept
{
const size_type n = data_length();
@ -711,6 +736,7 @@ class bit_array_base
return static_cast<dpf::bit>(static_cast<bool>(lhs) ^ static_cast<bool>(rhs));
}
HEDLEY_NO_THROW
friend constexpr dpf::bit operator+(bit_reference lhs, bit_reference rhs) noexcept
{
return static_cast<dpf::bit>(static_cast<bool>(lhs) ^ static_cast<bool>(rhs));
@ -820,6 +846,7 @@ class bit_array_base
/// @brief Exchange the bits named by two proxies, including temporaries
/// returned from `operator[]` and `operator*`.
HEDLEY_NO_THROW
friend constexpr void swap(bit_reference a, bit_reference b) noexcept
{
const bool tmp = static_cast<bool>(a);
@ -870,6 +897,7 @@ class bit_array_base
static constexpr word_type sentinel = ~word_type(0);
/// @brief Low `n` bits set. `n == 0` yields 0. `n >= bits_per_word` yields all ones.
HEDLEY_NO_THROW
static constexpr word_type low_bits_mask(size_type n) noexcept
{
if (n == 0) return word_type{0};
@ -878,18 +906,21 @@ class bit_array_base
static_cast<word_type>(~word_type{0}) >> (bits_per_word - n));
}
HEDLEY_NO_THROW
static constexpr size_type pop(word_type w) noexcept
{
return static_cast<size_type>(utils::popcount(w));
}
/// @brief Bits strictly below the single set bit in `mask`.
HEDLEY_NO_THROW
static constexpr word_type bits_below(word_type mask) noexcept
{
return static_cast<word_type>(mask - word_type{1});
}
/// @brief Bits at and above the single set bit in `mask`.
HEDLEY_NO_THROW
static constexpr word_type bits_at_and_above(word_type mask) noexcept
{
return static_cast<word_type>(~bits_below(mask));
@ -999,6 +1030,7 @@ class bit_iterator_base
word_type mask_;
/// @brief Singular iterator. Comparable, not dereferenceable.
HEDLEY_NO_THROW
inline constexpr bit_iterator_base() noexcept
: word_ptr_{nullptr},
mask_{lsb}
@ -1125,11 +1157,14 @@ class bit_iterator final
using word_pointer = typename bit_array_base<ConcreteBitArrayT, WordT>::word_pointer;
/// @brief Singular iterator. Comparable, not dereferenceable.
HEDLEY_NO_THROW
constexpr bit_iterator() noexcept = default;
HEDLEY_NO_THROW
inline constexpr
bit_iterator(const bit_iterator &) noexcept = default;
HEDLEY_NO_THROW
inline constexpr bit_iterator(bit_iterator &&) noexcept = default;
HEDLEY_NO_THROW
@ -1218,6 +1253,7 @@ class bit_iterator final
return tmp -= amt;
}
HEDLEY_NO_THROW
friend constexpr iterator operator+(difference_type amt, iterator it) noexcept
{
return it + amt;
@ -1257,6 +1293,7 @@ class const_bit_iterator final
using const_word_pointer = typename bit_array_base<ConcreteBitArrayT, WordT>::const_word_pointer;
/// @brief Singular iterator. Comparable, not dereferenceable.
HEDLEY_NO_THROW
constexpr const_bit_iterator() noexcept = default;
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
@ -1349,6 +1386,7 @@ class const_bit_iterator final
return *this;
}
HEDLEY_NO_THROW
inline constexpr
const_iterator & operator-=(difference_type amt) noexcept
{
@ -1356,6 +1394,7 @@ class const_bit_iterator final
return *this;
}
HEDLEY_NO_THROW
inline constexpr
const_iterator operator+(difference_type amt) const noexcept
{
@ -1363,6 +1402,7 @@ class const_bit_iterator final
return tmp += amt;
}
HEDLEY_NO_THROW
inline constexpr
const_iterator operator-(difference_type amt) const noexcept
{
@ -1370,12 +1410,14 @@ class const_bit_iterator final
return tmp -= amt;
}
HEDLEY_NO_THROW
friend constexpr const_iterator operator+(difference_type amt,
const_iterator it) noexcept
{
return it + amt;
}
HEDLEY_NO_THROW
inline constexpr
const_reference operator[](difference_type i) const noexcept
{
@ -1402,10 +1444,14 @@ class alignas(utils::max_align_v) static_bit_array final
/// `size()` bits
static constexpr size_type data_length_ = utils::quotient_ceiling(Nbits, bits_per_word);
public:
HEDLEY_NO_THROW
constexpr static_bit_array(static_bit_array &&) noexcept = default;
HEDLEY_NO_THROW
constexpr static_bit_array(const static_bit_array &) noexcept = default;
~static_bit_array() = default;
HEDLEY_NO_THROW
constexpr static_bit_array & operator=(static_bit_array &&) noexcept = default;
HEDLEY_NO_THROW
constexpr static_bit_array & operator=(const static_bit_array &) noexcept = default;
/// @brief constructs a zeroed `static_bit_array` that holds `Nbits` bits
inline constexpr static_bit_array()
@ -1459,6 +1505,7 @@ class alignas(utils::max_align_v) static_bit_array final
/// @brief returns the number of bits
/// @returns number of bits that the `static_bit_array` holds
/// @complexity `O(1)`
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr size_type size() const noexcept
@ -1509,6 +1556,7 @@ class dynamic_bit_array
std::copy_n(other.data_.get(), data_length_ + 1, data_.get());
}
HEDLEY_NO_THROW
dynamic_bit_array(dynamic_bit_array && other) noexcept
: num_bits_{std::exchange(other.num_bits_, 0)},
data_length_{std::exchange(other.data_length_, 0)},
@ -1525,6 +1573,7 @@ class dynamic_bit_array
return *this;
}
HEDLEY_NO_THROW
dynamic_bit_array & operator=(dynamic_bit_array && other) noexcept
{
if (this != &other)
@ -1537,6 +1586,7 @@ class dynamic_bit_array
return *this;
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
~dynamic_bit_array() noexcept
{
@ -1595,6 +1645,7 @@ class dynamic_bit_array
/// @brief returns the number of bits
/// @returns number of bits that the `dynamic_bit_array` holds
/// @complexity `O(1)`
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr size_type size() const noexcept
@ -1603,6 +1654,8 @@ class dynamic_bit_array
}
private:
/// Store zeros through `volatile` so the wipe is not deleted as a dead store.
HEDLEY_NO_THROW
void wipe() noexcept
{
if (!data_) return;
@ -1620,6 +1673,7 @@ class dynamic_bit_array
/// @brief
template <typename WordT>
HEDLEY_NO_THROW
inline constexpr void swap(typename dynamic_bit_array<WordT>::reference lhs,
typename dynamic_bit_array<WordT>::reference rhs) noexcept
{
@ -1630,6 +1684,7 @@ inline constexpr void swap(typename dynamic_bit_array<WordT>::reference lhs,
template <std::size_t Nbits,
typename WordT>
HEDLEY_NO_THROW
inline constexpr void swap(typename static_bit_array<Nbits, WordT>::reference lhs,
typename static_bit_array<Nbits, WordT>::reference rhs) noexcept
{

View file

@ -187,7 +187,9 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
/// @}
HEDLEY_NO_THROW
bitstring & operator=(const bitstring &) noexcept = default;
HEDLEY_NO_THROW
bitstring & operator=(bitstring &&) noexcept = default;
~bitstring() = default;
@ -387,6 +389,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
using base::flip;
/// @brief Flips every defined bit and clears bits above `Nbits`.
HEDLEY_NO_THROW
constexpr void flip() noexcept
{
base::flip();
@ -468,6 +471,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
/// @note Does not perform bounds checking; behaviour is undefined if
/// `pos` is out of bounds
/// @return `data()[pos]`
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr word_type data(size_type pos) const noexcept
{
@ -480,6 +484,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
/// @note Does not perform bounds checking; behaviour is undefined if
/// `pos` is out of bounds
/// @return `data()[pos]`
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr word_type & data(size_type pos) noexcept
{
@ -488,6 +493,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
/// @brief length of the underlying data array
/// @return the number of elements in the underlying array
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr size_type data_length() const noexcept
{
@ -497,6 +503,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
/// @brief returns the number of bits
/// @returns number of bits that the `bitstring` holds
/// @complexity `O(1)`
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr size_type size() const noexcept
@ -510,6 +517,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
std::array<word_type, data_length_> data_{};
/// @brief Mask of the bits that belong to this string in the high word.
HEDLEY_NO_THROW
static constexpr word_type defined_high_mask() noexcept
{
constexpr auto rem = Nbits % bits_per_word;
@ -519,6 +527,7 @@ class bitstring : public bit_array_base<bitstring<Nbits, WordT>, WordT>
return static_cast<word_type>((word_type{1} << rem) - word_type{1});
}
HEDLEY_NO_THROW
constexpr void clear_unused() noexcept
{
if constexpr (Nbits % bits_per_word != 0)
@ -630,6 +639,7 @@ struct countl_zero_symmetric_difference<dpf::bitstring<Nbits, WordT>>
{
using T = dpf::bitstring<Nbits, WordT>;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr
@ -706,6 +716,7 @@ struct make_from_integral_value<dpf::bitstring<Nbits, WordT>>
using integral_type = std::conditional_t<std::is_void_v<T_integral_type>, simde_uint128, T_integral_type>;
static constexpr auto mod = utils::mod_pow_2<integral_type>{};
static constexpr auto bits_per_last_word = Nbits % T::bits_per_word;
HEDLEY_NO_THROW
constexpr dpf::bitstring<Nbits, WordT> operator()(integral_type val) const noexcept
{
dpf::bitstring<Nbits, WordT> ret;
@ -731,6 +742,7 @@ struct mod_pow_2<dpf::bitstring<Nbits, WordT>>
using T = dpf::bitstring<Nbits, WordT>;
static constexpr auto to_int = to_integral_type<T>{};
static constexpr auto mod = mod_pow_2<typename T::integral_type>{};
HEDLEY_NO_THROW
std::size_t operator()(T val, std::size_t n) const noexcept
{
return mod(to_int(val), n);
@ -1141,14 +1153,23 @@ class numeric_limits<dpf::bitstring<Nbits, WordT>>
= std::numeric_limits<typename dpf::bitstring<Nbits, WordT>::integral_type>::traps;
static constexpr bool tinyness_before = false;
HEDLEY_NO_THROW
static constexpr dpf::bitstring<Nbits, WordT> min() noexcept { return dpf::bitstring<Nbits, WordT>{}; }
HEDLEY_NO_THROW
static constexpr dpf::bitstring<Nbits, WordT> lowest() noexcept { return dpf::bitstring<Nbits, WordT>{}; }
HEDLEY_NO_THROW
static constexpr dpf::bitstring<Nbits, WordT> max() noexcept { return ~dpf::bitstring<Nbits, WordT>{}; }
HEDLEY_NO_THROW
static constexpr dpf::bitstring<Nbits, WordT> epsilon() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr dpf::bitstring<Nbits, WordT> round_error() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr dpf::bitstring<Nbits, WordT> infinity() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr dpf::bitstring<Nbits, WordT> quiet_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr dpf::bitstring<Nbits, WordT> signaling_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr dpf::bitstring<Nbits, WordT> denorm_min() noexcept { return 0; }
};

475
include/dpf/blocked_dcf.hpp Normal file
View file

@ -0,0 +1,475 @@
/// @file dpf/blocked_dcf.hpp
/// @brief Blocked-checkpoint comparison: one ring word per block of levels.
/// @details The seed spine stays dense. Ring words are published only at a
/// public checkpoint schedule. Point eval expands parked siblings
/// up to the next checkpoint; a full-domain memoizer already holds
/// those nodes. `q` tail bits, when the comparison sets the key
/// depth, are a residual table on the node at height `h`.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
#ifndef LIBDPF_INCLUDE_DPF_BLOCKED_DCF_HPP__
#define LIBDPF_INCLUDE_DPF_BLOCKED_DCF_HPP__
#include <array>
#include <cstddef>
#include <cstdint>
#include <type_traits>
#include <utility>
#include <vector>
#include "hedley/hedley.h"
#include "dpf/dcf.hpp"
#include "dpf/path_memoizer.hpp"
#include "dpf/twiddle.hpp"
#include "dpf/utils.hpp"
namespace dpf
{
namespace detail
{
namespace blocked
{
template <std::size_t H, std::size_t B>
struct schedule
{
static constexpr std::size_t count =
(B == 0 || H == 0) ? 0 : (H + B - 1) / B;
static constexpr auto depths = [] {
std::array<std::size_t, count == 0 ? 1 : count> cs{};
if constexpr (count == 0)
return cs;
const std::size_t base = H / count;
const std::size_t extra = H % count;
std::size_t acc = 0;
for (std::size_t i = 0; i < count; ++i)
{
acc += base + (i < extra ? 1 : 0);
cs[i] = acc;
}
return cs;
}();
HEDLEY_CONST
HEDLEY_NO_THROW
static constexpr bool contains(std::size_t depth) noexcept
{
for (std::size_t i = 0; i < count; ++i)
{
if (depths[i] == depth)
return true;
}
return false;
}
HEDLEY_CONST
HEDLEY_NO_THROW
static constexpr std::size_t index(std::size_t depth) noexcept
{
for (std::size_t i = 0; i < count; ++i)
{
if (depths[i] == depth)
return i;
}
return static_cast<std::size_t>(-1);
}
};
template <typename PRG, typename Node>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
uint64_t rho_of(const Node & node, uint64_t mask) noexcept
{
auto kids = PRG::eval01(dpf::unset_lo_2bits(node));
return dcf_impl::convert_node(kids[0], mask);
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr int control_sign(uint8_t t0, uint8_t t1) noexcept
{
return static_cast<int>(t0) - static_cast<int>(t1);
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t mul_sgn(int sgn, uint64_t v, uint64_t mask) noexcept
{
if (sgn > 0)
return v & mask;
if (sgn < 0)
return dcf_impl::neg_m(v, mask);
return 0;
}
/// Group element `sgn` (`+1`, `-1`, or `0`) used as an `assign_cmp` coefficient.
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t sgn_coeff(int sgn, uint64_t mask) noexcept
{
if (sgn > 0)
return 1ULL & mask;
if (sgn < 0)
return dcf_impl::neg_m(1ULL, mask);
return 0;
}
template <typename PRG, typename Node>
HEDLEY_NO_THROW
uint64_t checkpoint_word(const Node & n0, const Node & n1, uint64_t beta,
uint64_t mask) noexcept
{
const int sgn = control_sign(
static_cast<uint8_t>(dpf::get_lo_bit(n0)),
static_cast<uint8_t>(dpf::get_lo_bit(n1)));
const uint64_t r0 = rho_of<PRG>(n0, mask);
const uint64_t r1 = rho_of<PRG>(n1, mask);
const uint64_t inner =
(beta + dcf_impl::neg_m(r0, mask) + r1) & mask;
return mul_sgn(sgn, inner, mask);
}
template <typename Node>
HEDLEY_NO_THROW
uint64_t checkpoint_coeff(const Node & n0, const Node & n1,
uint64_t mask) noexcept
{
return sgn_coeff(control_sign(
static_cast<uint8_t>(dpf::get_lo_bit(n0)),
static_cast<uint8_t>(dpf::get_lo_bit(n1))), mask);
}
template <typename PRG, typename Node>
HEDLEY_NON_NULL(4)
HEDLEY_NO_THROW
void suffix_masks(const Node & seed, std::size_t q, uint64_t mask,
uint64_t * out) noexcept
{
Node cur[4]{};
Node nxt[8]{};
cur[0] = seed;
std::size_t n = 1;
for (std::size_t lvl = 0; lvl < q; ++lvl)
{
std::size_t m = 0;
for (std::size_t i = 0; i < n; ++i)
{
auto kids = PRG::eval01(dpf::unset_lo_2bits(cur[i]));
nxt[m++] = kids[0];
nxt[m++] = kids[1];
}
for (std::size_t i = 0; i < m; ++i)
cur[i] = nxt[i];
n = m;
}
for (std::size_t i = 0; i < n; ++i)
out[i] = dcf_impl::convert_node(cur[i], mask);
}
template <typename PRG, typename Node>
HEDLEY_NON_NULL(8)
HEDLEY_NO_THROW
void tail_words(const Node & n0, const Node & n1, uint64_t beta, uint64_t mask,
bool include_eq, uint64_t suffix, std::size_t q, uint64_t * words,
uint64_t * coeffs) noexcept
{
uint64_t u0[4]{};
uint64_t u1[4]{};
suffix_masks<PRG>(n0, q, mask, u0);
suffix_masks<PRG>(n1, q, mask, u1);
const int sgn = control_sign(
static_cast<uint8_t>(dpf::get_lo_bit(n0)),
static_cast<uint8_t>(dpf::get_lo_bit(n1)));
const uint64_t coeff = sgn_coeff(sgn, mask);
const std::size_t n = std::size_t{1} << q;
for (std::size_t z = 0; z < n; ++z)
{
const bool pred = include_eq
? (z <= suffix)
: (z < suffix);
const uint64_t inner =
((pred ? beta : 0ULL) + dcf_impl::neg_m(u0[z], mask) + u1[z]) & mask;
words[z] = mul_sgn(sgn, inner, mask);
if (coeffs != nullptr)
coeffs[z] = pred ? coeff : 0ULL;
}
}
template <typename KeyT>
HEDLEY_NO_THROW
uint64_t add_membership(uint64_t acc, const typename KeyT::interior_node & node,
uint64_t word, uint64_t mask, int party) noexcept
{
using prg = typename KeyT::interior_prg;
const uint8_t t = static_cast<uint8_t>(dpf::get_lo_bit(node));
const uint64_t y =
(rho_of<prg>(node, mask) + (t ? word : 0ULL)) & mask;
return (acc + (party ? dcf_impl::neg_m(y, mask) : y)) & mask;
}
template <typename KeyT>
uint64_t add_frontier(uint64_t acc, const typename KeyT::interior_node & seed,
std::size_t from_depth, std::size_t to_depth, const KeyT & dpf, uint64_t word,
uint64_t mask, int party)
{
using node = typename KeyT::interior_node;
std::vector<node> cur;
std::vector<node> nxt;
cur.push_back(seed);
for (std::size_t lvl = from_depth; lvl < to_depth; ++lvl)
{
nxt.clear();
nxt.reserve(cur.size() * 2);
const node cw0 = dpf.correction_word(lvl, false);
const node cw1 = dpf.correction_word(lvl, true);
for (const node & fs : cur)
{
auto kids = KeyT::traverse_interior01(fs, cw0, cw1);
nxt.push_back(kids[0]);
nxt.push_back(kids[1]);
}
cur.swap(nxt);
}
for (const node & fs : cur)
acc = add_membership<KeyT>(acc, fs, word, mask, party);
return acc;
}
template <typename KeyT, typename InputT>
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t query_suffix(InputT tx, std::size_t nbits, std::size_t q) noexcept
{
if (q == 0)
return 0;
uint64_t z = 0;
auto bit_mask = KeyT::msb_mask >> (nbits - q);
for (std::size_t j = 0; j < q; ++j, bit_mask >>= 1)
z = (z << 1) | static_cast<uint64_t>(!!(bit_mask & tx));
return z;
}
template <typename KeyT>
HEDLEY_NO_THROW
uint64_t finish_share(const KeyT & dpf, uint64_t suffix, uint64_t acc,
const typename KeyT::interior_node & at_h, int party) noexcept
{
using namespace dcf_impl;
using prg = typename KeyT::interior_prg;
const auto & ch = dpf.cmp();
const uint64_t mask = ch.mask;
constexpr std::size_t q = KeyT::cmp_q;
constexpr std::size_t h = KeyT::cmp_h;
if constexpr (q == 0)
{
if (ch.include_eq)
{
constexpr auto wi = schedule<h, KeyT::cmp_block>::index(h);
acc = add_membership<KeyT>(acc, at_h, dpf.value_cw(wi), mask, party);
}
(void)suffix;
}
else
{
const uint64_t z = suffix;
uint64_t u[4]{};
suffix_masks<prg>(at_h, q, mask, u);
const uint8_t t = static_cast<uint8_t>(dpf::get_lo_bit(at_h));
const uint64_t y = (u[z] + (t ? dpf.tail_cw(z) : 0ULL)) & mask;
acc = (acc + (party ? neg_m(y, mask) : y)) & mask;
}
if (ch.eval_as_ge)
acc = neg_m(acc, mask);
const uint64_t add = [&]() -> uint64_t {
if constexpr (is_party_key_v<KeyT>)
return dpf.cmp_addend().raw();
else
return dpf.cmp_addend();
}();
return (acc + add) & mask;
}
template <typename KeyT, typename InputT, typename PathMemoizer>
uint64_t eval_share(const KeyT & dpf, InputT tx, PathMemoizer & path)
{
using node = typename KeyT::interior_node;
const auto & ch = dpf.cmp();
const uint64_t mask = ch.mask;
const uint64_t add = [&]() -> uint64_t {
if constexpr (is_party_key_v<KeyT>)
return dpf.cmp_addend().raw();
else
return dpf.cmp_addend();
}();
if (ch.trivial == cmp_trivial::always_true
|| ch.trivial == cmp_trivial::always_false)
return add & mask;
constexpr std::size_t h = KeyT::cmp_h;
using sched = schedule<h, KeyT::cmp_block>;
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
const int party = dpf::get_lo_bit(dpf.root()) ? 1 : 0;
dpf::detail::ensure_level(dpf, tx, path, h);
struct parked
{
node seed;
std::size_t depth;
};
parked pend[128];
std::size_t npend = 0;
uint64_t acc = 0;
auto bit_mask = KeyT::msb_mask;
for (std::size_t level = 0; level < h; ++level, bit_mask >>= 1)
{
const bool xi = !!(bit_mask & tx);
const node & parent = path[level];
const node right = KeyT::traverse_interior(parent,
dpf.correction_word(level, true), true);
if (!xi)
{
pend[npend].seed = right;
pend[npend].depth = level + 1;
++npend;
}
const std::size_t c = level + 1;
if (sched::contains(c))
{
const uint64_t word = dpf.value_cw(sched::index(c));
for (std::size_t p = 0; p < npend; ++p)
{
acc = add_frontier<KeyT>(acc, pend[p].seed, pend[p].depth, c,
dpf, word, mask, party);
}
npend = 0;
}
}
const uint64_t suffix = query_suffix<KeyT>(tx, nbits, KeyT::cmp_q);
return finish_share(dpf, suffix, acc, path[h], party);
}
template <typename KeyT, typename Integral, typename Memo>
HEDLEY_NO_THROW
bool memo_has(const Memo & memo, Integral prefix, std::size_t depth,
Integral from_lane, Integral to_excl) noexcept
{
const auto shift = KeyT::cmp_depth - depth;
const auto from_p = from_lane >> shift;
if (prefix < from_p)
return false;
const auto idx = static_cast<std::size_t>(prefix - from_p);
const auto count = memo.get_nodes_at_level(depth, from_lane, to_excl);
return idx < count;
}
template <typename KeyT, typename Integral, typename Memo>
const typename KeyT::interior_node & memo_node(const Memo & memo, Integral prefix,
std::size_t depth, Integral from_lane)
{
const auto shift = KeyT::cmp_depth - depth;
const auto from_p = from_lane >> shift;
const auto idx = static_cast<std::size_t>(prefix - from_p);
return memo[depth][idx];
}
template <typename KeyT, typename Integral, typename Memo>
uint64_t eval_share_memo(const KeyT & dpf, Integral lane,
Integral from_lane, Integral to_excl, const Memo & memo)
{
using node = typename KeyT::interior_node;
const auto & ch = dpf.cmp();
const uint64_t mask = ch.mask;
const uint64_t add = [&]() -> uint64_t {
if constexpr (is_party_key_v<KeyT>)
return dpf.cmp_addend().raw();
else
return dpf.cmp_addend();
}();
if (ch.trivial == cmp_trivial::always_true
|| ch.trivial == cmp_trivial::always_false)
return add & mask;
constexpr std::size_t h = KeyT::cmp_h;
using sched = schedule<h, KeyT::cmp_block>;
const int party = dpf::get_lo_bit(dpf.root()) ? 1 : 0;
struct parked
{
node seed;
Integral prefix;
std::size_t depth;
};
parked pend[128];
std::size_t npend = 0;
uint64_t acc = 0;
Integral path_pref = 0;
const std::size_t nbits = static_cast<std::size_t>(ch.nbits);
for (std::size_t level = 0; level < h; ++level)
{
const bool xi = ((lane >> (nbits - 1 - level)) & Integral{1}) != 0;
const node & parent = memo_node<KeyT>(memo, path_pref, level, from_lane);
const Integral sib = static_cast<Integral>((path_pref << 1) | Integral{1});
if (!xi)
{
const node right = KeyT::traverse_interior(parent,
dpf.correction_word(level, true), true);
pend[npend].seed = right;
pend[npend].prefix = sib;
pend[npend].depth = level + 1;
++npend;
}
path_pref = static_cast<Integral>((path_pref << 1) | Integral{xi ? 1 : 0});
const std::size_t c = level + 1;
if (!sched::contains(c))
continue;
const uint64_t word = dpf.value_cw(sched::index(c));
for (std::size_t p = 0; p < npend; ++p)
{
const std::size_t extra = c - pend[p].depth;
const Integral leftmost =
static_cast<Integral>(pend[p].prefix << extra);
const Integral rightmost = static_cast<Integral>(
leftmost + static_cast<Integral>((Integral{1} << extra) - 1));
const bool covered =
memo_has<KeyT>(memo, leftmost, c, from_lane, to_excl)
&& memo_has<KeyT>(memo, rightmost, c, from_lane, to_excl);
if (covered && extra < 16)
{
const Integral nleaf = static_cast<Integral>(Integral{1} << extra);
for (Integral k = 0; k < nleaf; ++k)
{
const auto pref = static_cast<Integral>(leftmost + k);
acc = add_membership<KeyT>(acc,
memo_node<KeyT>(memo, pref, c, from_lane), word, mask,
party);
}
}
else
{
acc = add_frontier<KeyT>(acc, pend[p].seed, pend[p].depth, c,
dpf, word, mask, party);
}
}
npend = 0;
}
const node & at_h = memo_node<KeyT>(memo, path_pref, h, from_lane);
const uint64_t suffix = static_cast<uint64_t>(lane)
& (KeyT::cmp_q == 0 ? 0ULL : ((1ULL << KeyT::cmp_q) - 1ULL));
return finish_share(dpf, suffix, acc, at_h, party);
}
} // namespace blocked
} // namespace detail
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_BLOCKED_DCF_HPP__

View file

@ -39,6 +39,7 @@ namespace detail
{
template <typename PRG>
HEDLEY_NO_THROW
typename PRG::block_type mask_master(typename PRG::block_type master) noexcept
{
unsigned char raw[sizeof(master)];
@ -140,6 +141,7 @@ struct buffered_slot
return lane_codec<PRG, T>::at(seed_, index);
}
HEDLEY_NO_THROW
std::uint64_t sampled() const noexcept { return absolute_pos_; }
private:
@ -165,7 +167,13 @@ typename PRG::block_type sample_master_seed()
return dpf::uniform_sample<typename PRG::block_type>();
}
/// Fixed lanes. Lane `I` is `PRG::eval(master, I)`.
/// Forward cursor over one PRG stream per value type.
///
/// `get<I>()` and `fill<I>()` consume the cursor. `at<I>(index)` reads an
/// absolute index and leaves the cursor where it is. `sampled<I>()` reports
/// how far `get` and `fill` have advanced. `per_stream_buffer_elems` is at
/// least 1.
/// @snippet evaluation/buffered_prg.cpp buffered-prg
template <typename PRG, typename... Ts>
class buffered_prg
{
@ -184,6 +192,7 @@ public:
buffers_(make_buffers(per_stream_buffer_elems))
{ }
HEDLEY_NO_THROW
const seed_type & seed() const noexcept { return seed_; }
template <std::size_t I>
@ -208,6 +217,7 @@ public:
}
template <std::size_t I>
HEDLEY_NO_THROW
std::uint64_t sampled() const noexcept
{
static_assert(I < stream_count, "stream index out of range");
@ -238,9 +248,12 @@ private:
template <typename... Ts>
using aes_buffered_prg = buffered_prg<dpf::prg::aes128, Ts...>;
/// Dynamic lanes of one value type. `value_at(role, index)` and
/// `mask_at(role, index)` are independent of call order. A window cache
/// refills from the requested index.
/// Seekable value and mask streams for a runtime set of roles.
///
/// `value_at(role, index)` and `mask_at(role, index)` are independent of
/// call order. A repeated index returns the same element. `window` is at
/// least 1.
/// @snippet evaluation/buffered_prg.cpp lane-table
template <typename T, typename PRG = dpf::prg::aes128>
class lane_table
{
@ -267,6 +280,7 @@ public:
lane_table(lane_table &&) = default;
lane_table & operator=(lane_table &&) = default;
HEDLEY_NO_THROW
const seed_type & seed() const noexcept { return seed_; }
T value_at(std::uint32_t role, std::uint64_t index) const

View file

@ -26,15 +26,61 @@
namespace dpf
{
template <typename Beta>
struct ic_pack;
template <typename T>
struct is_ic_pack : std::false_type {};
template <typename Beta>
struct is_ic_pack<ic_pack<Beta>> : std::true_type {};
template <typename ...Ts>
inline constexpr bool no_ic_pack_v =
(!is_ic_pack<std::decay_t<Ts>>::value && ...);
/// Comparison kind for the optional DCF channel on a key.
/// `lt`/`leq`/`gt`/`geq` are the comparison predicates. The later kinds are
/// path paints: one constant on each sibling subtree of the secret point,
/// evaluated by the same value-correction walk.
enum class cmp_kind : uint8_t
{
lt = 0,
leq = 1,
gt = 2,
geq = 3
geq = 3,
lcp = 4, // common-prefix length
prefix = 5, // matched prefix, in the lane's high bits
mask = 6, // high-bit mask of that length
one_hot = 7, // 2^{length} (0 when the bit does not fit)
break_bit = 8, // secret bit at the first difference
prefix_with_length = 9, // (low-aligned prefix << length_bits) | length
paint = 10 // caller-supplied unit plant
};
/// True for the path-paint kinds. Comparisons stay `lt`/`leq`/`gt`/`geq`.
HEDLEY_NO_THROW
inline constexpr bool is_paint_kind(cmp_kind kind) noexcept
{
switch (kind)
{
case cmp_kind::lcp:
case cmp_kind::prefix:
case cmp_kind::mask:
case cmp_kind::one_hot:
case cmp_kind::break_bit:
case cmp_kind::prefix_with_length:
case cmp_kind::paint:
return true;
default:
return false;
}
}
/// Unit plant for `path_paint`. `prefix` is the in-lane matched prefix.
using paint_callback = uint64_t (*)(std::size_t matched, uint64_t prefix,
bool leaf, const void * ctx);
enum class cmp_trivial : uint8_t
{
none = 0,
@ -48,6 +94,7 @@ namespace dcf_impl
{
template <typename Beta>
HEDLEY_NO_THROW
Beta default_false() noexcept
{
if constexpr (std::is_same_v<Beta, dpf::bit>)
@ -57,6 +104,7 @@ Beta default_false() noexcept
}
template <typename Beta>
HEDLEY_NO_THROW
uint64_t beta_delta_u64(const Beta & if_true, const Beta & if_false,
uint64_t mask) noexcept
{
@ -79,6 +127,7 @@ uint64_t beta_delta_u64(const Beta & if_true, const Beta & if_false,
}
template <typename Beta>
HEDLEY_NO_THROW
uint64_t beta_to_u64_simple(const Beta & beta, uint64_t mask) noexcept
{
if constexpr (std::is_same_v<Beta, dpf::bit>)
@ -88,6 +137,7 @@ uint64_t beta_to_u64_simple(const Beta & beta, uint64_t mask) noexcept
}
template <typename Beta>
HEDLEY_NO_THROW
Beta sub_beta(const Beta & a, const Beta & b) noexcept
{
if constexpr (std::is_same_v<Beta, dpf::bit>)
@ -99,6 +149,7 @@ Beta sub_beta(const Beta & a, const Beta & b) noexcept
}
template <typename Beta>
HEDLEY_NO_THROW
Beta u64_to_beta(uint64_t v) noexcept
{
if constexpr (std::is_same_v<Beta, dpf::bit>)
@ -107,7 +158,10 @@ Beta u64_to_beta(uint64_t v) noexcept
return static_cast<Beta>(v);
}
inline uint64_t default_mask_for_bits(std::size_t out_bits) noexcept
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t default_mask_for_bits(std::size_t out_bits) noexcept
{
if (out_bits >= 64)
return ~0ULL;
@ -116,14 +170,18 @@ inline uint64_t default_mask_for_bits(std::size_t out_bits) noexcept
return (1ULL << out_bits) - 1ULL;
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
uint64_t neg_m(uint64_t x, uint64_t mask) noexcept
constexpr uint64_t neg_m(uint64_t x, uint64_t mask) noexcept
{
return (0ULL - x) & mask;
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
uint64_t sgn_m(uint8_t t1, uint64_t x, uint64_t mask) noexcept
constexpr uint64_t sgn_m(uint8_t t1, uint64_t x, uint64_t mask) noexcept
{
return t1 ? neg_m(x, mask) : x;
}
@ -143,6 +201,7 @@ uint64_t convert_node(simde__m128i n, uint64_t mask) noexcept
/// same block source so their keys stay byte-identical (matched tapes).
template <typename BlockSampler>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
uint64_t sample_addend_blind(uint64_t mask, BlockSampler && sample) noexcept
{
return convert_node(dpf::unset_lo_2bits(sample()), mask);
@ -150,6 +209,7 @@ uint64_t sample_addend_blind(uint64_t mask, BlockSampler && sample) noexcept
/// One level of value CW on GGM children. Updates running `Va`.
/// `ai` is the keep-path bit of the (effective) threshold.
HEDLEY_NO_THROW
inline uint64_t make_value_cw(simde__m128i c0L, simde__m128i c0R,
simde__m128i c1L, simde__m128i c1R, uint8_t t0, uint8_t t1, int ai,
uint64_t & Va, uint64_t beta, uint64_t mask) noexcept
@ -179,8 +239,127 @@ inline uint64_t make_value_cw(simde__m128i c0L, simde__m128i c0R,
return vcw;
}
/// Same recurrence as `make_value_cw`, planting `plant` on the lose child
/// in both directions. `plant == 0` leaves the correction unchanged.
HEDLEY_NO_THROW
inline uint64_t make_value_cw_planted(simde__m128i c0L, simde__m128i c0R,
simde__m128i c1L, simde__m128i c1R, uint8_t t0, uint8_t t1, int ai,
uint64_t & Va, uint64_t plant, uint64_t mask) noexcept
{
(void)t0;
uint64_t v0K, v1K, v0Lo, v1Lo;
if (ai == 0)
{
v0K = convert_node(c0L, mask);
v1K = convert_node(c1L, mask);
v0Lo = convert_node(c0R, mask);
v1Lo = convert_node(c1R, mask);
}
else
{
v0K = convert_node(c0R, mask);
v1K = convert_node(c1R, mask);
v0Lo = convert_node(c0L, mask);
v1Lo = convert_node(c1L, mask);
}
uint64_t vcw = sgn_m(t1,
(v1Lo + neg_m(v0Lo, mask) + neg_m(Va, mask)) & mask, mask);
vcw = (vcw + sgn_m(t1, plant, mask)) & mask;
Va = (Va + neg_m(v1K, mask) + v0K + sgn_m(t1, vcw, mask)) & mask;
return vcw;
}
HEDLEY_NO_THROW
inline unsigned __int128 paint_lane_mask(std::size_t nbits) noexcept
{
using u128 = unsigned __int128;
if (nbits == 0)
return 0;
if (nbits >= 128)
return ~u128{0};
return (u128{1} << nbits) - 1;
}
/// High `d` bits of an `nbits`-wide lane, in that lane's own positions.
HEDLEY_NO_THROW
inline unsigned __int128 paint_high_bits(unsigned __int128 alpha,
std::size_t nbits, std::size_t d) noexcept
{
alpha &= paint_lane_mask(nbits);
if (d == 0 || nbits == 0)
return 0;
if (d >= nbits)
return alpha;
const std::size_t drop = nbits - d;
return (alpha >> drop) << drop;
}
HEDLEY_NO_THROW
inline unsigned __int128 paint_low_aligned(unsigned __int128 alpha,
std::size_t nbits, std::size_t d) noexcept
{
alpha &= paint_lane_mask(nbits);
if (d == 0 || nbits == 0)
return 0;
if (d >= nbits)
return alpha;
return alpha >> (nbits - d);
}
/// Unit (β = 1) lose-subtree or leaf plant. The caller scales by δ.
/// `matched` is the number of leading bits already shared with α. A lose
/// subtree at that depth reconstructs to this value; `leaf` is the full match.
inline uint64_t paint_unit(cmp_kind kind, std::size_t matched,
unsigned __int128 alpha, std::size_t nbits, std::size_t length_bits,
bool leaf, paint_callback fn, const void * ctx)
{
if (nbits == 0)
return 0;
const std::size_t d = leaf ? nbits : matched;
switch (kind)
{
case cmp_kind::lcp:
return static_cast<uint64_t>(d);
case cmp_kind::prefix:
return static_cast<uint64_t>(paint_high_bits(alpha, nbits, d));
case cmp_kind::mask:
return static_cast<uint64_t>(
paint_high_bits(paint_lane_mask(nbits), nbits, d));
case cmp_kind::one_hot:
return d >= 64 ? 0ULL : (1ULL << d);
case cmp_kind::break_bit:
if (leaf || matched >= nbits)
return 0;
return static_cast<uint64_t>(
(alpha >> (nbits - 1 - matched)) & 1);
case cmp_kind::prefix_with_length:
{
if (length_bits >= 128)
return static_cast<uint64_t>(d);
unsigned __int128 packed =
paint_low_aligned(alpha, nbits, d) << length_bits;
packed |= static_cast<unsigned __int128>(d);
return static_cast<uint64_t>(packed);
}
case cmp_kind::paint:
if (fn == nullptr)
return 0;
return fn(d, static_cast<uint64_t>(paint_high_bits(alpha, nbits, d)),
leaf, ctx);
default:
return 0;
}
}
HEDLEY_NO_THROW
inline uint64_t scale_plant(uint64_t unit, uint64_t scale, uint64_t mask) noexcept
{
return (unit * scale) & mask;
}
/// Final leaf value CW. `on_path` is the payload reconstructed when the query
/// stays on α's path through all levels (0 for strict lt/geq; β for leq/gt).
HEDLEY_NO_THROW
inline uint64_t make_final_cw(simde__m128i s0, simde__m128i s1, uint8_t t1,
uint64_t Va, uint64_t mask, uint64_t on_path = 0) noexcept
{
@ -206,7 +385,11 @@ struct cmp_meta
bool eval_as_ge = false; // invert path-sum (geq / gt)
bool include_eq = false; // plant δ on the α-path leaf (leq / gt)
bool active = false;
bool incremental = false; // final correction saved at every depth
int block_width = 0; // 0 = per-level path-sum
int tail_bits = 0; // residual q; 0 when the tree covers every bit
HEDLEY_NO_THROW
bool empty() const noexcept { return !active; }
};
@ -225,6 +408,7 @@ struct cmp_pack
static constexpr bool is_cmp = true;
static constexpr cmp_kind kind = Kind;
static constexpr std::size_t prefix = 0;
static constexpr std::size_t block_width = 0;
using beta_type = Beta;
Beta if_true;
Beta if_false;
@ -239,6 +423,7 @@ struct cmp_at_pack
static constexpr bool is_cmp = true;
static constexpr cmp_kind kind = Kind;
static constexpr std::size_t prefix = N;
static constexpr std::size_t block_width = 0;
using beta_type = Beta;
Beta if_true;
Beta if_false;
@ -289,6 +474,228 @@ inline auto geq_at(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t
return cmp_at_pack<N, cmp_kind::geq, std::decay_t<Beta>>(std::move(t), std::move(f));
}
// ---------------------------------------------------------------------------
// Path paints. Same channel and same (if_true, if_false) scale as a comparison:
// the reconstructed value is if_false + (if_true − if_false) · unit(x).
// `unit` is the common-prefix length, the matched prefix, a mask, and so on.
// ---------------------------------------------------------------------------
template <typename T, typename = void>
struct spec_is_incremental : std::false_type {};
template <typename T>
struct spec_is_incremental<T, std::void_t<decltype(T::incremental)>>
: std::bool_constant<T::incremental> {};
template <typename T, typename = void>
struct spec_length_bits : std::integral_constant<std::size_t, 0> {};
template <typename T>
struct spec_length_bits<T, std::void_t<decltype(T::length_bits)>>
: std::integral_constant<std::size_t, T::length_bits> {};
template <cmp_kind Kind, typename Beta, std::size_t LengthBits = 0>
struct paint_pack
{
static constexpr bool is_cmp = true;
static constexpr cmp_kind kind = Kind;
static constexpr std::size_t prefix = 0;
static constexpr std::size_t block_width = 0;
static constexpr std::size_t length_bits = LengthBits;
static constexpr bool incremental = false;
using beta_type = Beta;
Beta if_true;
Beta if_false;
explicit paint_pack(Beta t, Beta f = detail::dcf_impl::default_false<Beta>())
: if_true{std::move(t)}, if_false{std::move(f)} { }
};
template <std::size_t N, cmp_kind Kind, typename Beta, std::size_t LengthBits = 0>
struct paint_at_pack
{
static constexpr bool is_cmp = true;
static constexpr cmp_kind kind = Kind;
static constexpr std::size_t prefix = N;
static constexpr std::size_t block_width = 0;
static constexpr std::size_t length_bits = LengthBits;
static constexpr bool incremental = false;
using beta_type = Beta;
Beta if_true;
Beta if_false;
explicit paint_at_pack(Beta t, Beta f = detail::dcf_impl::default_false<Beta>())
: if_true{std::move(t)}, if_false{std::move(f)} { }
};
template <typename Beta = uint64_t>
inline auto lcp(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_pack<cmp_kind::lcp, Beta>(std::move(t), std::move(f));
}
template <std::size_t N, typename Beta = uint64_t>
inline auto lcp_at(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_at_pack<N, cmp_kind::lcp, Beta>(std::move(t), std::move(f));
}
template <typename Beta = uint64_t>
inline auto common_prefix(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_pack<cmp_kind::prefix, Beta>(std::move(t), std::move(f));
}
template <std::size_t N, typename Beta = uint64_t>
inline auto common_prefix_at(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_at_pack<N, cmp_kind::prefix, Beta>(std::move(t), std::move(f));
}
template <typename Beta = uint64_t>
inline auto prefix_mask(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_pack<cmp_kind::mask, Beta>(std::move(t), std::move(f));
}
template <std::size_t N, typename Beta = uint64_t>
inline auto prefix_mask_at(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_at_pack<N, cmp_kind::mask, Beta>(std::move(t), std::move(f));
}
template <typename Beta = uint64_t>
inline auto diverge_one_hot(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_pack<cmp_kind::one_hot, Beta>(std::move(t), std::move(f));
}
template <std::size_t N, typename Beta = uint64_t>
inline auto diverge_one_hot_at(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_at_pack<N, cmp_kind::one_hot, Beta>(std::move(t), std::move(f));
}
template <typename Beta = uint64_t>
inline auto break_bit(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_pack<cmp_kind::break_bit, Beta>(std::move(t), std::move(f));
}
template <std::size_t N, typename Beta = uint64_t>
inline auto break_bit_at(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_at_pack<N, cmp_kind::break_bit, Beta>(std::move(t), std::move(f));
}
/// Low `LengthBits` hold the common-prefix length. Above them sits the
/// matched prefix packed into the low bits of the lane (`α >> (N − d)`).
template <std::size_t LengthBits = 8, typename Beta = uint64_t>
inline auto prefix_with_length(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_pack<cmp_kind::prefix_with_length, Beta, LengthBits>(
std::move(t), std::move(f));
}
template <std::size_t N, std::size_t LengthBits = 8, typename Beta = uint64_t>
inline auto prefix_with_length_at(Beta t = Beta{1},
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_at_pack<N, cmp_kind::prefix_with_length, Beta, LengthBits>(
std::move(t), std::move(f));
}
/// Arbitrary unit plant. `fn(matched, in_lane_prefix, leaf)` returns the β = 1
/// value of that sibling subtree (`leaf` is the full match, `matched == N`).
/// The result is scaled by `if_true − if_false` like the canned recipes.
template <typename Beta, typename Fn>
struct paint_fn_pack
{
static constexpr bool is_cmp = true;
static constexpr cmp_kind kind = cmp_kind::paint;
static constexpr std::size_t prefix = 0;
static constexpr std::size_t block_width = 0;
static constexpr std::size_t length_bits = 0;
static constexpr bool incremental = false;
using beta_type = Beta;
Beta if_true;
Beta if_false;
Fn fn;
paint_fn_pack(Beta t, Beta f, Fn g)
: if_true{std::move(t)}, if_false{std::move(f)}, fn{std::move(g)} { }
};
template <std::size_t N, typename Beta, typename Fn>
struct paint_fn_at_pack
{
static constexpr bool is_cmp = true;
static constexpr cmp_kind kind = cmp_kind::paint;
static constexpr std::size_t prefix = N;
static constexpr std::size_t block_width = 0;
static constexpr std::size_t length_bits = 0;
static constexpr bool incremental = false;
using beta_type = Beta;
Beta if_true;
Beta if_false;
Fn fn;
paint_fn_at_pack(Beta t, Beta f, Fn g)
: if_true{std::move(t)}, if_false{std::move(f)}, fn{std::move(g)} { }
};
template <typename Fn, typename Beta>
inline auto path_paint(Fn fn, Beta t,
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_fn_pack<std::decay_t<Beta>, std::decay_t<Fn>>(
std::move(t), std::move(f), std::move(fn));
}
template <typename Fn>
inline auto path_paint(Fn fn)
{
return path_paint(std::move(fn), uint64_t{1});
}
template <std::size_t N, typename Fn, typename Beta>
inline auto path_paint_at(Fn fn, Beta t,
Beta f = detail::dcf_impl::default_false<Beta>())
{
return paint_fn_at_pack<N, std::decay_t<Beta>, std::decay_t<Fn>>(
std::move(t), std::move(f), std::move(fn));
}
/// Incremental comparison: the same predicate, correct at every prefix length.
/// Evaluate the full point with `cmp`, and a prefix with `cmp_prefix<L>`.
template <typename Spec>
struct idcf_pack
{
static constexpr bool is_cmp = true;
static constexpr bool incremental = true;
static constexpr cmp_kind kind = Spec::kind;
static constexpr std::size_t prefix = Spec::prefix;
static constexpr std::size_t block_width = Spec::block_width;
static constexpr std::size_t length_bits = spec_length_bits<Spec>::value;
using beta_type = typename Spec::beta_type;
beta_type if_true;
beta_type if_false;
explicit idcf_pack(Spec spec)
: if_true{std::move(spec.if_true)}, if_false{std::move(spec.if_false)} { }
};
template <typename Spec>
inline auto idcf(Spec spec)
{
static_assert(is_paint_kind(Spec::kind) == false,
"idcf wraps lt/leq/gt/geq; path paints are already one full-domain value");
static_assert(Spec::block_width == 0,
"idcf uses the per-level path, not blocked checkpoints");
return idcf_pack<Spec>{std::move(spec)};
}
// ---------------------------------------------------------------------------
// Equality specs: eq / eq_at
// ---------------------------------------------------------------------------
@ -330,10 +737,54 @@ inline auto eq_at(Beta t, Beta f = detail::dcf_impl::default_false<std::decay_t<
return eq_at_pack<N, std::decay_t<Beta>>(std::move(t), std::move(f));
}
template <std::size_t BlockWidth, typename Spec>
struct block_width_pack
{
static_assert(BlockWidth >= 1, "block_width<B> needs B >= 1");
static_assert(Spec::block_width == 0, "comparison is already block_width");
static constexpr bool is_cmp = true;
static constexpr std::size_t block_width = BlockWidth;
static constexpr cmp_kind kind = Spec::kind;
static constexpr std::size_t prefix = Spec::prefix;
static constexpr bool incremental = spec_is_incremental<Spec>::value;
static constexpr std::size_t length_bits = spec_length_bits<Spec>::value;
using beta_type = typename Spec::beta_type;
beta_type if_true;
beta_type if_false;
explicit block_width_pack(Spec spec)
: if_true{std::move(spec.if_true)}, if_false{std::move(spec.if_false)} { }
};
template <std::size_t BlockWidth>
struct block_width_fn
{
template <typename Spec>
constexpr auto operator()(Spec spec) const
{
return block_width_pack<BlockWidth, Spec>{std::move(spec)};
}
};
template <std::size_t BlockWidth>
inline constexpr block_width_fn<BlockWidth> block_width{};
template <typename T> struct is_cmp_spec : std::false_type {};
template <cmp_kind K, typename B> struct is_cmp_spec<cmp_pack<K, B>> : std::true_type {};
template <std::size_t N, cmp_kind K, typename B>
struct is_cmp_spec<cmp_at_pack<N, K, B>> : std::true_type {};
template <cmp_kind K, typename B, std::size_t L>
struct is_cmp_spec<paint_pack<K, B, L>> : std::true_type {};
template <std::size_t N, cmp_kind K, typename B, std::size_t L>
struct is_cmp_spec<paint_at_pack<N, K, B, L>> : std::true_type {};
template <typename B, typename Fn>
struct is_cmp_spec<paint_fn_pack<B, Fn>> : std::true_type {};
template <std::size_t N, typename B, typename Fn>
struct is_cmp_spec<paint_fn_at_pack<N, B, Fn>> : std::true_type {};
template <typename Spec>
struct is_cmp_spec<idcf_pack<Spec>> : std::true_type {};
template <std::size_t B, typename Spec>
struct is_cmp_spec<block_width_pack<B, Spec>> : std::true_type {};
template <typename T>
inline constexpr bool is_cmp_spec_v = is_cmp_spec<T>::value;

View file

@ -1,11 +1,12 @@
/// @file dpf/doerner_shelat.hpp
/// @brief Doerner–Shelat generation of a dealer DPF key.
/// @details Two XOR shares of the point are walked level by level. Correction
/// words, advice bits, seeds, and leaves are the ones `make_dpf`
/// would emit for the XOR of those shares, the same roots, and the
/// same beaver coins. Beaver pads used to hide the path bit cancel
/// and are not part of the key. Pad randomness must not come from
/// `uniform_fill` if the beaver tape is being matched.
/// @details Two shares of the point are walked level by level — XOR shares by
/// default, or additive shares when tagged with `arith_input`.
/// Correction words, advice bits, seeds, and leaves are the ones
/// `make_dpf` would emit for the reconstructed point, the same roots,
/// and the same beaver coins. Beaver pads used to hide the path bit
/// cancel and are not part of the key. Pad randomness must not come
/// from `uniform_fill` if the beaver tape is being matched.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
/// see [LICENSE.md](@ref license) for details.
@ -28,6 +29,14 @@
namespace dpf
{
/// Tag: Doerner–Shelat / geneval takes additive shares of the point
/// (`x0 + x1` in the input ring). Default calls take XOR shares.
struct arith_input_t
{
};
inline constexpr arith_input_t arith_input{};
/// Roots and the Beaver-pad stream for one Doerner–Shelat generation.
/// `root` is called twice, same as `make_dpf`: party 0 clears the low bit of
/// the first sample, party 1 sets the low bit of the second.
@ -86,12 +95,14 @@ struct ds_and_shares
simde__m128i z1;
};
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
simde__m128i ds_xor(simde__m128i a, simde__m128i b) noexcept
{
return simde_mm_xor_si128(a, b);
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
simde__m128i ds_gate(uint8_t bit, simde__m128i block) noexcept
{
@ -128,6 +139,7 @@ ds_and_pads ds_sample_and(PadRng & pad)
return p;
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
simde__m128i ds_cw_share(simde__m128i L, simde__m128i R, uint8_t my_bit,
const ds_cw_party & mine, const ds_blind & their) noexcept
@ -144,6 +156,7 @@ simde__m128i ds_cw_share(simde__m128i L, simde__m128i R, uint8_t my_bit,
return out;
}
HEDLEY_NO_THROW
inline void ds_cw_blinds(const ds_cw_pads & p,
simde__m128i L0, simde__m128i R0, uint8_t bit0,
simde__m128i L1, simde__m128i R1, uint8_t bit1,
@ -155,6 +168,7 @@ inline void ds_cw_blinds(const ds_cw_pads & p,
b1.msg = ds_xor(ds_xor(L1, R1), p.p1.rand);
}
HEDLEY_NO_THROW
inline simde__m128i ds_cw_outs(const ds_cw_pads & p,
simde__m128i L0, simde__m128i R0, uint8_t bit0,
simde__m128i L1, simde__m128i R1, uint8_t bit1,
@ -165,6 +179,7 @@ inline simde__m128i ds_cw_outs(const ds_cw_pads & p,
ds_cw_share(L1, R1, bit1, p.p1, b0));
}
HEDLEY_NO_THROW
inline uint8_t ds_open_advice(simde__m128i L0, simde__m128i R0, uint8_t bit0,
simde__m128i L1, simde__m128i R1, uint8_t bit1) noexcept
{
@ -177,6 +192,7 @@ inline uint8_t ds_open_advice(simde__m128i L0, simde__m128i R0, uint8_t bit0,
return static_cast<uint8_t>((t1 << 1) | (t0 & 1u));
}
HEDLEY_NO_THROW
inline void ds_next_terms(simde__m128i L, simde__m128i R, uint8_t advice,
simde__m128i cw, uint8_t tpack, simde__m128i & M, simde__m128i & base) noexcept
{
@ -190,6 +206,7 @@ inline void ds_next_terms(simde__m128i L, simde__m128i R, uint8_t advice,
base = (advice & 1u) ? ds_xor(L, cw_base) : L;
}
HEDLEY_NO_THROW
inline ds_and_shares ds_and_open(const ds_and_pads & p, simde__m128i M,
uint8_t b_recv) noexcept
{
@ -202,6 +219,7 @@ inline ds_and_shares ds_and_open(const ds_and_pads & p, simde__m128i M,
return z;
}
HEDLEY_NO_THROW
inline simde__m128i ds_deliver(uint8_t b_exp, simde__m128i base, simde__m128i M,
const ds_and_shares & z) noexcept
{
@ -249,6 +267,11 @@ struct ds_cmp_gen_state
bool track_coeff = false;
uint64_t Va1 = 0;
uint64_t last_vcw_coeff = 0;
cmp_kind kind = cmp_kind::lt;
bool paint = false;
std::size_t length_bits = 0;
paint_callback paint_cb = nullptr;
const void * paint_ctx = nullptr;
};
/// Local joint simulation: today's `ds_cw_outs` / `ds_open_advice` / `ds_and_open`.
@ -288,6 +311,7 @@ struct local_cw_protocol
}
/// Open CW + advice only (AND pads stay in `blinds` for a later open).
HEDLEY_NO_THROW
std::pair<simde__m128i, uint8_t> open_cw(const ds_level_blinds & b) noexcept
{
return {ds_cw_outs(b.cwp, b.L0, b.R0, b.bit0, b.L1, b.R1, b.bit1,
@ -297,6 +321,7 @@ struct local_cw_protocol
/// Open the public value CW for this level (local: clear convert+make_value_cw).
/// MPC backends open additive shares of the same word.
HEDLEY_NO_THROW
uint64_t open_value_cw(const ds_level_blinds & b, uint8_t adv0, uint8_t adv1,
int ai, uint64_t & Va, uint64_t beta, uint64_t mask) noexcept
{
@ -304,6 +329,16 @@ struct local_cw_protocol
adv1, ai, Va, beta, mask);
}
/// Open a path-paint value CW. `plant` is the scaled lose-subtree constant.
HEDLEY_NO_THROW
uint64_t open_planted_cw(const ds_level_blinds & b, uint8_t adv0, uint8_t adv1,
int ai, uint64_t & Va, uint64_t plant, uint64_t mask) noexcept
{
return dcf_impl::make_value_cw_planted(b.L0, b.R0, b.L1, b.R1, adv0,
adv1, ai, Va, plant, mask);
}
HEDLEY_NO_THROW
ds_and_shares open_and(const ds_and_pads & p, simde__m128i M,
uint8_t b_recv) noexcept
{
@ -313,6 +348,7 @@ struct local_cw_protocol
/// Open the final comparison leaf CW. Wraps `make_final_cw` so the
/// Doerner–Shelat gen does not call it directly on reconstructed seeds;
/// an MPC backend would open additive shares of the same word.
HEDLEY_NO_THROW
uint64_t open_final_cw(simde__m128i s0, simde__m128i s1, uint8_t t1,
uint64_t Va, uint64_t mask, uint64_t on_path) noexcept
{
@ -323,18 +359,81 @@ struct local_cw_protocol
/// the shared root sampler so the blind matches the dealer's; an MPC
/// backend would instead pull a group-width element from the pad stream.
template <typename BlockSampler>
HEDLEY_NO_THROW
uint64_t sample_addend_blind(uint64_t mask, BlockSampler && sample) noexcept
{
return dcf_impl::sample_addend_blind(mask,
std::forward<BlockSampler>(sample));
}
/// Majority of three bits (next carry of a full adder).
HEDLEY_NO_THROW
static constexpr uint8_t majority(uint8_t a, uint8_t b, uint8_t c) noexcept
{
return static_cast<uint8_t>((a & b) | (a & c) | (b & c));
}
/// One additive digit: sum bit `a XOR b XOR cin`, carry out = majority.
HEDLEY_NO_THROW
static constexpr uint8_t open_sum_bit(uint8_t a, uint8_t b, uint8_t cin,
uint8_t & cout) noexcept
{
cout = majority(a, b, cin);
return static_cast<uint8_t>(a ^ b ^ cin);
}
/// Reconstruct `a0 + a1` via a LSB→MSB carry chain, then flip the MSB
/// when the domain is signed — matching `make_dpf` on the sum. The call
/// site never forms the sum; an MPC backend would open the same bits.
template <typename InputT>
InputT open_arith_point(InputT a0, InputT a1) const
{
constexpr auto to_int = utils::to_integral_type<InputT>{};
using FromI = typename utils::make_from_integral_value<InputT>::integral_type;
using U = std::make_unsigned_t<FromI>;
const U u0 = static_cast<U>(to_int(a0));
const U u1 = static_cast<U>(to_int(a1));
U sum = 0;
uint8_t carry = 0;
constexpr std::size_t nbits = utils::bitlength_of_v<InputT>;
for (std::size_t i = 0; i < nbits; ++i)
{
const uint8_t b0 = static_cast<uint8_t>((u0 >> i) & U{1});
const uint8_t b1 = static_cast<uint8_t>((u1 >> i) & U{1});
const uint8_t s = open_sum_bit(b0, b1, carry, carry);
sum = static_cast<U>(sum | (static_cast<U>(s) << i));
}
InputT out = utils::make_from_integral_value<InputT>{}(
static_cast<FromI>(sum));
utils::flip_msb_if_signed_integral(out);
return out;
}
/// Encode shares for the XOR-style CW walk. XOR mode flips party 0's MSB
/// (linear over XOR). Arithmetic mode opens the sum (carry + signed MSB)
/// and returns `(alpha, 0)` so the walk matches `make_dpf(alpha)`.
template <typename InputT>
void encode_walk_shares(InputT & x0, InputT & x1, bool arith) const
{
if (arith)
{
const InputT alpha = open_arith_point(x0, x1);
x0 = alpha;
x1 = InputT{};
}
else
{
utils::flip_msb_if_signed_integral(x0);
}
}
/// Open a group of leaf correction words for one prefix group. In this
/// local joint simulation both XOR shares of the point are present, so the
/// point is reconstructed *inside* the protocol and handed to `leaf_fn`
/// (which runs `make_leaves` for the group). The Doerner–Shelat gen never
/// forms `x = x0 ^ x1` at its own call site; an MPC backend would instead
/// run a per-group leaf CW exchange that never reveals `x`.
/// run a per-group leaf CW exchange that never reveals `x`. After
/// `encode_walk_shares`, arithmetic inputs are already `(alpha, 0)`.
template <typename InputT, typename LeafFn>
void open_leaf_group(InputT x0, InputT x1, LeafFn && leaf_fn)
{
@ -351,6 +450,7 @@ struct ds_gen_state
NodeT root0;
NodeT root1;
HEDLEY_NO_THROW
void init(NodeT r0, NodeT r1) noexcept
{
root0 = r0;
@ -361,9 +461,13 @@ struct ds_gen_state
home[1] = 1;
}
HEDLEY_NO_THROW
NodeT & seed0() noexcept { return inbox[home[0]]; }
HEDLEY_NO_THROW
NodeT & seed1() noexcept { return inbox[home[1]]; }
HEDLEY_NO_THROW
const NodeT & seed0() const noexcept { return inbox[home[0]]; }
HEDLEY_NO_THROW
const NodeT & seed1() const noexcept { return inbox[home[1]]; }
};
@ -403,15 +507,37 @@ void ds_advance_level(ds_gen_state<NodeT> & st, InputT x0, InputT x1,
{
const int ai = static_cast<int>(
(cmp->thresh >> (cmp->nbits - 1 - level)) & 1);
*value_cw_out = proto.open_value_cw(blinds, adv0, adv1, ai, cmp->Va,
cmp->beta, cmp->mask);
if (cmp->track_coeff)
if (cmp->paint)
{
// Affine coefficient: same level with β = 1 on a parallel Va.
const uint64_t v1 = proto.open_value_cw(blinds, adv0, adv1, ai,
cmp->Va1, 1ULL, cmp->mask);
cmp->last_vcw_coeff =
(v1 + dcf_impl::neg_m(*value_cw_out, cmp->mask)) & cmp->mask;
const uint64_t unit = dcf_impl::paint_unit(cmp->kind, level,
cmp->thresh, cmp->nbits, cmp->length_bits, false,
cmp->paint_cb, cmp->paint_ctx);
const uint64_t plant = dcf_impl::scale_plant(unit, cmp->beta,
cmp->mask);
*value_cw_out = proto.open_planted_cw(blinds, adv0, adv1, ai,
cmp->Va, plant, cmp->mask);
if (cmp->track_coeff)
{
const uint64_t plant1 = dcf_impl::scale_plant(unit, 1ULL,
cmp->mask);
const uint64_t v1 = proto.open_planted_cw(blinds, adv0, adv1,
ai, cmp->Va1, plant1, cmp->mask);
cmp->last_vcw_coeff =
(v1 + dcf_impl::neg_m(*value_cw_out, cmp->mask)) & cmp->mask;
}
}
else
{
*value_cw_out = proto.open_value_cw(blinds, adv0, adv1, ai, cmp->Va,
cmp->beta, cmp->mask);
if (cmp->track_coeff)
{
// Affine coefficient: same level with β = 1 on a parallel Va.
const uint64_t v1 = proto.open_value_cw(blinds, adv0, adv1, ai,
cmp->Va1, 1ULL, cmp->mask);
cmp->last_vcw_coeff =
(v1 + dcf_impl::neg_m(*value_cw_out, cmp->mask)) & cmp->mask;
}
}
}
@ -475,14 +601,14 @@ template <typename InteriorPRG,
typename ...OutputTs,
typename RootSampler,
typename CwProtocol>
auto make_dpf_doerner_shelat_impl(InputT x0, InputT x1,
auto make_dpf_doerner_shelat_impl(bool arith, InputT x0, InputT x1,
RootSampler & root_sampler, CwProtocol & proto, OutputT && y,
OutputTs && ...ys)
{
static_assert(!dpf::is_wildcard_v<InputT>,
"Doerner–Shelat gen takes XOR shares of a concrete point");
"Doerner–Shelat gen takes shares of a concrete point");
static_assert(!dpf::is_secret_share_v<InputT>,
"Doerner–Shelat: pass additive_share of xor_wrapper, or raw XOR shares");
"Doerner–Shelat: pass additive_share of xor_wrapper, or raw shares");
static_assert(sizeof(typename InteriorPRG::block_type) == sizeof(simde__m128i),
"Doerner–Shelat gen uses the AES-block interior node");
@ -492,7 +618,7 @@ auto make_dpf_doerner_shelat_impl(InputT x0, InputT x1,
using input_type = typename dpf_type::input_type;
constexpr auto depth = dpf_type::depth;
utils::flip_msb_if_signed_integral(x0);
proto.encode_walk_shares(x0, x1, arith);
const node root0 = dpf::unset_lo_bit(static_cast<node>(root_sampler()));
const node root1 = dpf::set_lo_bit(static_cast<node>(root_sampler()));

View file

@ -150,6 +150,12 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
static constexpr std::size_t num_outputs = 1 + sizeof...(OutputTs);
static constexpr std::size_t cmp_depth = 0;
static constexpr std::size_t cmp_out_bits = 0;
static constexpr std::size_t cmp_block = 0;
static constexpr bool cmp_idcf = false;
static constexpr std::size_t cmp_q = 0;
static constexpr std::size_t cmp_h = 0;
static constexpr std::size_t cmp_checkpoints = 0;
static constexpr std::size_t cmp_tail = 0;
/// Classic keys are single-level; the unified eval surface keeps routing
/// them through the classic `eval_*` fast paths (see `is_multilevel_key`).
static constexpr bool is_multilevel = false;
@ -286,6 +292,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
return std::get<I>(leaf_nodes).beaver();
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
constexpr bool is_wildcard(std::size_t i) const noexcept
@ -442,41 +449,68 @@ namespace incr
/// / `cw_last` are affine in the payload δ, so after keygen with δ = 0 the
/// concrete values are `base[i] + coeff[i]·δ`; `assign_cmp` patches them in
/// place with no tree re-walk / re-PRG.
template <std::size_t Depth, typename ValueCwWord, bool Wild>
template <std::size_t Depth, typename ValueCwWord, bool Wild,
std::size_t TailLen = 0, bool Idcf = false>
struct cmp_wild_state { };
template <std::size_t Depth, typename ValueCwWord>
struct cmp_wild_state<Depth, ValueCwWord, true>
template <std::size_t Depth, typename ValueCwWord, std::size_t TailLen, bool Idcf>
struct cmp_wild_state<Depth, ValueCwWord, true, TailLen, Idcf>
{
std::array<ValueCwWord, Depth> value_cw_coeff{};
ValueCwWord cw_last_coeff{0};
std::array<ValueCwWord, TailLen> tail_coeff{};
std::array<ValueCwWord, Idcf ? Depth + 1 : 0> prefix_cw_coeff{};
bool assigned{false};
};
template <std::size_t Depth, typename ValueCwWord, bool Wild = false>
template <std::size_t Depth, typename ValueCwWord, bool Wild = false,
std::size_t TailLen = 0, bool Blocked = false, bool Idcf = false>
struct cmp_storage
{
using value_cw_word = ValueCwWord;
using value_cw_array = std::array<value_cw_word, Depth>;
using tail_array = std::array<value_cw_word, TailLen>;
static constexpr std::size_t prefix_cw_len = Idcf ? Depth + 1 : 0;
using prefix_cw_array = std::array<value_cw_word, prefix_cw_len>;
cmp_storage() = default;
cmp_storage(detail::cmp_meta cmp, value_cw_array value_cws,
value_cw_word cw_last_in, value_cw_word cmp_addend_in)
value_cw_word cw_last_in, value_cw_word cmp_addend_in,
tail_array tail = {}, tail_array tail_coeff = {},
prefix_cw_array prefix = {}, prefix_cw_array prefix_coeff = {})
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
cmp_addend_{cmp_addend_in} { }
cmp_addend_{cmp_addend_in}, tail_{tail}, prefix_cw_{prefix}
{
if constexpr (Wild && Blocked)
wild_.tail_coeff = tail_coeff;
else
(void)tail_coeff;
if constexpr (Wild && Idcf)
wild_.prefix_cw_coeff = prefix_coeff;
else
(void)prefix_coeff;
}
cmp_storage(detail::cmp_meta cmp, value_cw_array value_cws,
value_cw_word cw_last_in, value_cw_word cmp_addend_in,
value_cw_array coeff, value_cw_word cw_last_coeff)
value_cw_array coeff, value_cw_word cw_last_coeff,
tail_array tail = {}, tail_array tail_coeff = {},
prefix_cw_array prefix = {}, prefix_cw_array prefix_coeff = {})
: cmp_{cmp}, value_cw_{value_cws}, cw_last_{cw_last_in},
cmp_addend_{cmp_addend_in}
cmp_addend_{cmp_addend_in}, tail_{tail}, prefix_cw_{prefix}
{
if constexpr (Wild)
{
wild_.value_cw_coeff = coeff;
wild_.cw_last_coeff = cw_last_coeff;
if constexpr (Blocked)
wild_.tail_coeff = tail_coeff;
if constexpr (Idcf)
wild_.prefix_cw_coeff = prefix_coeff;
}
else
{
(void)coeff;
(void)cw_last_coeff;
(void)tail_coeff;
(void)prefix_coeff;
}
}
@ -485,15 +519,32 @@ struct cmp_storage
{
return static_cast<uint64_t>(value_cw_[level]);
}
HEDLEY_NO_THROW
uint64_t cw_last() const noexcept { return static_cast<uint64_t>(cw_last_); }
HEDLEY_NO_THROW
const tail_array & tail_cw() const noexcept { return tail_; }
uint64_t tail_cw(std::size_t i) const
{
return static_cast<uint64_t>(tail_[i]);
}
HEDLEY_NO_THROW
uint64_t cmp_addend() const noexcept
{
return static_cast<uint64_t>(cmp_addend_);
}
HEDLEY_NO_THROW
const detail::cmp_meta & cmp() const noexcept { return cmp_; }
HEDLEY_NO_THROW
bool has_cmp() const noexcept { return cmp_.active; }
HEDLEY_NO_THROW
const prefix_cw_array & prefix_cws() const noexcept { return prefix_cw_; }
uint64_t prefix_cw(std::size_t i) const
{
return static_cast<uint64_t>(prefix_cw_[i]);
}
static constexpr bool is_wildcard = Wild;
HEDLEY_NO_THROW
bool cmp_assigned() const noexcept
{
if constexpr (Wild)
@ -518,9 +569,27 @@ struct cmp_storage
const uint64_t c = static_cast<uint64_t>(wild_.value_cw_coeff[i]);
value_cw_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
}
if constexpr (Blocked)
{
for (std::size_t i = 0; i < TailLen; ++i)
{
const uint64_t base = static_cast<uint64_t>(tail_[i]);
const uint64_t c = static_cast<uint64_t>(wild_.tail_coeff[i]);
tail_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
}
}
const uint64_t lbase = static_cast<uint64_t>(cw_last_);
const uint64_t lc = static_cast<uint64_t>(wild_.cw_last_coeff);
cw_last_ = static_cast<value_cw_word>((lbase + lc * delta) & mask);
if constexpr (Idcf)
{
for (std::size_t i = 0; i < prefix_cw_len; ++i)
{
const uint64_t base = static_cast<uint64_t>(prefix_cw_[i]);
const uint64_t c = static_cast<uint64_t>(wild_.prefix_cw_coeff[i]);
prefix_cw_[i] = static_cast<value_cw_word>((base + c * delta) & mask);
}
}
cmp_addend_ = static_cast<value_cw_word>(addend_share & mask);
wild_.assigned = true;
}
@ -531,14 +600,17 @@ struct cmp_storage
value_cw_array value_cw_{};
value_cw_word cw_last_{0};
value_cw_word cmp_addend_{0};
cmp_wild_state<Depth, value_cw_word, Wild> wild_{};
tail_array tail_{};
prefix_cw_array prefix_cw_{};
cmp_wild_state<Depth, value_cw_word, Wild, TailLen, Idcf> wild_{};
};
/// Multi-level / comparison DPF key body. `PlacedTuple` is a tuple of
/// `placed<N, T>` slots; `CmpDepth > 0` activates the comparison channel.
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
typename PlacedTuple, std::size_t CmpDepth = 0,
std::size_t CmpOutBits = 0, bool CmpWild = false>
std::size_t CmpOutBits = 0, bool CmpWild = false,
std::size_t CmpBlock = 0, bool CmpIdcf = false>
struct incr_key_base
{
public:
@ -554,6 +626,26 @@ struct incr_key_base
static constexpr std::size_t cmp_out_bits = CmpOutBits;
/// True when the comparison payload is an unassigned wildcard.
static constexpr bool cmp_is_wildcard = CmpWild;
/// 0 = per-level path-sum. `B >= 1` = blocked checkpoints of width `B`.
static constexpr std::size_t cmp_block = CmpBlock;
static constexpr bool cmp_idcf = CmpIdcf;
static constexpr std::size_t max_output_level =
detail::incr::max_tree_level_v<node_type, PlacedTuple>;
/// Residual tail width. 2 only when dropping those levels does not cut an
/// output and the comparison itself is what sets the tree height.
static constexpr std::size_t cmp_q = [] {
if (CmpBlock == 0 || CmpDepth <= 2)
return std::size_t{0};
if (max_output_level > CmpDepth - 2)
return std::size_t{0};
return std::size_t{2};
}();
static constexpr std::size_t cmp_h =
(CmpBlock == 0) ? CmpDepth : (CmpDepth - cmp_q);
static constexpr std::size_t cmp_checkpoints =
(CmpBlock == 0 || cmp_h == 0) ? 0 : (cmp_h + CmpBlock - 1) / CmpBlock;
static constexpr std::size_t cmp_tail =
(CmpBlock == 0 || cmp_q == 0) ? 0 : (std::size_t{1} << cmp_q);
/// Multi-level / comparison keys route through the slot-aware eval path.
static constexpr bool is_multilevel = true;
/// Narrowest unsigned word that holds `cmp_out_bits` bits (1 byte for a
@ -564,8 +656,11 @@ struct incr_key_base
static constexpr std::size_t num_outputs = std::tuple_size_v<PlacedTuple>;
static constexpr std::size_t input_bits = utils::bitlength_of_v<input_type>;
static constexpr std::size_t depth = std::max(
detail::incr::max_tree_level_v<node_type, PlacedTuple>, CmpDepth);
static constexpr std::size_t depth = std::max(max_output_level,
(CmpBlock == 0) ? CmpDepth : cmp_h);
static constexpr std::size_t value_cw_len =
(CmpBlock == 0) ? depth
: (cmp_checkpoints == 0 ? std::size_t{1} : cmp_checkpoints);
static constexpr auto msb_mask = utils::msb_of_v<input_type>;
using integral_type = utils::integral_type_from_bitlength_t<
input_bits, utils::bitlength_of_v<std::size_t>>;
@ -577,7 +672,10 @@ struct incr_key_base
using correction_words_array = std::array<interior_node, depth>;
using correction_advice_array = std::array<psnip_uint8_t, depth>;
using value_cw_array = std::array<value_cw_word, depth>;
using value_cw_array = std::array<value_cw_word, value_cw_len>;
using tail_array = std::array<value_cw_word, cmp_tail>;
static constexpr std::size_t prefix_cw_len = CmpIdcf ? depth + 1 : 0;
using prefix_cw_array = std::array<value_cw_word, prefix_cw_len>;
using meta_array = std::array<detail::incr::slot_meta, num_outputs>;
static constexpr meta_array meta =
detail::incr::build_meta<node_type, PlacedTuple>();
@ -675,14 +773,17 @@ struct incr_key_base
detail::cmp_meta cmp = {}, value_cw_array value_cws = {},
uint64_t cw_last_in = 0, uint64_t cmp_addend_in = 0,
addend_tuple addends = {}, value_cw_array value_cw_coeff = {},
uint64_t cw_last_coeff_in = 0)
uint64_t cw_last_coeff_in = 0, tail_array tail_in = {},
tail_array tail_coeff_in = {}, prefix_cw_array prefix_in = {},
prefix_cw_array prefix_coeff_in = {})
: leaf_nodes{std::move(leaves)},
offset_x{offset_share},
cmp_store_{cmp, value_cws,
static_cast<value_cw_word>(cw_last_in),
static_cast<value_cw_word>(cmp_addend_in),
value_cw_coeff,
static_cast<value_cw_word>(cw_last_coeff_in)},
static_cast<value_cw_word>(cw_last_coeff_in),
tail_in, tail_coeff_in, prefix_in, prefix_coeff_in},
public_addends{std::move(addends)},
root_{root},
correction_words_{correction_words},
@ -706,10 +807,19 @@ struct incr_key_base
return correction_advice_;
}
const value_cw_array & value_cw() const { return cmp_store_.value_cw(); }
HEDLEY_NO_THROW
uint64_t cw_last() const noexcept { return cmp_store_.cw_last(); }
HEDLEY_NO_THROW
const prefix_cw_array & prefix_cws() const noexcept
{
return cmp_store_.prefix_cws();
}
uint64_t prefix_cw(std::size_t i) const { return cmp_store_.prefix_cw(i); }
/// Party-local share of the constant absorb (`if_false`, or
/// `δ + if_false` when `eval_as_ge`). Reconstructs with the peer share.
HEDLEY_NO_THROW
uint64_t cmp_addend() const noexcept { return cmp_store_.cmp_addend(); }
HEDLEY_NO_THROW
const detail::cmp_meta & cmp() const noexcept { return cmp_store_.cmp(); }
const digest_type & common_part_hash() const { return common_part_hash_; }
const leaf_wrapper_tuple & leaves() const { return leaf_nodes; }
@ -728,6 +838,8 @@ struct incr_key_base
(correction_advice_[level] >> direction) & 1);
}
uint64_t value_cw(std::size_t level) const { return cmp_store_.value_cw(level); }
const tail_array & tail_cw() const { return cmp_store_.tail_cw(); }
uint64_t tail_cw(std::size_t i) const { return cmp_store_.tail_cw(i); }
template <std::size_t I = 0>
const auto & leaf() const
@ -812,6 +924,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
}
template <std::size_t I = 0>
HEDLEY_NO_THROW
auto traverse_exterior(const interior_node & node) const noexcept
{
static_assert(num_outputs > 0, "cmp-only key has no exterior outputs");
@ -836,9 +949,11 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
/// Public `if_false` addends for `eq` / `eq_at` slots.
addend_tuple public_addends{};
HEDLEY_NO_THROW
bool has_cmp() const noexcept { return cmp_store_.has_cmp(); }
/// True once a wildcard comparison payload has been assigned (always true
/// for concrete cmp keys and for keys without a comparison channel).
HEDLEY_NO_THROW
bool cmp_assigned() const noexcept { return cmp_store_.cmp_assigned(); }
/// Patch the value CWs / `cw_last` for a resolved payload δ and install
@ -850,7 +965,9 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
}
private:
cmp_storage<depth, value_cw_word, CmpWild> cmp_store_{};
cmp_storage<value_cw_len, value_cw_word, CmpWild, cmp_tail, (CmpBlock > 0),
CmpIdcf>
cmp_store_{};
interior_node root_;
correction_words_array correction_words_;
correction_advice_array correction_advice_;
@ -890,7 +1007,13 @@ using dpf_key_base_t = std::conditional_t<
OutputT, OutputTs...>::cmp_out_bits,
dpf::detail::incr::normalize_pack<
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
OutputT, OutputTs...>::cmp_wild>>;
OutputT, OutputTs...>::cmp_wild,
dpf::detail::incr::normalize_pack<
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
OutputT, OutputTs...>::cmp_block,
dpf::detail::incr::normalize_pack<
utils::bitlength_of_v<dpf::concrete_type_t<InputT>>,
OutputT, OutputTs...>::cmp_idcf>>;
} // namespace detail
@ -919,39 +1042,40 @@ namespace incr
// expanding the placed slots into the output pack and appending the phantom
// cmp tag when a comparison channel is present.
template <std::size_t CmpDepth, std::size_t CmpOutBits, bool CmpWild,
typename InteriorPRG,
std::size_t CmpBlock, bool CmpIdcf, typename InteriorPRG,
typename ExteriorPRG, typename InputT, typename ...Ps>
struct assemble_key
{
using type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...,
dpf::cmp_channel_tag<CmpDepth, CmpOutBits, CmpWild>>;
dpf::cmp_channel_tag<CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>>;
};
template <std::size_t CmpOutBits, bool CmpWild, typename InteriorPRG,
template <std::size_t CmpOutBits, bool CmpWild, std::size_t CmpBlock,
bool CmpIdcf, typename InteriorPRG,
typename ExteriorPRG, typename InputT, typename ...Ps>
struct assemble_key<0, CmpOutBits, CmpWild, InteriorPRG, ExteriorPRG, InputT,
Ps...>
struct assemble_key<0, CmpOutBits, CmpWild, CmpBlock, CmpIdcf, InteriorPRG,
ExteriorPRG, InputT, Ps...>
{
using type = dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, Ps...>;
};
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
typename PlacedTuple, std::size_t CmpDepth, std::size_t CmpOutBits = 0,
bool CmpWild = false>
bool CmpWild = false, std::size_t CmpBlock = 0, bool CmpIdcf = false>
struct incr_dpf_key_of;
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
typename ...Ps, std::size_t CmpDepth, std::size_t CmpOutBits,
bool CmpWild>
bool CmpWild, std::size_t CmpBlock, bool CmpIdcf>
struct incr_dpf_key_of<InteriorPRG, ExteriorPRG, InputT, std::tuple<Ps...>,
CmpDepth, CmpOutBits, CmpWild>
CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>
{
using type = typename assemble_key<CmpDepth, CmpOutBits, CmpWild,
InteriorPRG, ExteriorPRG, InputT, Ps...>::type;
using type = typename assemble_key<CmpDepth, CmpOutBits, CmpWild, CmpBlock,
CmpIdcf, InteriorPRG, ExteriorPRG, InputT, Ps...>::type;
};
template <typename InteriorPRG, typename ExteriorPRG, typename InputT,
typename PlacedTuple, std::size_t CmpDepth, std::size_t CmpOutBits = 0,
bool CmpWild = false>
bool CmpWild = false, std::size_t CmpBlock = 0, bool CmpIdcf = false>
using incr_dpf_key_of_t = typename incr_dpf_key_of<InteriorPRG, ExteriorPRG,
InputT, PlacedTuple, CmpDepth, CmpOutBits, CmpWild>::type;
InputT, PlacedTuple, CmpDepth, CmpOutBits, CmpWild, CmpBlock, CmpIdcf>::type;
} // namespace incr
} // namespace detail

View file

@ -67,8 +67,10 @@ struct alignas(utils::max_align_v) dpf_output
subtractive_share<OutputT, Party>>;
dpf_output(const dpf_output &) = default;
HEDLEY_NO_THROW
dpf_output(dpf_output &&) noexcept = default;
dpf_output & operator=(const dpf_output &) = default;
HEDLEY_NO_THROW
dpf_output & operator=(dpf_output &&) noexcept = default;
~dpf_output() = default;
@ -156,6 +158,7 @@ auto make_eval_dpf_output(const Node & node, Input x)
/// Wrap a raw comparison `Beta` value as an additive share when `KeyT` is a
/// `party_key`.
template <typename KeyT, typename Beta>
HEDLEY_NO_THROW
auto make_eval_cmp_result(Beta raw) noexcept
{
if constexpr (is_party_key_v<KeyT>)

View file

@ -1,6 +1,8 @@
/// @file dpf/eval_full.hpp
/// @brief
/// @details
/// @brief Evaluate every input in the DPF domain.
/// @details Equivalent to `eval_interval` from
/// `std::numeric_limits<input_type>::min()` through `max()`.
/// @snippet evaluation/eval_full.cpp eval-full
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
@ -135,6 +137,7 @@ auto eval_full(const DpfKey & dpf,
return std::make_pair(std::move(outbufs), std::move(iterable));
}
/// Evaluate the whole domain, allocating a basic full memoizer and a buffer.
template <std::size_t I = 0,
std::size_t ...Is,
typename DpfKey,

View file

@ -331,7 +331,12 @@ void eval_prepare_nodes(const DpfKey & dpf, InputT from, InputT to,
integral_type from_node = utils::get_from_node<dpf_type>(from);
integral_type to_node = utils::get_to_node<dpf_type>(to);
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth);
constexpr auto to_int = utils::to_integral_type<InputT>{};
const bool wraps = utils::interval_wraps(
static_cast<integral_type>(to_int(from)),
static_cast<integral_type>(to_int(to)),
utils::bitlength_of_v<InputT>);
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth, wraps);
// The memoizer keeps one interval. A wrap is two intervals, and walking
// the first clobbers the second, so only a single segment can be cached.
if (segs.n == 1)
@ -359,7 +364,12 @@ auto eval_inner_product_impl(const DpfKey & dpf, InputT from, InputT to,
integral_type from_node = utils::get_from_node<dpf_type>(from);
integral_type to_node = utils::get_to_node<dpf_type>(to);
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth);
constexpr auto to_int = utils::to_integral_type<InputT>{};
const bool wraps = utils::interval_wraps(
static_cast<integral_type>(to_int(from)),
static_cast<integral_type>(to_int(to)),
utils::bitlength_of_v<InputT>);
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth, wraps);
auto accs = std::make_tuple(
ip_accum<typename DpfKey::concrete_output_type<Is>>{}...);

View file

@ -1,6 +1,10 @@
/// @file dpf/eval_interval.hpp
/// @brief
/// @details
/// @brief Evaluate every input in a closed interval.
/// @details `[from, to]` is inclusive. The returned iterable yields one
/// share per input, in that order. Pass a named output buffer;
/// this overload binds it as a non-const reference. An interval
/// memoizer is optional and comes after the buffer.
/// @snippet evaluation/eval_interval.cpp eval-interval
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
@ -339,7 +343,12 @@ auto eval_interval_impl(const DpfKey & dpf, InputT from, InputT to,
integral_type from_node = utils::get_from_node<dpf_type>(from),
to_node = utils::get_to_node<dpf_type>(to);
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth);
constexpr auto to_int = utils::to_integral_type<InputT>{};
const bool wraps = utils::interval_wraps(
static_cast<integral_type>(to_int(from)),
static_cast<integral_type>(to_int(to)),
utils::bitlength_of_v<InputT>);
auto segs = utils::split_leaf_nodes(from_node, to_node, dpf.depth, wraps);
auto idxs = std::index_sequence<IIs...>{};
std::size_t start = 0;
@ -388,6 +397,10 @@ auto eval_interval(const DpfKey & dpf, InputT from, InputT to,
} // namespace internal
/// Write outputs `I, Is...` for `[from, to]` into `outbufs`.
/// @param outbufs Named buffer, or a tuple of buffers when several outputs
/// are selected. Must outlive the returned iterable.
/// @param memoizer Workspace sized for at least this interval.
template <std::size_t I = 0,
std::size_t ...Is,
typename DpfKey,
@ -404,6 +417,7 @@ auto eval_interval(const DpfKey & dpf, InputT from, InputT to,
return internal::eval_interval<I, Is...>(dpf, dpf.offset_x(from), dpf.offset_x(to), outbufs, memoizer, std::make_index_sequence<1+sizeof...(Is)>());
}
/// Evaluate `[from, to]` into `outbufs`, allocating a basic interval memoizer.
template <std::size_t I = 0,
std::size_t ...Is,
typename DpfKey,
@ -421,6 +435,9 @@ auto eval_interval(const DpfKey & dpf, InputT from, InputT to,
dpf::make_basic_interval_memoizer<DpfKey>(from, to));
}
/// Evaluate `[from, to]` with a caller-supplied memoizer.
/// @return `std::pair` of a new buffer (or tuple of buffers) and an iterable
/// into that buffer.
template <std::size_t I = 0,
std::size_t ...Is,
typename DpfKey,
@ -445,6 +462,7 @@ auto eval_interval(const DpfKey & dpf, InputT from, InputT to,
return std::make_pair(std::move(outbufs), std::move(iterable));
}
/// Evaluate `[from, to]`, allocating a basic interval memoizer and a buffer.
template <std::size_t I = 0,
std::size_t ...Is,
typename DpfKey,

View file

@ -1,6 +1,11 @@
/// @file dpf/eval_point.hpp
/// @brief
/// @details
/// @brief Evaluate one DPF input.
/// @details `eval_point(key, x)` returns a handle; `*handle` is that party's
/// share of output 0. `eval_point<I>` selects another output.
/// `eval_point<I0, I1, ...>` returns a tuple of shares.
/// Pass a `basic_path_memoizer` lvalue to resume a previous path.
/// An unassigned wildcard output throws `std::runtime_error`.
/// @snippet evaluation/eval_point.cpp eval-point
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
@ -72,6 +77,10 @@ auto eval_point(const DpfKey & dpf, InputT && x, PathMemoizer && path)
} // namespace internal
/// Evaluate output `I` at `x`.
/// @param path Mutable path memoizer. The default is a fresh
/// nonmemoizing workspace for this call.
/// @return Handle whose `operator*` is the party's share.
template <std::size_t I = 0,
typename DpfKey,
typename InputT,
@ -88,6 +97,8 @@ auto eval_point(const DpfKey & dpf, InputT && x, PathMemoizer && path = PathMemo
internal::eval_point<I>(dpf, tx, path), tx);
}
/// Evaluate several outputs at `x`.
/// @return Tuple of shares, already dereferenced.
template <std::size_t I0,
std::size_t I1,
std::size_t ...Is,

View file

@ -1,6 +1,11 @@
/// @file dpf/eval_sequence.hpp
/// @brief
/// @details
/// @brief Evaluate a sorted list of DPF inputs.
/// @details The range is nondecreasing; an unsorted range throws
/// `std::runtime_error`. `return_output_only_tag_` stores one share
/// per point. `return_entire_node_tag_` stores whole leaves and is
/// the default. A `sequence_recipe` repeats the list, and a sequence
/// memoizer bound to that recipe object resumes the traversal.
/// @snippet evaluation/eval_sequence.cpp eval-sequence
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
@ -143,6 +148,9 @@ inline auto eval_sequence(const DpfKey & dpf, ForwardIterator begin, ForwardIter
}
}
/// Evaluate the sorted range `[begin, end)`, allocating a buffer.
/// @param return_type `return_entire_node_tag_{}` or `return_output_only_tag_{}`.
/// @return Pair of buffer (or tuple of buffers) and an iterable in list order.
template <std::size_t I = 0,
std::size_t ...Is,
typename DpfKey,
@ -446,6 +454,9 @@ auto eval_sequence(const DpfKey & dpf, const sequence_recipe & recipe,
} // namespace internal
/// Evaluate `recipe` into a named buffer, reusing `memoizer`.
/// @param recipe The same object `memoizer` was constructed from.
/// @param outbufs Named buffer. The returned iterable refers into it.
template <std::size_t I = 0,
std::size_t ...Is,
typename DpfKey,

View file

@ -35,6 +35,15 @@ struct cmp_t
};
inline constexpr cmp_t cmp{};
/// Prefix of an `idcf` comparison. `L` is the number of leading bits.
template <std::size_t L>
struct cmp_prefix_t
{
static constexpr std::size_t length = L;
};
template <std::size_t L>
inline constexpr cmp_prefix_t<L> cmp_prefix{};
template <typename T>
struct is_out : std::false_type
{
@ -53,10 +62,22 @@ struct is_cmp_target : std::bool_constant<std::is_same_v<std::decay_t<T>, cmp_t>
template <typename T>
inline constexpr bool is_cmp_target_v = is_cmp_target<T>::value;
template <typename T>
struct is_cmp_prefix_target : std::false_type
{
};
template <std::size_t L>
struct is_cmp_prefix_target<cmp_prefix_t<L>> : std::true_type
{
};
template <typename T>
inline constexpr bool is_cmp_prefix_target_v =
is_cmp_prefix_target<std::decay_t<T>>::value;
/// True for channel tags that must not bind as the key in classic eval_*.
template <typename T>
inline constexpr bool is_eval_channel_tag_v =
is_out_v<T> || is_cmp_target_v<T>;
is_out_v<T> || is_cmp_target_v<T> || is_cmp_prefix_target_v<T>;
template <typename T, typename = void>
struct looks_like_dpf_key : std::false_type

View file

@ -1,6 +1,11 @@
/// @file dpf/eval_unified.hpp
/// @brief Target-first eval surface for DPF / iDPF / DCF channels.
/// @details `eval_*(out<I>, …)` and `eval_*(cmp, …)` are the public API.
/// @details `eval_*(out<I>, …)` selects point-output slot `I`.
/// `eval_*(cmp, …)` selects the comparison channel. Memoizer and
/// buffer arguments match the classic overloads: a path memoizer
/// on `eval_point`, an output buffer then an interval memoizer on
/// `eval_interval`. `make_output_buffer(out<I>, key, from, to)` and
/// `make_output_buffer(cmp, key, n)` size the buffer for that channel.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
@ -38,6 +43,7 @@ namespace detail
{
template <std::size_t I, std::size_t N, typename KeyT>
HEDLEY_NO_THROW
constexpr std::size_t resolved_out_prefix() noexcept
{
if constexpr (is_multilevel_key_v<KeyT>)
@ -91,6 +97,15 @@ auto eval_point(cmp_t, const KeyT & key, QueryT && x,
std::forward<PathMemoizer>(path));
}
template <std::size_t L, typename Beta = uint64_t, typename KeyT, typename QueryT,
typename PathMemoizer = basic_path_memoizer<KeyT>>
auto eval_point(cmp_prefix_t<L>, const KeyT & key, QueryT && x,
PathMemoizer && path = PathMemoizer{})
{
return detail::incr::eval_cmp_prefix_point_impl<L, Beta>(key,
std::forward<QueryT>(x), std::forward<PathMemoizer>(path));
}
// ---------------------------------------------------------------------------
// eval_interval(target, key, from, to [, buf [, memo]])
// ---------------------------------------------------------------------------
@ -388,11 +403,12 @@ auto eval_out_inner_product_impl(const KeyT & dpf, LaneT from, LaneT to,
utils::flip_msb_if_signed_integral(from);
utils::flip_msb_if_signed_integral(to);
integral_type from_node = utils::leaf_node_floor(
static_cast<integral_type>(to_int(from)), lg_opl);
integral_type to_node = utils::leaf_node_ceil_exclusive(
static_cast<integral_type>(to_int(to)), lg_opl);
const auto segs = utils::split_leaf_nodes(from_node, to_node, to_level);
const auto from_i = static_cast<integral_type>(to_int(from));
const auto to_i = static_cast<integral_type>(to_int(to));
integral_type from_node = utils::leaf_node_floor(from_i, lg_opl);
integral_type to_node = utils::leaf_node_ceil_exclusive(to_i, lg_opl);
const bool wraps = utils::interval_wraps(from_i, to_i, N);
const auto segs = utils::split_leaf_nodes(from_node, to_node, to_level, wraps);
ml_ip_accum<output_type, exterior_node> acc{};
std::size_t start = 0;
@ -437,15 +453,27 @@ Beta eval_cmp_inner_product_impl(const KeyT & dpf, LaneT from, LaneT to,
constexpr std::size_t stop =
KeyT::cmp_depth == 0 ? KeyT::depth : KeyT::cmp_depth;
detail::incr::cmp_full_interval_memo<KeyT, stop> memo{count};
const std::size_t levels = unwrap_party_key_t<KeyT>::cmp_block > 0
? unwrap_party_key_t<KeyT>::cmp_h : nbits;
detail::incr::eval_cmp_interval_impl_interior(dpf, a, cmp_exclusive_end(b),
nbits, memo);
nbits, memo, levels);
uint64_t dot = 0;
for (std::size_t i = 0; i < count; ++i)
{
const auto q = static_cast<integral>(a + static_cast<integral>(i));
const uint64_t raw =
detail::incr::eval_cmp_from_interval_memo(dpf, q, a, nbits, memo);
const uint64_t raw = [&] {
if constexpr (unwrap_party_key_t<KeyT>::cmp_block > 0)
{
return detail::blocked::eval_share_memo(dpf, q, a,
cmp_exclusive_end(b), memo);
}
else
{
return detail::incr::eval_cmp_from_interval_memo(
dpf, q, a, nbits, memo);
}
}();
const uint64_t wt = static_cast<uint64_t>(weights[i]) & mask;
dot = (dot + ((raw & mask) * wt)) & mask;
}

View file

@ -10,10 +10,10 @@
/// nodes are identical across the two parties, so a dummy word
/// cancels.
///
/// A wildcard-input call takes additive shares of the real point and
/// a public query. It samples a random target, runs geneval there,
/// and shifts the query by `target - x`, which is what
/// `offset_x` does after a wildcard key is bound to `x`.
/// Default calls take XOR shares of the point. Tagged with
/// `arith_input`, the point is the ring sum of the two shares; path
/// bits are opened by a carry chain inside the local CW protocol so
/// the words match `make_dpf(x0 + x1)` at the caller's query.
///
/// `geneval_cmp` is the comparison-channel form. The value-correction
/// word is a function of the secret path at every level, so the walk
@ -50,13 +50,6 @@
namespace dpf
{
/// Tag for a geneval whose point is known only as additive shares.
struct wildcard_input_t
{
};
inline constexpr wildcard_input_t wildcard_input{};
/// Shares and the correction words opened along the query trie.
/// `correction_words[i]` / `correction_advice[i]` match a reusable key at
/// the same target for every `i < live_levels`. `leaf_live` means the
@ -78,6 +71,7 @@ namespace detail
template <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
T geneval_mod_add(T a, T b) noexcept
{
using U = std::make_unsigned_t<T>;
@ -87,17 +81,6 @@ T geneval_mod_add(T a, T b) noexcept
return out;
}
template <typename T>
HEDLEY_ALWAYS_INLINE
T geneval_mod_sub(T a, T b) noexcept
{
using U = std::make_unsigned_t<T>;
U diff = static_cast<U>(static_cast<U>(a) - static_cast<U>(b));
T out;
std::memcpy(&out, &diff, sizeof(out));
return out;
}
template <typename T>
T geneval_flipped(T x)
{
@ -151,8 +134,9 @@ template <typename InteriorPRG,
typename OutputT,
typename RootSampler,
typename PadRng>
auto geneval_run(InputT x0, InputT x1, const std::vector<InputT> & queries,
RootSampler & root_sampler, PadRng & pads, OutputT y)
auto geneval_run(bool arith, InputT x0, InputT x1,
const std::vector<InputT> & queries, RootSampler & root_sampler,
PadRng & pads, OutputT y)
{
static_assert(std::is_integral_v<InputT>,
"geneval input shares are an integral domain");
@ -172,9 +156,10 @@ auto geneval_run(InputT x0, InputT x1, const std::vector<InputT> & queries,
if (queries.size() > (std::size_t{1} << 22))
throw std::length_error("geneval query is too large");
local_cw_protocol<PadRng> proto{pads};
InputT x0c = x0;
InputT x1c = x1;
utils::flip_msb_if_signed_integral(x0c);
proto.encode_walk_shares(x0c, x1c, arith);
const InputT alpha = utils::xor_input_shares(x0c, x1c);
std::vector<InputT> flipped;
@ -196,7 +181,6 @@ auto geneval_run(InputT x0, InputT x1, const std::vector<InputT> & queries,
const uint64_t secret_leaf = geneval_leaf_id<dpf_type>(alpha);
local_cw_protocol<PadRng> proto{pads};
constexpr auto to_int = utils::to_integral_type<InputT>{};
const node root0 = dpf::unset_lo_bit(static_cast<node>(root_sampler()));
@ -342,6 +326,19 @@ auto geneval_run(InputT x0, InputT x1, const std::vector<InputT> & queries,
return result;
}
template <typename InteriorPRG,
typename ExteriorPRG,
typename InputT,
typename OutputT,
typename RootSampler,
typename PadRng>
auto geneval_run(InputT x0, InputT x1, const std::vector<InputT> & queries,
RootSampler & root_sampler, PadRng & pads, OutputT y)
{
return geneval_run<InteriorPRG, ExteriorPRG>(false, x0, x1, queries,
root_sampler, pads, y);
}
template <typename InputT>
InputT geneval_from_bits(uint64_t bits)
{
@ -401,22 +398,6 @@ std::vector<InputT> geneval_inclusive(InputT from, InputT to)
return qs;
}
template <typename InputT, typename TargetSampler>
InputT geneval_sample_target(TargetSampler & sample)
{
return static_cast<InputT>(sample());
}
template <typename InputT>
std::vector<InputT> geneval_shift_all(const std::vector<InputT> & qs, InputT delta)
{
std::vector<InputT> out;
out.reserve(qs.size());
for (const InputT & q : qs)
out.push_back(geneval_mod_add(q, delta));
return out;
}
} // namespace detail
/// Geneval at one public point. The secret point is `x0 XOR x1`.
@ -430,7 +411,22 @@ HEDLEY_WARN_UNUSED_RESULT
auto geneval_point(InputT x0, InputT x1, InputT query,
ds_randomness<RootSampler, PadRng> rng, OutputT y)
{
return detail::geneval_run<InteriorPRG, ExteriorPRG>(x0, x1,
return detail::geneval_run<InteriorPRG, ExteriorPRG>(false, x0, x1,
std::vector<InputT>{query}, rng.root, rng.pad, y);
}
/// Geneval at one public point. The secret point is `x0 + x1`.
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT,
typename RootSampler,
typename PadRng>
HEDLEY_WARN_UNUSED_RESULT
auto geneval_point(arith_input_t, InputT x0, InputT x1, InputT query,
ds_randomness<RootSampler, PadRng> rng, OutputT y)
{
return detail::geneval_run<InteriorPRG, ExteriorPRG>(true, x0, x1,
std::vector<InputT>{query}, rng.root, rng.pad, y);
}
@ -445,7 +441,21 @@ HEDLEY_WARN_UNUSED_RESULT
auto geneval_interval(InputT x0, InputT x1, InputT from, InputT to,
ds_randomness<RootSampler, PadRng> rng, OutputT y)
{
return detail::geneval_run<InteriorPRG, ExteriorPRG>(x0, x1,
return detail::geneval_run<InteriorPRG, ExteriorPRG>(false, x0, x1,
detail::geneval_inclusive(from, to), rng.root, rng.pad, y);
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT,
typename RootSampler,
typename PadRng>
HEDLEY_WARN_UNUSED_RESULT
auto geneval_interval(arith_input_t, InputT x0, InputT x1, InputT from,
InputT to, ds_randomness<RootSampler, PadRng> rng, OutputT y)
{
return detail::geneval_run<InteriorPRG, ExteriorPRG>(true, x0, x1,
detail::geneval_inclusive(from, to), rng.root, rng.pad, y);
}
@ -460,7 +470,21 @@ HEDLEY_WARN_UNUSED_RESULT
auto geneval_full(InputT x0, InputT x1,
ds_randomness<RootSampler, PadRng> rng, OutputT y)
{
return detail::geneval_run<InteriorPRG, ExteriorPRG>(x0, x1,
return detail::geneval_run<InteriorPRG, ExteriorPRG>(false, x0, x1,
detail::geneval_full_domain<InputT>(), rng.root, rng.pad, y);
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT,
typename RootSampler,
typename PadRng>
HEDLEY_WARN_UNUSED_RESULT
auto geneval_full(arith_input_t, InputT x0, InputT x1,
ds_randomness<RootSampler, PadRng> rng, OutputT y)
{
return detail::geneval_run<InteriorPRG, ExteriorPRG>(true, x0, x1,
detail::geneval_full_domain<InputT>(), rng.root, rng.pad, y);
}
@ -477,154 +501,10 @@ auto geneval_sequence(InputT x0, InputT x1, ForwardIterator begin,
ForwardIterator end, ds_randomness<RootSampler, PadRng> rng, OutputT y)
{
std::vector<InputT> qs(begin, end);
return detail::geneval_run<InteriorPRG, ExteriorPRG>(x0, x1,
return detail::geneval_run<InteriorPRG, ExteriorPRG>(false, x0, x1,
std::move(qs), rng.root, rng.pad, y);
}
/// Wildcard-input geneval. `x0 + x1` is the real point (additive shares).
/// `sample_target()` is the random DPF target; the public query is shifted
/// by `target - (x0 + x1)` before the walk.
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT,
typename RootSampler,
typename PadRng,
typename TargetSampler>
HEDLEY_WARN_UNUSED_RESULT
auto geneval_point(wildcard_input_t, InputT x0, InputT x1, InputT query,
ds_randomness<RootSampler, PadRng> rng, TargetSampler sample_target,
OutputT y)
{
const InputT alpha = detail::geneval_sample_target<InputT>(sample_target);
const InputT delta = detail::geneval_mod_sub(alpha,
detail::geneval_mod_add(x0, x1));
const InputT shifted = detail::geneval_mod_add(query, delta);
InputT zero{};
return detail::geneval_run<InteriorPRG, ExteriorPRG>(zero, alpha,
std::vector<InputT>{shifted}, rng.root, rng.pad, y);
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT,
typename RootSampler,
typename PadRng>
HEDLEY_WARN_UNUSED_RESULT
auto geneval_point(wildcard_input_t, InputT x0, InputT x1, InputT query,
ds_randomness<RootSampler, PadRng> rng, OutputT y)
{
return geneval_point<InteriorPRG, ExteriorPRG>(wildcard_input, x0, x1, query,
std::move(rng), [] { return dpf::uniform_sample<InputT>(); }, y);
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT,
typename RootSampler,
typename PadRng,
typename TargetSampler>
HEDLEY_WARN_UNUSED_RESULT
auto geneval_interval(wildcard_input_t, InputT x0, InputT x1, InputT from,
InputT to, ds_randomness<RootSampler, PadRng> rng, TargetSampler sample_target,
OutputT y)
{
const InputT alpha = detail::geneval_sample_target<InputT>(sample_target);
const InputT delta = detail::geneval_mod_sub(alpha,
detail::geneval_mod_add(x0, x1));
auto shifted = detail::geneval_shift_all(
detail::geneval_inclusive(from, to), delta);
InputT zero{};
return detail::geneval_run<InteriorPRG, ExteriorPRG>(zero, alpha,
std::move(shifted), rng.root, rng.pad, y);
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT,
typename RootSampler,
typename PadRng>
HEDLEY_WARN_UNUSED_RESULT
auto geneval_interval(wildcard_input_t, InputT x0, InputT x1, InputT from,
InputT to, ds_randomness<RootSampler, PadRng> rng, OutputT y)
{
return geneval_interval<InteriorPRG, ExteriorPRG>(wildcard_input, x0, x1,
from, to, std::move(rng), [] { return dpf::uniform_sample<InputT>(); }, y);
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT,
typename RootSampler,
typename PadRng,
typename TargetSampler>
HEDLEY_WARN_UNUSED_RESULT
auto geneval_full(wildcard_input_t, InputT x0, InputT x1,
ds_randomness<RootSampler, PadRng> rng, TargetSampler sample_target, OutputT y)
{
const InputT alpha = detail::geneval_sample_target<InputT>(sample_target);
const InputT delta = detail::geneval_mod_sub(alpha,
detail::geneval_mod_add(x0, x1));
InputT zero{};
auto full = detail::geneval_run<InteriorPRG, ExteriorPRG>(zero, alpha,
detail::geneval_full_domain<InputT>(), rng.root, rng.pad, y);
constexpr auto to_int = utils::to_integral_type<InputT>{};
const std::size_t n = full.party0.size();
std::vector<OutputT> p0(n), p1(n);
for (std::size_t i = 0; i < n; ++i)
{
InputT q = detail::geneval_from_bits<InputT>(i);
InputT s = detail::geneval_mod_add(q, delta);
const std::size_t si = static_cast<std::size_t>(to_int(s));
p0[i] = full.party0[si];
p1[i] = full.party1[si];
}
full.party0 = std::move(p0);
full.party1 = std::move(p1);
return full;
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT,
typename RootSampler,
typename PadRng>
HEDLEY_WARN_UNUSED_RESULT
auto geneval_full(wildcard_input_t, InputT x0, InputT x1,
ds_randomness<RootSampler, PadRng> rng, OutputT y)
{
return geneval_full<InteriorPRG, ExteriorPRG>(wildcard_input, x0, x1,
std::move(rng), [] { return dpf::uniform_sample<InputT>(); }, y);
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename OutputT,
typename RootSampler,
typename PadRng,
typename ForwardIterator,
typename TargetSampler>
HEDLEY_WARN_UNUSED_RESULT
auto geneval_sequence(wildcard_input_t, InputT x0, InputT x1,
ForwardIterator begin, ForwardIterator end,
ds_randomness<RootSampler, PadRng> rng, TargetSampler sample_target, OutputT y)
{
const InputT alpha = detail::geneval_sample_target<InputT>(sample_target);
const InputT delta = detail::geneval_mod_sub(alpha,
detail::geneval_mod_add(x0, x1));
std::vector<InputT> qs(begin, end);
auto shifted = detail::geneval_shift_all(qs, delta);
InputT zero{};
return detail::geneval_run<InteriorPRG, ExteriorPRG>(zero, alpha,
std::move(shifted), rng.root, rng.pad, y);
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
@ -633,12 +513,13 @@ template <typename InteriorPRG = dpf::prg::aes128,
typename PadRng,
typename ForwardIterator>
HEDLEY_WARN_UNUSED_RESULT
auto geneval_sequence(wildcard_input_t, InputT x0, InputT x1,
auto geneval_sequence(arith_input_t, InputT x0, InputT x1,
ForwardIterator begin, ForwardIterator end,
ds_randomness<RootSampler, PadRng> rng, OutputT y)
{
return geneval_sequence<InteriorPRG, ExteriorPRG>(wildcard_input, x0, x1,
begin, end, std::move(rng), [] { return dpf::uniform_sample<InputT>(); }, y);
std::vector<InputT> qs(begin, end);
return detail::geneval_run<InteriorPRG, ExteriorPRG>(true, x0, x1,
std::move(qs), rng.root, rng.pad, y);
}
/// Opened comparison key material and one prefix share per endpoint.
@ -651,6 +532,7 @@ struct geneval_cmp_result
std::vector<simde__m128i, aligned_allocator<simde__m128i>> correction_words;
std::vector<uint8_t> correction_advice;
std::vector<uint64_t> value_cw;
std::vector<uint64_t> tail_cw;
uint64_t cw_last = 0;
uint64_t addend0 = 0;
uint64_t addend1 = 0;
@ -689,12 +571,77 @@ geneval_cmp_result geneval_cmp(InputT x0, InputT x1,
out.addend1 = k1.cmp_addend().raw();
out.correction_words.resize(depth);
out.correction_advice.resize(depth);
out.value_cw.resize(depth);
if constexpr (key_type::cmp_block > 0)
{
out.value_cw.resize(key_type::cmp_checkpoints);
for (std::size_t i = 0; i < key_type::cmp_checkpoints; ++i)
out.value_cw[i] = k0.value_cw(i);
out.tail_cw.resize(key_type::cmp_tail);
for (std::size_t z = 0; z < key_type::cmp_tail; ++z)
out.tail_cw[z] = k0.tail_cw(z);
}
else
out.value_cw.resize(depth);
for (std::size_t level = 0; level < depth; ++level)
{
out.correction_words[level] = k0.correction_word(level);
out.correction_advice[level] = static_cast<uint8_t>(k0.correction_advice(level));
out.value_cw[level] = k0.value_cw(level);
if constexpr (key_type::cmp_block == 0)
out.value_cw[level] = k0.value_cw(level);
}
for (auto it = begin; it != end; ++it)
{
out.party0.push_back(eval_point(dpf::cmp, k0, *it).raw());
out.party1.push_back(eval_point(dpf::cmp, k1, *it).raw());
}
return out;
}
/// Comparison geneval with additive shares of the point (`x0 + x1`).
template <typename InputT,
typename ForwardIterator,
typename RootSampler,
typename PadRng,
typename Spec>
HEDLEY_WARN_UNUSED_RESULT
geneval_cmp_result geneval_cmp(arith_input_t, InputT x0, InputT x1,
ForwardIterator begin, ForwardIterator end,
ds_randomness<RootSampler, PadRng> rng, Spec spec)
{
geneval_cmp_result out;
if (begin == end)
return out;
auto keys = make_dpf_doerner_shelat(arith_input, std::move(x0), std::move(x1),
std::move(rng), std::move(spec));
const auto & k0 = keys.first;
const auto & k1 = keys.second;
using key_type = std::decay_t<decltype(k0)>;
constexpr std::size_t depth = key_type::depth;
out.live_levels = depth;
out.mask = k0.cmp().mask;
out.cw_last = k0.cw_last();
out.addend0 = k0.cmp_addend().raw();
out.addend1 = k1.cmp_addend().raw();
out.correction_words.resize(depth);
out.correction_advice.resize(depth);
if constexpr (key_type::cmp_block > 0)
{
out.value_cw.resize(key_type::cmp_checkpoints);
for (std::size_t i = 0; i < key_type::cmp_checkpoints; ++i)
out.value_cw[i] = k0.value_cw(i);
out.tail_cw.resize(key_type::cmp_tail);
for (std::size_t z = 0; z < key_type::cmp_tail; ++z)
out.tail_cw[z] = k0.tail_cw(z);
}
else
out.value_cw.resize(depth);
for (std::size_t level = 0; level < depth; ++level)
{
out.correction_words[level] = k0.correction_word(level);
out.correction_advice[level] = static_cast<uint8_t>(k0.correction_advice(level));
if constexpr (key_type::cmp_block == 0)
out.value_cw[level] = k0.value_cw(level);
}
for (auto it = begin; it != end; ++it)
{
@ -718,6 +665,19 @@ geneval_cmp_result geneval_cmp(InputT x0, InputT x1,
std::move(rng), dpf::gt(beta));
}
template <typename InputT,
typename ForwardIterator,
typename RootSampler,
typename PadRng>
HEDLEY_WARN_UNUSED_RESULT
geneval_cmp_result geneval_cmp(arith_input_t, InputT x0, InputT x1,
ForwardIterator begin, ForwardIterator end,
ds_randomness<RootSampler, PadRng> rng, uint64_t beta)
{
return geneval_cmp(arith_input, std::move(x0), std::move(x1), begin, end,
std::move(rng), dpf::gt(beta));
}
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_GENEVAL_HPP__

File diff suppressed because it is too large Load diff

674
include/dpf/interval.hpp Normal file
View file

@ -0,0 +1,674 @@
/// @file dpf/interval.hpp
/// @brief Public-bound interval containment on one comparison key.
/// @details `make_dpf(r, ic(p, q, β))` hides the mask `r` and the payload `β`.
/// The bounds are public. Reconstruction is `β` when
/// `p ≤ (x − r) mod 2^n ≤ q`, and the false payload otherwise.
///
/// The key is one `lt` comparison at `γ = r − 1`, the Boyle–Chandran–
/// Gilboa–Gupta–Ishai–Kumar–Rathee reduction (EUROCRYPT 2021, Fig. 3).
/// Evaluation walks that key at the two public shifts of `x` and adds
/// a secret-shared correction. Seed corrections, advice bits, leaves,
/// and the path memoizer stay single-path.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
#ifndef LIBDPF_INCLUDE_DPF_INTERVAL_HPP__
#define LIBDPF_INCLUDE_DPF_INTERVAL_HPP__
#include <cstddef>
#include <cstdint>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "hedley/hedley.h"
#include "dpf/dcf.hpp"
#include "dpf/eval_unified.hpp"
#include "dpf/geneval.hpp"
#include "dpf/incremental.hpp"
#include "dpf/output_buffer.hpp"
#include "dpf/secret_share.hpp"
#include "dpf/utils.hpp"
namespace dpf
{
template <typename Beta>
struct ic_pack
{
static constexpr bool is_ic = true;
using beta_type = Beta;
uint64_t lo = 0;
uint64_t hi = 0;
Beta if_true{};
Beta if_false{};
};
/// Spec tag and factory. `dpf::ic(p, q, beta)` builds a pack;
/// `eval_point(dpf::ic, key, x)` evaluates it.
struct ic_fn
{
template <typename Lo, typename Hi, typename Beta>
HEDLEY_WARN_UNUSED_RESULT
ic_pack<std::decay_t<Beta>> operator()(Lo lo, Hi hi, Beta t,
Beta f = detail::dcf_impl::default_false<std::decay_t<Beta>>()) const
{
ic_pack<std::decay_t<Beta>> spec;
spec.lo = static_cast<uint64_t>(lo);
spec.hi = static_cast<uint64_t>(hi);
spec.if_true = std::move(t);
spec.if_false = std::move(f);
return spec;
}
};
inline constexpr ic_fn ic{};
template <typename T>
struct is_ic_key : std::false_type {};
template <std::size_t Party, typename Key, typename Input, typename Beta>
struct ic_key
{
static constexpr std::size_t party = Party;
static constexpr bool wildcard = Key::cmp_is_wildcard;
using input_type = Input;
using key_type = party_key<Party, Key>;
using beta_type = Beta;
key_type key;
uint64_t lo = 0;
uint64_t hi = 0;
uint64_t input_mask = 0;
uint64_t group_mask = 0;
/// Share of `δ`. Public `c_x ∈ {-1,0,1}` scales it locally.
uint64_t delta_share = 0;
/// Share of `δ · c_r + if_false`.
uint64_t cr_share = 0;
/// Wildcard only: shares of `1` and of `c_r`, scaled by `δ` in `assign_cmp`.
uint64_t delta_coeff = 0;
uint64_t cr_coeff = 0;
bool assigned = !wildcard;
ic_key(key_type k, uint64_t lo_in, uint64_t hi_in, uint64_t nmask,
uint64_t gmask, uint64_t dshare, uint64_t cshare, uint64_t dcoeff,
uint64_t ccoeff)
: key(std::move(k))
, lo(lo_in)
, hi(hi_in)
, input_mask(nmask)
, group_mask(gmask)
, delta_share(dshare)
, cr_share(cshare)
, delta_coeff(dcoeff)
, cr_coeff(ccoeff)
{}
};
template <std::size_t Party, typename Key, typename Input, typename Beta>
struct is_ic_key<ic_key<Party, Key, Input, Beta>> : std::true_type {};
template <typename T>
inline constexpr bool is_ic_key_v = is_ic_key<std::decay_t<T>>::value;
namespace detail
{
namespace ic_impl
{
template <typename Input>
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t input_mask_of() noexcept
{
constexpr auto n = utils::bitlength_of_v<Input>;
if constexpr (n >= 64)
return ~uint64_t{0};
else
return (uint64_t{1} << n) - 1ULL;
}
template <typename Input>
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t bits_of(Input x) noexcept
{
constexpr auto to_int = utils::to_integral_type<Input>{};
return static_cast<uint64_t>(to_int(x)) & input_mask_of<Input>();
}
template <typename Input>
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr Input input_from_bits(uint64_t u) noexcept
{
return static_cast<Input>(u & input_mask_of<Input>());
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t embed_small(int s, uint64_t mask) noexcept
{
if (s >= 0)
return static_cast<uint64_t>(s) & mask;
return dcf_impl::neg_m(static_cast<uint64_t>(-s), mask);
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t mul_mask(uint64_t a, uint64_t b, uint64_t mask) noexcept
{
return static_cast<uint64_t>(static_cast<unsigned __int128>(a) * b) & mask;
}
/// Dealer correction in Fig. 3, as an element of the payload group.
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t correction(uint64_t r, uint64_t p, uint64_t q,
uint64_t nmask, uint64_t gmask) noexcept
{
const uint64_t aq = (q + r) & nmask;
const uint64_t ap = (p + r) & nmask;
const uint64_t q0 = (q + 1ULL) & nmask;
const uint64_t aq0 = (q0 + r) & nmask;
const int s = (ap > aq ? 1 : 0) - (ap > p ? 1 : 0)
+ (aq0 > q0 ? 1 : 0) + (aq == nmask ? 1 : 0);
return embed_small(s, gmask);
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr int public_cx(uint64_t x, uint64_t p, uint64_t q, uint64_t nmask) noexcept
{
const uint64_t q0 = (q + 1ULL) & nmask;
return (x > p ? 1 : 0) - (x > q0 ? 1 : 0);
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t shift_p(uint64_t x, uint64_t p, uint64_t nmask) noexcept
{
return (x + (nmask - p)) & nmask;
}
HEDLEY_CONST
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr uint64_t shift_q0(uint64_t x, uint64_t q, uint64_t nmask) noexcept
{
const uint64_t q0 = (q + 1ULL) & nmask;
return (x + (nmask - q0)) & nmask;
}
template <typename T>
HEDLEY_PURE
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
uint64_t opened_u64(const T & v, uint64_t mask) noexcept
{
if constexpr (is_secret_share_v<std::decay_t<T>>)
return dcf_impl::beta_to_u64_simple(v.raw(), mask);
else
return dcf_impl::beta_to_u64_simple(v, mask);
}
inline void split_target(uint64_t target, uint64_t mask,
uint64_t & s0, uint64_t & s1)
{
const uint64_t blind = dcf_impl::sample_addend_blind(mask,
[] { return dpf::uniform_sample<simde__m128i>(); });
incr::split_cmp_addend(target, mask, blind, s0, s1);
}
template <typename Input>
void check_input()
{
static_assert(std::is_unsigned_v<Input> && !std::is_same_v<Input, bool>,
"ic: input type must be an unsigned integer of at most 64 bits");
static_assert(utils::bitlength_of_v<Input> <= 64,
"ic: input type must be an unsigned integer of at most 64 bits");
static_assert(utils::bitlength_of_v<Input> > 0,
"ic: input type must be an unsigned integer of at most 64 bits");
}
template <typename Input, typename Beta>
void check_bounds(const ic_pack<Beta> & spec)
{
const uint64_t nmask = input_mask_of<Input>();
if (spec.lo > nmask || spec.hi > nmask)
throw std::invalid_argument("ic: bound does not fit in the input domain");
if (spec.lo > spec.hi)
throw std::invalid_argument("ic: require lo <= hi (the interval does not wrap)");
}
template <typename Beta>
HEDLEY_NO_THROW
uint64_t group_mask_of() noexcept
{
using B = concrete_type_t<Beta>;
if constexpr (std::is_same_v<B, dpf::bit>)
return 1ULL;
else
return dcf_impl::default_mask_for_bits(utils::bitlength_of_v<B>);
}
template <std::size_t Party, typename Key, typename Input, typename Beta>
ic_key<Party, Key, Input, Beta> make_side(party_key<Party, Key> key,
uint64_t lo, uint64_t hi, uint64_t nmask, uint64_t gmask,
uint64_t delta_share, uint64_t cr_share,
uint64_t delta_coeff, uint64_t cr_coeff)
{
return ic_key<Party, Key, Input, Beta>(std::move(key), lo, hi, nmask, gmask,
delta_share, cr_share, delta_coeff, cr_coeff);
}
template <typename Input, typename Beta, typename Pair>
auto finish(uint64_t r_bits, const ic_pack<Beta> & spec, Pair && inner)
{
using in_type = std::decay_t<Input>;
using party0 = std::decay_t<decltype(inner.first)>;
using raw_key = typename party0::key_type;
using out_beta = concrete_type_t<Beta>;
const uint64_t nmask = input_mask_of<in_type>();
const uint64_t gmask = group_mask_of<Beta>();
constexpr bool wild = is_wildcard_v<Beta>;
uint64_t delta = 0;
uint64_t fval = 0;
if constexpr (!wild)
{
delta = dcf_impl::beta_delta_u64(spec.if_true, spec.if_false, gmask);
fval = dcf_impl::beta_to_u64_simple(spec.if_false, gmask);
}
const uint64_t cr = correction(r_bits, spec.lo, spec.hi, nmask, gmask);
uint64_t d0 = 0, d1 = 0, c0 = 0, c1 = 0;
uint64_t dc0 = 0, dc1 = 0, cc0 = 0, cc1 = 0;
if constexpr (wild)
{
split_target(1ULL & gmask, gmask, dc0, dc1);
split_target(cr, gmask, cc0, cc1);
}
else
{
split_target(delta, gmask, d0, d1);
const uint64_t absorb =
(mul_mask(delta, cr, gmask) + fval) & gmask;
split_target(absorb, gmask, c0, c1);
}
auto k0 = make_side<0, raw_key, in_type, out_beta>(std::move(inner.first),
spec.lo, spec.hi, nmask, gmask, d0, c0, dc0, cc0);
auto k1 = make_side<1, raw_key, in_type, out_beta>(std::move(inner.second),
spec.lo, spec.hi, nmask, gmask, d1, c1, dc1, cc1);
return std::make_pair(std::move(k0), std::move(k1));
}
template <typename Beta>
auto inner_lt(const ic_pack<Beta> & spec)
{
using B = std::decay_t<Beta>;
if constexpr (is_wildcard_v<B>)
return lt(spec.if_true, spec.if_false);
else
{
const uint64_t gmask = group_mask_of<B>();
const uint64_t delta =
dcf_impl::beta_delta_u64(spec.if_true, spec.if_false, gmask);
return lt(dcf_impl::u64_to_beta<B>(delta), dcf_impl::u64_to_beta<B>(0));
}
}
template <typename Input>
Input gamma_of(Input r)
{
const uint64_t nmask = input_mask_of<Input>();
const uint64_t ru = bits_of(r);
return input_from_bits<Input>((ru - 1ULL) & nmask);
}
template <typename IcKey, typename Query, typename Memo>
auto eval_one(const IcKey & k, Query && x, Memo & memo)
{
if (!k.assigned)
throw std::invalid_argument(
"ic eval: wildcard payload not assigned (call assign_cmp)");
using in_type = typename IcKey::input_type;
const uint64_t xu = bits_of(in_type(std::forward<Query>(x)));
const uint64_t xp = shift_p(xu, k.lo, k.input_mask);
const uint64_t xq = shift_q0(xu, k.hi, k.input_mask);
const uint64_t a = opened_u64(
eval_point(dpf::cmp, k.key, input_from_bits<in_type>(xp), memo),
k.group_mask);
const uint64_t b = opened_u64(
eval_point(dpf::cmp, k.key, input_from_bits<in_type>(xq), memo),
k.group_mask);
const int cx = public_cx(xu, k.lo, k.hi, k.input_mask);
uint64_t scaled = 0;
if (cx == 1)
scaled = k.delta_share & k.group_mask;
else if (cx == -1)
scaled = dcf_impl::neg_m(k.delta_share, k.group_mask);
const uint64_t y = (dcf_impl::neg_m(a, k.group_mask) + b + k.cr_share
+ scaled) & k.group_mask;
return make_eval_cmp_result<typename IcKey::key_type>(
dcf_impl::u64_to_beta<typename IcKey::beta_type>(y));
}
} // namespace ic_impl
} // namespace detail
/// Dealer key for public bounds `spec` and secret mask `r`.
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf(InputT && r, const ic_pack<Beta> & spec)
{
using input_type = std::decay_t<InputT>;
detail::ic_impl::check_input<input_type>();
detail::ic_impl::check_bounds<input_type>(spec);
const uint64_t r_bits = detail::ic_impl::bits_of(input_type(r));
const input_type gamma = detail::ic_impl::gamma_of(input_type(r));
auto inner = make_dpf<InteriorPRG, ExteriorPRG>(gamma,
detail::ic_impl::inner_lt(spec));
return detail::ic_impl::finish<input_type>(r_bits, spec, std::move(inner));
}
/// Doerner–Shelat key. `r0 XOR r1` is the secret mask.
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename RootSampler,
typename PadRng,
typename InputT,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf_doerner_shelat(InputT r0, InputT r1,
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
{
using input_type = std::decay_t<InputT>;
detail::ic_impl::check_input<input_type>();
detail::ic_impl::check_bounds<input_type>(spec);
const input_type r = utils::xor_input_shares(r0, r1);
const uint64_t r_bits = detail::ic_impl::bits_of(r);
const input_type gamma = detail::ic_impl::gamma_of(r);
const input_type g0 = r0;
const input_type g1 = utils::xor_input_shares(g0, gamma);
auto inner = make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(g0, g1,
std::move(rng), detail::ic_impl::inner_lt(spec));
return detail::ic_impl::finish<input_type>(r_bits, spec, std::move(inner));
}
/// Doerner–Shelat IC key. `r0 + r1` is the secret mask; γ = (r0 + r1) − 1.
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename RootSampler,
typename PadRng,
typename InputT,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf_doerner_shelat(arith_input_t, InputT r0, InputT r1,
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
{
using input_type = std::decay_t<InputT>;
detail::ic_impl::check_input<input_type>();
detail::ic_impl::check_bounds<input_type>(spec);
const uint64_t nmask = detail::ic_impl::input_mask_of<input_type>();
const uint64_t r_bits =
(detail::ic_impl::bits_of(r0) + detail::ic_impl::bits_of(r1)) & nmask;
const input_type r = detail::ic_impl::input_from_bits<input_type>(r_bits);
// Additive shares of γ = r − 1: (r0 − 1, r1).
const input_type g0 = detail::ic_impl::input_from_bits<input_type>(
(detail::ic_impl::bits_of(r0) - 1ULL) & nmask);
const input_type g1 = r1;
auto inner = make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(
arith_input, g0, g1, std::move(rng), detail::ic_impl::inner_lt(spec));
return detail::ic_impl::finish<input_type>(
detail::ic_impl::bits_of(r), spec, std::move(inner));
}
/// Doerner–Shelat key sampled from the library entropy source.
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf_doerner_shelat(InputT r0, InputT r1, const ic_pack<Beta> & spec)
{
using block = typename InteriorPRG::block_type;
ds_randomness<block (*)(), detail::urandom_pad_rng> rng{
dpf::uniform_sample<block>, {}};
return make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(
std::move(r0), std::move(r1), rng, spec);
}
template <typename InteriorPRG = dpf::prg::aes128,
typename ExteriorPRG = InteriorPRG,
typename InputT,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
auto make_dpf_doerner_shelat(arith_input_t, InputT r0, InputT r1,
const ic_pack<Beta> & spec)
{
using block = typename InteriorPRG::block_type;
ds_randomness<block (*)(), detail::urandom_pad_rng> rng{
dpf::uniform_sample<block>, {}};
return make_dpf_doerner_shelat<InteriorPRG, ExteriorPRG>(
arith_input, std::move(r0), std::move(r1), rng, spec);
}
/// Open a wildcard interval payload onto an existing key pair.
template <typename Key, typename Input, typename Beta, typename Payload>
void assign_cmp(ic_key<0, Key, Input, Beta> & k0,
ic_key<1, Key, Input, Beta> & k1, const Payload & if_true,
const Payload & if_false = Payload{})
{
static_assert(Key::cmp_is_wildcard,
"assign_cmp: interval payload is not a wildcard");
const uint64_t mask = k0.group_mask;
const uint64_t delta =
detail::dcf_impl::beta_delta_u64(if_true, if_false, mask);
const uint64_t fval =
detail::dcf_impl::beta_to_u64_simple(if_false, mask);
assign_cmp(k0.key, k1.key,
detail::dcf_impl::u64_to_beta<Beta>(delta),
detail::dcf_impl::u64_to_beta<Beta>(0));
k0.delta_share = detail::ic_impl::mul_mask(k0.delta_coeff, delta, mask);
k1.delta_share = detail::ic_impl::mul_mask(k1.delta_coeff, delta, mask);
k0.cr_share = detail::ic_impl::mul_mask(k0.cr_coeff, delta, mask);
k1.cr_share = detail::ic_impl::mul_mask(k1.cr_coeff, delta, mask);
uint64_t f0 = 0, f1 = 0;
detail::ic_impl::split_target(fval, mask, f0, f1);
k0.cr_share = (k0.cr_share + f0) & mask;
k1.cr_share = (k1.cr_share + f1) & mask;
k0.assigned = true;
k1.assigned = true;
}
/// Point evaluation. `memo` is a path memoizer for the inner comparison key.
template <typename IcKey, typename Query,
typename Memo = basic_path_memoizer<typename IcKey::key_type>,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
HEDLEY_WARN_UNUSED_RESULT
auto eval_point(ic_fn, const IcKey & key, Query && x, Memo && memo = Memo{})
{
return detail::ic_impl::eval_one(key, std::forward<Query>(x), memo);
}
/// Inclusive interval `[from, to]` on the input domain.
template <typename IcKey, typename Lane, typename Buffer, typename Memo,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
void eval_interval(ic_fn, const IcKey & key, Lane from, Lane to,
Buffer && buf, Memo && memo)
{
using in_type = typename IcKey::input_type;
const uint64_t nmask = key.input_mask;
const uint64_t a = detail::ic_impl::bits_of(in_type(from));
const uint64_t b = detail::ic_impl::bits_of(in_type(to));
if (a > b)
throw std::invalid_argument("ic interval: to < from");
std::size_t i = 0;
for (uint64_t x = a;; ++x)
{
buf[i++] = detail::ic_impl::eval_one(key,
detail::ic_impl::input_from_bits<in_type>(x), memo);
if (x == b)
break;
if (x == nmask)
throw std::invalid_argument("ic interval: to < from");
}
}
template <typename IcKey, typename Lane, typename Buffer,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
void eval_interval(ic_fn, const IcKey & key, Lane from, Lane to, Buffer && buf)
{
basic_path_memoizer<typename IcKey::key_type> memo;
eval_interval(ic, key, from, to, std::forward<Buffer>(buf), memo);
}
/// Evaluate the points in `[begin, end)`.
template <typename IcKey, typename Iter, typename Buffer, typename Memo,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
void eval_sequence(ic_fn, const IcKey & key, Iter begin, Iter end,
Buffer && buf, Memo && memo)
{
std::size_t i = 0;
for (auto it = begin; it != end; ++it, ++i)
buf[i] = detail::ic_impl::eval_one(key, *it, memo);
}
template <typename IcKey, typename Iter, typename Buffer,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
void eval_sequence(ic_fn, const IcKey & key, Iter begin, Iter end, Buffer && buf)
{
basic_path_memoizer<typename IcKey::key_type> memo;
eval_sequence(ic, key, begin, end, std::forward<Buffer>(buf), memo);
}
/// Buffer of `n` interval shares.
template <typename IcKey, typename = std::enable_if_t<is_ic_key_v<IcKey>>>
HEDLEY_WARN_UNUSED_RESULT
auto make_output_buffer(ic_fn, const IcKey &, std::size_t n)
{
using beta = typename IcKey::beta_type;
using elem = cmp_buffer_elem_t<typename IcKey::key_type, beta>;
return output_buffer<elem>(n);
}
/// Buffer large enough for the inclusive interval `[from, to]`.
template <typename IcKey, typename Lane,
typename = std::enable_if_t<is_ic_key_v<IcKey>>>
HEDLEY_WARN_UNUSED_RESULT
auto make_output_buffer(ic_fn, const IcKey & key, Lane from, Lane to)
{
using in_type = typename IcKey::input_type;
const uint64_t a = detail::ic_impl::bits_of(in_type(from));
const uint64_t b = detail::ic_impl::bits_of(in_type(to));
if (a > b)
throw std::invalid_argument("ic interval: to < from");
const uint64_t n = b - a + 1ULL;
return make_output_buffer(ic, key, static_cast<std::size_t>(n));
}
/// Doerner–Shelat geneval. `r0 XOR r1` is the secret mask. Each query is
/// returned already combined into the interval share.
template <typename InputT, typename Iter, typename RootSampler, typename PadRng,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
geneval_cmp_result geneval_ic(InputT r0, InputT r1, Iter begin, Iter end,
ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
{
static_assert(!is_wildcard_v<Beta>,
"geneval_ic: payload must be concrete (assign_cmp is a separate step)");
geneval_cmp_result out;
if (begin == end)
return out;
auto keys = make_dpf_doerner_shelat(std::move(r0), std::move(r1),
std::move(rng), spec);
const auto & k0 = keys.first;
const auto & k1 = keys.second;
using key_type = unwrap_party_key_t<typename std::decay_t<decltype(k0)>::key_type>;
constexpr std::size_t depth = key_type::depth;
out.live_levels = depth;
out.mask = k0.key.cmp().mask;
out.cw_last = k0.key.cw_last();
out.addend0 = k0.key.cmp_addend().raw();
out.addend1 = k1.key.cmp_addend().raw();
out.correction_words.resize(depth);
out.correction_advice.resize(depth);
out.value_cw.resize(depth);
for (std::size_t level = 0; level < depth; ++level)
{
out.correction_words[level] = k0.key.correction_word(level);
out.correction_advice[level] =
static_cast<uint8_t>(k0.key.correction_advice(level));
out.value_cw[level] = k0.key.value_cw(level);
}
for (auto it = begin; it != end; ++it)
{
out.party0.push_back(detail::ic_impl::opened_u64(
eval_point(ic, k0, *it), out.mask));
out.party1.push_back(detail::ic_impl::opened_u64(
eval_point(ic, k1, *it), out.mask));
}
return out;
}
/// Additive-share geneval_ic. `r0 + r1` is the secret mask.
template <typename InputT, typename Iter, typename RootSampler, typename PadRng,
typename Beta>
HEDLEY_WARN_UNUSED_RESULT
geneval_cmp_result geneval_ic(arith_input_t, InputT r0, InputT r1, Iter begin,
Iter end, ds_randomness<RootSampler, PadRng> rng, const ic_pack<Beta> & spec)
{
static_assert(!is_wildcard_v<Beta>,
"geneval_ic: payload must be concrete (assign_cmp is a separate step)");
geneval_cmp_result out;
if (begin == end)
return out;
auto keys = make_dpf_doerner_shelat(arith_input, std::move(r0), std::move(r1),
std::move(rng), spec);
const auto & k0 = keys.first;
const auto & k1 = keys.second;
using key_type = unwrap_party_key_t<typename std::decay_t<decltype(k0)>::key_type>;
constexpr std::size_t depth = key_type::depth;
out.live_levels = depth;
out.mask = k0.key.cmp().mask;
out.cw_last = k0.key.cw_last();
out.addend0 = k0.key.cmp_addend().raw();
out.addend1 = k1.key.cmp_addend().raw();
out.correction_words.resize(depth);
out.correction_advice.resize(depth);
out.value_cw.resize(depth);
for (std::size_t level = 0; level < depth; ++level)
{
out.correction_words[level] = k0.key.correction_word(level);
out.correction_advice[level] =
static_cast<uint8_t>(k0.key.correction_advice(level));
out.value_cw[level] = k0.key.value_cw(level);
}
for (auto it = begin; it != end; ++it)
{
out.party0.push_back(detail::ic_impl::opened_u64(
eval_point(ic, k0, *it), out.mask));
out.party1.push_back(detail::ic_impl::opened_u64(
eval_point(ic, k1, *it), out.mask));
}
return out;
}
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_INTERVAL_HPP__

View file

@ -1,6 +1,13 @@
/// @file dpf/interval_memoizer.hpp
/// @brief
/// @details
/// @brief Workspaces for an inclusive interval of DPF leaves.
/// @details `basic_interval_memoizer` keeps two levels of the interval.
/// `full_tree_interval_memoizer` keeps every level. Size either one
/// for the widest interval you will evaluate; a wider interval
/// throws `std::length_error`. The same key and the same endpoints
/// leave the final interior level in place.
///
/// Factories unwrap `party_key`. Pass the memoizer as a mutable
/// lvalue to `eval_interval` or `eval_full`.
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
@ -55,10 +62,13 @@ struct interval_memoizer_base
// level 0 should access the root
// level goes up to (and including) depth
HEDLEY_NO_THROW
virtual return_type operator[](std::size_t) const noexcept = 0;
// iterators should access most recently completed level
HEDLEY_NO_THROW
virtual return_type begin() const noexcept = 0;
HEDLEY_NO_THROW
virtual return_type end() const noexcept = 0;
virtual std::size_t assign_interval(const dpf_type & dpf, integral_type new_from, integral_type new_to)
@ -151,6 +161,8 @@ struct interval_memoizer_base
std::optional<integral_type> to_;
};
/// Two-level workspace for one interval. This is what
/// `eval_interval(key, from, to)` allocates when you omit the memoizer.
template <typename DpfKey,
typename Allocator = aligned_allocator<
typename interval_memoizer_key_t<DpfKey>::interior_node>>
@ -229,6 +241,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
unique_ptr buf;
};
/// Every level of the interval. `retains_all_levels` is true.
template <typename DpfKey,
typename Allocator = aligned_allocator<
typename interval_memoizer_key_t<DpfKey>::interior_node>>
@ -392,6 +405,7 @@ struct basic_interval_memoizer_at
- utils::shift_right(from_node, offset) + 1;
}
HEDLEY_NO_THROW
return_type operator[](std::size_t level) const noexcept
{
bool b = (depth ^ level) & 1;
@ -420,9 +434,6 @@ auto make_interval_memoizer(InputT from, InputT to)
{
using dpf_type = DpfKey;
utils::flip_msb_if_signed_integral(from);
utils::flip_msb_if_signed_integral(to);
std::size_t nodes_in_interval = utils::get_leafnodes_in_output_interval<dpf_type>(from, to);
return MemoizerT(nodes_in_interval);
@ -430,6 +441,10 @@ auto make_interval_memoizer(InputT from, InputT to)
} // namespace detail
/// Two-level workspace sized for the closed interval `[from, to]`.
/// @param from Inclusive start, in the key's input domain.
/// @param to Inclusive end. `to` is at least `from` in that domain.
/// @snippet evaluation/memoizers.cpp interval-memoizer
template <typename DpfKey,
typename InputT>
inline auto make_basic_interval_memoizer(InputT from, InputT to)
@ -448,6 +463,7 @@ inline auto make_basic_interval_memoizer(const DpfKey &, InputT from, InputT to)
return make_basic_interval_memoizer<DpfKey>(from, to);
}
/// `make_basic_interval_memoizer` sized for the whole input domain.
template <typename DpfKey>
inline auto make_basic_full_memoizer()
{
@ -464,6 +480,7 @@ inline auto make_basic_full_memoizer(const DpfKey &)
return make_basic_full_memoizer<DpfKey>();
}
/// Full-tree workspace sized for the closed interval `[from, to]`.
template <typename DpfKey,
typename InputT>
inline auto make_full_tree_interval_memoizer(InputT from, InputT to)
@ -482,6 +499,7 @@ inline auto make_full_tree_interval_memoizer(const DpfKey &, InputT from, InputT
return make_full_tree_interval_memoizer<DpfKey>(from, to);
}
/// `make_full_tree_interval_memoizer` sized for the whole input domain.
template <typename DpfKey>
inline auto make_full_tree_full_memoizer()
{
@ -521,11 +539,13 @@ inline auto make_basic_interval_memoizer(InputT from, InputT to)
utils::flip_msb_if_signed_integral(from);
utils::flip_msb_if_signed_integral(to);
const integral_type from_node = utils::leaf_node_floor(
static_cast<integral_type>(to_int(from)), lg);
const integral_type to_node = utils::leaf_node_ceil_exclusive(
static_cast<integral_type>(to_int(to)), lg);
const auto segs = utils::split_leaf_nodes(from_node, to_node, stop);
const auto from_i = static_cast<integral_type>(to_int(from));
const auto to_i = static_cast<integral_type>(to_int(to));
const integral_type from_node = utils::leaf_node_floor(from_i, lg);
const integral_type to_node = utils::leaf_node_ceil_exclusive(to_i, lg);
const bool wraps = utils::interval_wraps(from_i, to_i,
utils::bitlength_of_v<InputT>);
const auto segs = utils::split_leaf_nodes(from_node, to_node, stop, wraps);
return basic_interval_memoizer_at<DpfKey, stop>(segs.total);
}

View file

@ -1,6 +1,7 @@
/// @file dpf/json.hpp
/// @brief
/// @details
/// @brief nlohmann::json serializers for DPF keys and beaver triples.
/// @details ADL `adl_serializer` specializations so `nlohmann::json` can
/// convert the library's key and triple types.
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
@ -92,6 +93,9 @@ struct adl_serializer<dpf::detail::cmp_meta>
j.at("eval_as_ge").get_to(c.eval_as_ge);
j.at("include_eq").get_to(c.include_eq);
j.at("active").get_to(c.active);
c.incremental = j.value("incremental", false);
c.block_width = j.value("block_width", 0);
c.tail_bits = j.value("tail_bits", 0);
}
static void to_json(nlohmann::json & j, const dpf::detail::cmp_meta & c) // NOLINT(runtime/references)
@ -105,6 +109,13 @@ struct adl_serializer<dpf::detail::cmp_meta>
{"include_eq", c.include_eq},
{"active", c.active}
};
if (c.incremental)
j["incremental"] = true;
if (c.block_width != 0)
{
j["block_width"] = c.block_width;
j["tail_bits"] = c.tail_bits;
}
}
};
@ -197,6 +208,12 @@ struct adl_serializer<dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, Ou
j.at("value_cw").get_to(value_cws);
uint64_t cw_last = j.at("cw_last").template get<uint64_t>();
uint64_t cmp_addend = j.at("cmp_addend").template get<uint64_t>();
typename dpf_type::tail_array tail{};
if constexpr (dpf_type::cmp_block > 0)
j.at("tail_cw").get_to(tail);
typename dpf_type::prefix_cw_array prefix{};
if constexpr (dpf_type::cmp_idcf)
j.at("prefix_cw").get_to(prefix);
typename dpf_type::leaf_wrapper_tuple leaves{};
input_type offset_share{};
@ -204,7 +221,7 @@ struct adl_serializer<dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, Ou
return dpf_type{root, correction_words, correction_advice,
std::move(leaves), offset_share, cmp, value_cws, cw_last,
cmp_addend, addends};
cmp_addend, addends, {}, 0, tail, {}, prefix};
}
static void to_json(nlohmann::json & j, const dpf_type & dpf) // NOLINT(runtime/references)
@ -221,6 +238,10 @@ struct adl_serializer<dpf::dpf_key<InteriorPRG, ExteriorPRG, InputT, OutputT, Ou
{"cw_last", static_cast<uint64_t>(dpf.cw_last())},
{"cmp_addend", static_cast<uint64_t>(dpf.cmp_addend())}
};
if constexpr (dpf_type::cmp_block > 0)
j["tail_cw"] = dpf.tail_cw();
if constexpr (dpf_type::cmp_idcf)
j["prefix_cw"] = dpf.prefix_cws();
}
};

View file

@ -174,11 +174,13 @@ class basic_fixed_length_string : public dpf::modint<static_cast<std::size_t>(st
/// @brief default constructor
/// @details Constructs the `basic_fixed_length_string` with a value
/// corresponding to the empty string.
HEDLEY_NO_THROW
constexpr basic_fixed_length_string() noexcept = default;
/// @brief copy constructor
/// @details Constructs the `basic_fixed_length_string` with a value
/// copied from another `basic_fixed_length_string`.
HEDLEY_NO_THROW
constexpr
basic_fixed_length_string(const basic_fixed_length_string &)
noexcept = default;
@ -186,6 +188,7 @@ class basic_fixed_length_string : public dpf::modint<static_cast<std::size_t>(st
/// @brief move constructor
/// @details Constructs the `basic_fixed_length_string` from another
/// `basic_fixed_length_string` using move semantics.
HEDLEY_NO_THROW
constexpr
basic_fixed_length_string(basic_fixed_length_string &&)
noexcept = default;
@ -232,6 +235,7 @@ class basic_fixed_length_string : public dpf::modint<static_cast<std::size_t>(st
/// @brief move assignment
/// @details Assigns the `basic_fixed_length_string` from another
/// `basic_fixed_length_string` using move semantics.
HEDLEY_NO_THROW
constexpr basic_fixed_length_string &
operator=(basic_fixed_length_string &&) noexcept = default;
@ -268,10 +272,12 @@ class basic_fixed_length_string : public dpf::modint<static_cast<std::size_t>(st
private:
constexpr
// cppcheck-suppress noExplicitConstructor
HEDLEY_NO_THROW
basic_fixed_length_string(integral_type val) // NOLINT(runtime/explicit)
noexcept
: parent::modint(val) { }
HEDLEY_NO_THROW
constexpr basic_fixed_length_string(parent val) noexcept
: parent::modint(val) { }
@ -448,6 +454,7 @@ struct make_from_integral_value<dpf::basic_fixed_length_string<MaxLen, CharT, Al
{
using T = dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits, Alloc>;
using integral_type = integral_type_from_bitlength_t<bitlength_of_v<T>>;
HEDLEY_NO_THROW
constexpr T operator()(integral_type val) const noexcept
{
return T{val};
@ -499,14 +506,23 @@ class numeric_limits<dpf::basic_fixed_length_string<MaxLen, CharT, Alpha, Traits
= std::numeric_limits<typename keyword_type::integral_type>::traps;
static constexpr bool tinyness_before = false;
HEDLEY_NO_THROW
static constexpr keyword_type min() noexcept { return keyword_type{""}; }
HEDLEY_NO_THROW
static constexpr keyword_type lowest() noexcept { return keyword_type{""}; }
HEDLEY_NO_THROW
static constexpr keyword_type max() noexcept { return ~keyword_type{""}; }
HEDLEY_NO_THROW
static constexpr keyword_type epsilon() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr keyword_type round_error() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr keyword_type infinity() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr keyword_type quiet_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr keyword_type signaling_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr keyword_type denorm_min() noexcept { return 0; }
};

View file

@ -45,6 +45,8 @@
#include <string_view>
#include <type_traits>
#include "hedley/hedley.h"
#include "dpf/modint.hpp"
#include "dpf/utils.hpp"
@ -1492,6 +1494,7 @@ constexpr hit match_full(const program & p, const char * s, std::size_t n)
return run_id(c, root, 0, u256{}, nullptr, 0, 0);
}
HEDLEY_NON_NULL(4)
constexpr int unrank_node(const program & p, std::uint16_t id, u256 rank, char * out, int n);
constexpr int unrank_node(const program & p, std::uint16_t id, u256 rank, char * out, int n)
@ -1590,12 +1593,14 @@ constexpr int unrank_node(const program & p, std::uint16_t id, u256 rank, char *
return n;
}
HEDLEY_NON_NULL(3)
constexpr int unrank_root(const program & p, u256 rank, char * out)
{
if (!p.lang.all && cmp_card_u(rank, p.lang) >= 0) return -1;
return unrank_node(p, p.tree.root, rank, out, 0);
}
HEDLEY_NON_NULL(1)
constexpr program compile_pattern(const char * pattern)
{
program p{};
@ -1719,6 +1724,7 @@ class keyword2
constexpr keyword2(std::string_view str)
: parent(encode_(str)) { }
HEDLEY_NON_NULL(1)
constexpr keyword2(const char * str)
: keyword2(std::string_view(str)) { }
@ -1731,7 +1737,7 @@ class keyword2
constexpr keyword2 & operator=(const keyword2 &) noexcept = default;
constexpr keyword2 & operator=(keyword2 &&) noexcept = default;
~keyword2() = default;
~keyword2() noexcept = default;
/// @brief Rank, including values outside the language that fill the bit width.
static constexpr keyword2 from_rank(integral_type rank) noexcept

View file

@ -81,6 +81,7 @@ static constexpr std::size_t block_length_of_leaf_v
template <typename OutputT,
typename NodeT,
typename InputT>
HEDLEY_NO_THROW
constexpr std::size_t offset_within_block(InputT x) noexcept
{
constexpr auto mod = utils::mod_pow_2<InputT>{};

View file

@ -1,3 +1,8 @@
/// @file dpf/literals.hpp
/// @brief Re-exports the user-defined literal namespaces.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
#ifndef LIBDPF_INCLUDE_DPF_LITERALS_HPP__
#define LIBDPF_INCLUDE_DPF_LITERALS_HPP__

View file

@ -57,18 +57,21 @@ class modint
/// @brief default constructor
/// @details Constructs a `modint` whose value is initialized to `0`.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr modint() noexcept = default;
/// @brief copy constructor
/// @details Constructs the `modint` with a value copied from another
/// `modint`.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr modint(const modint &) noexcept = default;
/// @brief move constructor
/// @details Constructs the `modint` from another `modint` using move
/// semantics.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr modint(modint &&) noexcept = default;
@ -91,12 +94,14 @@ class modint
/// @brief copy assignment
/// @details Assigns the `modint` with a value copied from another
/// `modint`.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr modint & operator=(const modint &) noexcept = default;
/// @brief move assignment
/// @details Assigns the `modint` from another `modint` using move
/// semantics.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr modint & operator=(modint &&) noexcept = default;
@ -278,7 +283,11 @@ class modint
HEDLEY_ALWAYS_INLINE
constexpr modint operator<<(std::size_t shift_amount) const noexcept
{
return modint{static_cast<integral_type>(this->val << shift_amount)};
if (shift_amount >= Nbits)
return modint{integral_type{0}};
// Reduce first: an unreduced limb shifted by less than Nbits can
// still leave the modulus, and a shift of the limb width is UB.
return modint{static_cast<integral_type>(this->reduced_value() << shift_amount)};
}
/// @brief bitwise-left-shift-assignment operator
@ -290,7 +299,10 @@ class modint
HEDLEY_ALWAYS_INLINE
constexpr modint & operator<<=(std::size_t shift_amount) noexcept
{
this->val <<= shift_amount;
if (shift_amount >= Nbits)
this->val = integral_type{0};
else
this->val = static_cast<integral_type>(this->reduced_value() << shift_amount);
return *this;
}
@ -304,6 +316,8 @@ class modint
HEDLEY_ALWAYS_INLINE
constexpr modint operator>>(std::size_t shift_amount) const noexcept
{
if (shift_amount >= Nbits)
return modint{integral_type{0}};
return modint{static_cast<integral_type>(this->reduced_value() >> shift_amount)};
}
@ -316,7 +330,10 @@ class modint
HEDLEY_ALWAYS_INLINE
constexpr modint & operator>>=(std::size_t shift_amount) noexcept
{
this->val = this->reduced_value() >> shift_amount;
if (shift_amount >= Nbits)
this->val = integral_type{0};
else
this->val = this->reduced_value() >> shift_amount;
return *this;
}
@ -603,6 +620,7 @@ class modint
template <std::size_t Nbits>
HEDLEY_CONST
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr modint<Nbits> operator*(typename modint<Nbits>::integral_type lhs,
modint<Nbits> rhs) noexcept
{
@ -616,6 +634,7 @@ constexpr modint<Nbits> operator*(typename modint<Nbits>::integral_type lhs,
template <std::size_t Nbits>
HEDLEY_CONST
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr bool operator<(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
{
return static_cast<typename modint<Nbits>::integral_type>(lhs)
@ -626,6 +645,7 @@ constexpr bool operator<(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
template <std::size_t Nbits>
HEDLEY_CONST
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr bool operator<=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
{
return static_cast<typename modint<Nbits>::integral_type>(lhs)
@ -636,6 +656,7 @@ constexpr bool operator<=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
template <std::size_t Nbits>
HEDLEY_CONST
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr bool operator>(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
{
return static_cast<typename modint<Nbits>::integral_type>(lhs)
@ -646,6 +667,7 @@ constexpr bool operator>(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
template <std::size_t Nbits>
HEDLEY_CONST
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr bool operator>=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
{
return static_cast<typename modint<Nbits>::integral_type>(lhs)
@ -656,6 +678,7 @@ constexpr bool operator>=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
template <std::size_t Nbits>
HEDLEY_CONST
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr bool operator==(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
{
return static_cast<typename modint<Nbits>::integral_type>(lhs)
@ -666,6 +689,7 @@ constexpr bool operator==(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
template <std::size_t Nbits>
HEDLEY_CONST
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr bool operator!=(modint<Nbits> lhs, modint<Nbits> rhs) noexcept
{
return static_cast<typename modint<Nbits>::integral_type>(lhs)
@ -695,6 +719,7 @@ template <std::size_t Nbits>
struct countl_zero_symmetric_difference<dpf::modint<Nbits>>
{
using T = dpf::modint<Nbits>;
HEDLEY_NO_THROW
HEDLEY_CONST
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(T lhs, T rhs) const noexcept
@ -731,6 +756,7 @@ struct mod_pow_2<dpf::modint<Nbits>>
{
using T = dpf::modint<Nbits>;
static constexpr auto mod = mod_pow_2<typename T::integral_type>{};
HEDLEY_NO_THROW
std::size_t operator()(T val, std::size_t n) const noexcept
{
return mod(val.val, n);
@ -1106,218 +1132,218 @@ constexpr static auto operator "" _u61(unsigned long long int x) { return dpf::m
constexpr static auto operator "" _u62(unsigned long long int x) { return dpf::modints::modint62_t{static_cast<psnip_uint64_t>(x)}; }
constexpr static auto operator "" _u63(unsigned long long int x) { return dpf::modints::modint63_t{static_cast<psnip_uint64_t>(x)}; }
constexpr static auto operator "" _u64(unsigned long long int x) { return dpf::modints::modint64_t{static_cast<psnip_uint64_t>(x)}; }
template <char ...digits> constexpr static auto operator "" _u65() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint65_t{x}; }
template <char ...digits> constexpr static auto operator "" _u66() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint66_t{x}; }
template <char ...digits> constexpr static auto operator "" _u67() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint67_t{x}; }
template <char ...digits> constexpr static auto operator "" _u68() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint68_t{x}; }
template <char ...digits> constexpr static auto operator "" _u69() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint69_t{x}; }
template <char ...digits> constexpr static auto operator "" _u65() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint65_t{x}; }
template <char ...digits> constexpr static auto operator "" _u66() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint66_t{x}; }
template <char ...digits> constexpr static auto operator "" _u67() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint67_t{x}; }
template <char ...digits> constexpr static auto operator "" _u68() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint68_t{x}; }
template <char ...digits> constexpr static auto operator "" _u69() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint69_t{x}; }
// 70--79
template <char ...digits> constexpr static auto operator "" _u70() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint70_t{x}; }
template <char ...digits> constexpr static auto operator "" _u71() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint71_t{x}; }
template <char ...digits> constexpr static auto operator "" _u72() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint72_t{x}; }
template <char ...digits> constexpr static auto operator "" _u73() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint73_t{x}; }
template <char ...digits> constexpr static auto operator "" _u74() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint74_t{x}; }
template <char ...digits> constexpr static auto operator "" _u75() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint75_t{x}; }
template <char ...digits> constexpr static auto operator "" _u76() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint76_t{x}; }
template <char ...digits> constexpr static auto operator "" _u77() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint77_t{x}; }
template <char ...digits> constexpr static auto operator "" _u78() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint78_t{x}; }
template <char ...digits> constexpr static auto operator "" _u79() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint79_t{x}; }
template <char ...digits> constexpr static auto operator "" _u70() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint70_t{x}; }
template <char ...digits> constexpr static auto operator "" _u71() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint71_t{x}; }
template <char ...digits> constexpr static auto operator "" _u72() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint72_t{x}; }
template <char ...digits> constexpr static auto operator "" _u73() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint73_t{x}; }
template <char ...digits> constexpr static auto operator "" _u74() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint74_t{x}; }
template <char ...digits> constexpr static auto operator "" _u75() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint75_t{x}; }
template <char ...digits> constexpr static auto operator "" _u76() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint76_t{x}; }
template <char ...digits> constexpr static auto operator "" _u77() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint77_t{x}; }
template <char ...digits> constexpr static auto operator "" _u78() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint78_t{x}; }
template <char ...digits> constexpr static auto operator "" _u79() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint79_t{x}; }
// 80--89
template <char ...digits> constexpr static auto operator "" _u80() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint80_t{x}; }
template <char ...digits> constexpr static auto operator "" _u81() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint81_t{x}; }
template <char ...digits> constexpr static auto operator "" _u82() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint82_t{x}; }
template <char ...digits> constexpr static auto operator "" _u83() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint83_t{x}; }
template <char ...digits> constexpr static auto operator "" _u84() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint84_t{x}; }
template <char ...digits> constexpr static auto operator "" _u85() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint85_t{x}; }
template <char ...digits> constexpr static auto operator "" _u86() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint86_t{x}; }
template <char ...digits> constexpr static auto operator "" _u87() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint87_t{x}; }
template <char ...digits> constexpr static auto operator "" _u88() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint88_t{x}; }
template <char ...digits> constexpr static auto operator "" _u89() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint89_t{x}; }
template <char ...digits> constexpr static auto operator "" _u80() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint80_t{x}; }
template <char ...digits> constexpr static auto operator "" _u81() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint81_t{x}; }
template <char ...digits> constexpr static auto operator "" _u82() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint82_t{x}; }
template <char ...digits> constexpr static auto operator "" _u83() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint83_t{x}; }
template <char ...digits> constexpr static auto operator "" _u84() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint84_t{x}; }
template <char ...digits> constexpr static auto operator "" _u85() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint85_t{x}; }
template <char ...digits> constexpr static auto operator "" _u86() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint86_t{x}; }
template <char ...digits> constexpr static auto operator "" _u87() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint87_t{x}; }
template <char ...digits> constexpr static auto operator "" _u88() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint88_t{x}; }
template <char ...digits> constexpr static auto operator "" _u89() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint89_t{x}; }
// 90--99
template <char ...digits> constexpr static auto operator "" _u90() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint90_t{x}; }
template <char ...digits> constexpr static auto operator "" _u91() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint91_t{x}; }
template <char ...digits> constexpr static auto operator "" _u92() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint92_t{x}; }
template <char ...digits> constexpr static auto operator "" _u93() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint93_t{x}; }
template <char ...digits> constexpr static auto operator "" _u94() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint94_t{x}; }
template <char ...digits> constexpr static auto operator "" _u95() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint95_t{x}; }
template <char ...digits> constexpr static auto operator "" _u96() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint96_t{x}; }
template <char ...digits> constexpr static auto operator "" _u97() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint97_t{x}; }
template <char ...digits> constexpr static auto operator "" _u98() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint98_t{x}; }
template <char ...digits> constexpr static auto operator "" _u99() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint99_t{x}; }
template <char ...digits> constexpr static auto operator "" _u90() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint90_t{x}; }
template <char ...digits> constexpr static auto operator "" _u91() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint91_t{x}; }
template <char ...digits> constexpr static auto operator "" _u92() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint92_t{x}; }
template <char ...digits> constexpr static auto operator "" _u93() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint93_t{x}; }
template <char ...digits> constexpr static auto operator "" _u94() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint94_t{x}; }
template <char ...digits> constexpr static auto operator "" _u95() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint95_t{x}; }
template <char ...digits> constexpr static auto operator "" _u96() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint96_t{x}; }
template <char ...digits> constexpr static auto operator "" _u97() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint97_t{x}; }
template <char ...digits> constexpr static auto operator "" _u98() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint98_t{x}; }
template <char ...digits> constexpr static auto operator "" _u99() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint99_t{x}; }
// 100--109
template <char ...digits> constexpr static auto operator "" _u100() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint100_t{x}; }
template <char ...digits> constexpr static auto operator "" _u101() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint101_t{x}; }
template <char ...digits> constexpr static auto operator "" _u102() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint102_t{x}; }
template <char ...digits> constexpr static auto operator "" _u103() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint103_t{x}; }
template <char ...digits> constexpr static auto operator "" _u104() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint104_t{x}; }
template <char ...digits> constexpr static auto operator "" _u105() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint105_t{x}; }
template <char ...digits> constexpr static auto operator "" _u106() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint106_t{x}; }
template <char ...digits> constexpr static auto operator "" _u107() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint107_t{x}; }
template <char ...digits> constexpr static auto operator "" _u108() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint108_t{x}; }
template <char ...digits> constexpr static auto operator "" _u109() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint109_t{x}; }
template <char ...digits> constexpr static auto operator "" _u100() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint100_t{x}; }
template <char ...digits> constexpr static auto operator "" _u101() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint101_t{x}; }
template <char ...digits> constexpr static auto operator "" _u102() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint102_t{x}; }
template <char ...digits> constexpr static auto operator "" _u103() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint103_t{x}; }
template <char ...digits> constexpr static auto operator "" _u104() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint104_t{x}; }
template <char ...digits> constexpr static auto operator "" _u105() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint105_t{x}; }
template <char ...digits> constexpr static auto operator "" _u106() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint106_t{x}; }
template <char ...digits> constexpr static auto operator "" _u107() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint107_t{x}; }
template <char ...digits> constexpr static auto operator "" _u108() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint108_t{x}; }
template <char ...digits> constexpr static auto operator "" _u109() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint109_t{x}; }
// 110--119
template <char ...digits> constexpr static auto operator "" _u110() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint110_t{x}; }
template <char ...digits> constexpr static auto operator "" _u111() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint111_t{x}; }
template <char ...digits> constexpr static auto operator "" _u112() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint112_t{x}; }
template <char ...digits> constexpr static auto operator "" _u113() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint113_t{x}; }
template <char ...digits> constexpr static auto operator "" _u114() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint114_t{x}; }
template <char ...digits> constexpr static auto operator "" _u115() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint115_t{x}; }
template <char ...digits> constexpr static auto operator "" _u116() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint116_t{x}; }
template <char ...digits> constexpr static auto operator "" _u117() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint117_t{x}; }
template <char ...digits> constexpr static auto operator "" _u118() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint118_t{x}; }
template <char ...digits> constexpr static auto operator "" _u119() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint119_t{x}; }
template <char ...digits> constexpr static auto operator "" _u110() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint110_t{x}; }
template <char ...digits> constexpr static auto operator "" _u111() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint111_t{x}; }
template <char ...digits> constexpr static auto operator "" _u112() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint112_t{x}; }
template <char ...digits> constexpr static auto operator "" _u113() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint113_t{x}; }
template <char ...digits> constexpr static auto operator "" _u114() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint114_t{x}; }
template <char ...digits> constexpr static auto operator "" _u115() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint115_t{x}; }
template <char ...digits> constexpr static auto operator "" _u116() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint116_t{x}; }
template <char ...digits> constexpr static auto operator "" _u117() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint117_t{x}; }
template <char ...digits> constexpr static auto operator "" _u118() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint118_t{x}; }
template <char ...digits> constexpr static auto operator "" _u119() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint119_t{x}; }
// 120--128
template <char ...digits> constexpr static auto operator "" _u120() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint120_t{x}; }
template <char ...digits> constexpr static auto operator "" _u121() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint121_t{x}; }
template <char ...digits> constexpr static auto operator "" _u122() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint122_t{x}; }
template <char ...digits> constexpr static auto operator "" _u123() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint123_t{x}; }
template <char ...digits> constexpr static auto operator "" _u124() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint124_t{x}; }
template <char ...digits> constexpr static auto operator "" _u125() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint125_t{x}; }
template <char ...digits> constexpr static auto operator "" _u126() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint126_t{x}; }
template <char ...digits> constexpr static auto operator "" _u127() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint127_t{x}; }
template <char ...digits> constexpr static auto operator "" _u128() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint128_t{x}; }
template <char ...digits> constexpr static auto operator "" _u129() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint129_t{x}; }
template <char ...digits> constexpr static auto operator "" _u120() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint120_t{x}; }
template <char ...digits> constexpr static auto operator "" _u121() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint121_t{x}; }
template <char ...digits> constexpr static auto operator "" _u122() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint122_t{x}; }
template <char ...digits> constexpr static auto operator "" _u123() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint123_t{x}; }
template <char ...digits> constexpr static auto operator "" _u124() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint124_t{x}; }
template <char ...digits> constexpr static auto operator "" _u125() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint125_t{x}; }
template <char ...digits> constexpr static auto operator "" _u126() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint126_t{x}; }
template <char ...digits> constexpr static auto operator "" _u127() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint127_t{x}; }
template <char ...digits> constexpr static auto operator "" _u128() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint128_t{x}; }
template <char ...digits> constexpr static auto operator "" _u129() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint129_t{x}; }
// 120--139
template <char ...digits> constexpr static auto operator "" _u130() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint130_t{x}; }
template <char ...digits> constexpr static auto operator "" _u131() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint131_t{x}; }
template <char ...digits> constexpr static auto operator "" _u132() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint132_t{x}; }
template <char ...digits> constexpr static auto operator "" _u133() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint133_t{x}; }
template <char ...digits> constexpr static auto operator "" _u134() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint134_t{x}; }
template <char ...digits> constexpr static auto operator "" _u135() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint135_t{x}; }
template <char ...digits> constexpr static auto operator "" _u136() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint136_t{x}; }
template <char ...digits> constexpr static auto operator "" _u137() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint137_t{x}; }
template <char ...digits> constexpr static auto operator "" _u138() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint138_t{x}; }
template <char ...digits> constexpr static auto operator "" _u139() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint139_t{x}; }
template <char ...digits> constexpr static auto operator "" _u130() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint130_t{x}; }
template <char ...digits> constexpr static auto operator "" _u131() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint131_t{x}; }
template <char ...digits> constexpr static auto operator "" _u132() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint132_t{x}; }
template <char ...digits> constexpr static auto operator "" _u133() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint133_t{x}; }
template <char ...digits> constexpr static auto operator "" _u134() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint134_t{x}; }
template <char ...digits> constexpr static auto operator "" _u135() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint135_t{x}; }
template <char ...digits> constexpr static auto operator "" _u136() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint136_t{x}; }
template <char ...digits> constexpr static auto operator "" _u137() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint137_t{x}; }
template <char ...digits> constexpr static auto operator "" _u138() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint138_t{x}; }
template <char ...digits> constexpr static auto operator "" _u139() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint139_t{x}; }
// 140--149
template <char ...digits> constexpr static auto operator "" _u140() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint140_t{x}; }
template <char ...digits> constexpr static auto operator "" _u141() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint141_t{x}; }
template <char ...digits> constexpr static auto operator "" _u142() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint142_t{x}; }
template <char ...digits> constexpr static auto operator "" _u143() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint143_t{x}; }
template <char ...digits> constexpr static auto operator "" _u144() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint144_t{x}; }
template <char ...digits> constexpr static auto operator "" _u145() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint145_t{x}; }
template <char ...digits> constexpr static auto operator "" _u146() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint146_t{x}; }
template <char ...digits> constexpr static auto operator "" _u147() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint147_t{x}; }
template <char ...digits> constexpr static auto operator "" _u148() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint148_t{x}; }
template <char ...digits> constexpr static auto operator "" _u149() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint149_t{x}; }
template <char ...digits> constexpr static auto operator "" _u140() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint140_t{x}; }
template <char ...digits> constexpr static auto operator "" _u141() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint141_t{x}; }
template <char ...digits> constexpr static auto operator "" _u142() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint142_t{x}; }
template <char ...digits> constexpr static auto operator "" _u143() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint143_t{x}; }
template <char ...digits> constexpr static auto operator "" _u144() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint144_t{x}; }
template <char ...digits> constexpr static auto operator "" _u145() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint145_t{x}; }
template <char ...digits> constexpr static auto operator "" _u146() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint146_t{x}; }
template <char ...digits> constexpr static auto operator "" _u147() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint147_t{x}; }
template <char ...digits> constexpr static auto operator "" _u148() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint148_t{x}; }
template <char ...digits> constexpr static auto operator "" _u149() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint149_t{x}; }
// 150--159
template <char ...digits> constexpr static auto operator "" _u150() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint150_t{x}; }
template <char ...digits> constexpr static auto operator "" _u151() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint151_t{x}; }
template <char ...digits> constexpr static auto operator "" _u152() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint152_t{x}; }
template <char ...digits> constexpr static auto operator "" _u153() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint153_t{x}; }
template <char ...digits> constexpr static auto operator "" _u154() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint154_t{x}; }
template <char ...digits> constexpr static auto operator "" _u155() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint155_t{x}; }
template <char ...digits> constexpr static auto operator "" _u156() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint156_t{x}; }
template <char ...digits> constexpr static auto operator "" _u157() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint157_t{x}; }
template <char ...digits> constexpr static auto operator "" _u158() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint158_t{x}; }
template <char ...digits> constexpr static auto operator "" _u159() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint159_t{x}; }
template <char ...digits> constexpr static auto operator "" _u150() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint150_t{x}; }
template <char ...digits> constexpr static auto operator "" _u151() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint151_t{x}; }
template <char ...digits> constexpr static auto operator "" _u152() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint152_t{x}; }
template <char ...digits> constexpr static auto operator "" _u153() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint153_t{x}; }
template <char ...digits> constexpr static auto operator "" _u154() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint154_t{x}; }
template <char ...digits> constexpr static auto operator "" _u155() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint155_t{x}; }
template <char ...digits> constexpr static auto operator "" _u156() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint156_t{x}; }
template <char ...digits> constexpr static auto operator "" _u157() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint157_t{x}; }
template <char ...digits> constexpr static auto operator "" _u158() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint158_t{x}; }
template <char ...digits> constexpr static auto operator "" _u159() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint159_t{x}; }
// 160--169
template <char ...digits> constexpr static auto operator "" _u160() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint160_t{x}; }
template <char ...digits> constexpr static auto operator "" _u161() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint161_t{x}; }
template <char ...digits> constexpr static auto operator "" _u162() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint162_t{x}; }
template <char ...digits> constexpr static auto operator "" _u163() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint163_t{x}; }
template <char ...digits> constexpr static auto operator "" _u164() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint164_t{x}; }
template <char ...digits> constexpr static auto operator "" _u165() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint165_t{x}; }
template <char ...digits> constexpr static auto operator "" _u166() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint166_t{x}; }
template <char ...digits> constexpr static auto operator "" _u167() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint167_t{x}; }
template <char ...digits> constexpr static auto operator "" _u168() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint168_t{x}; }
template <char ...digits> constexpr static auto operator "" _u169() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint169_t{x}; }
template <char ...digits> constexpr static auto operator "" _u160() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint160_t{x}; }
template <char ...digits> constexpr static auto operator "" _u161() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint161_t{x}; }
template <char ...digits> constexpr static auto operator "" _u162() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint162_t{x}; }
template <char ...digits> constexpr static auto operator "" _u163() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint163_t{x}; }
template <char ...digits> constexpr static auto operator "" _u164() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint164_t{x}; }
template <char ...digits> constexpr static auto operator "" _u165() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint165_t{x}; }
template <char ...digits> constexpr static auto operator "" _u166() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint166_t{x}; }
template <char ...digits> constexpr static auto operator "" _u167() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint167_t{x}; }
template <char ...digits> constexpr static auto operator "" _u168() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint168_t{x}; }
template <char ...digits> constexpr static auto operator "" _u169() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint169_t{x}; }
// 170-179
template <char ...digits> constexpr static auto operator "" _u170() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint170_t{x}; }
template <char ...digits> constexpr static auto operator "" _u171() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint171_t{x}; }
template <char ...digits> constexpr static auto operator "" _u172() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint172_t{x}; }
template <char ...digits> constexpr static auto operator "" _u173() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint173_t{x}; }
template <char ...digits> constexpr static auto operator "" _u174() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint174_t{x}; }
template <char ...digits> constexpr static auto operator "" _u175() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint175_t{x}; }
template <char ...digits> constexpr static auto operator "" _u176() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint176_t{x}; }
template <char ...digits> constexpr static auto operator "" _u177() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint177_t{x}; }
template <char ...digits> constexpr static auto operator "" _u178() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint178_t{x}; }
template <char ...digits> constexpr static auto operator "" _u179() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint179_t{x}; }
template <char ...digits> constexpr static auto operator "" _u170() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint170_t{x}; }
template <char ...digits> constexpr static auto operator "" _u171() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint171_t{x}; }
template <char ...digits> constexpr static auto operator "" _u172() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint172_t{x}; }
template <char ...digits> constexpr static auto operator "" _u173() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint173_t{x}; }
template <char ...digits> constexpr static auto operator "" _u174() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint174_t{x}; }
template <char ...digits> constexpr static auto operator "" _u175() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint175_t{x}; }
template <char ...digits> constexpr static auto operator "" _u176() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint176_t{x}; }
template <char ...digits> constexpr static auto operator "" _u177() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint177_t{x}; }
template <char ...digits> constexpr static auto operator "" _u178() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint178_t{x}; }
template <char ...digits> constexpr static auto operator "" _u179() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint179_t{x}; }
// 180--189
template <char ...digits> constexpr static auto operator "" _u180() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint180_t{x}; }
template <char ...digits> constexpr static auto operator "" _u181() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint181_t{x}; }
template <char ...digits> constexpr static auto operator "" _u182() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint182_t{x}; }
template <char ...digits> constexpr static auto operator "" _u183() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint183_t{x}; }
template <char ...digits> constexpr static auto operator "" _u184() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint184_t{x}; }
template <char ...digits> constexpr static auto operator "" _u185() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint185_t{x}; }
template <char ...digits> constexpr static auto operator "" _u186() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint186_t{x}; }
template <char ...digits> constexpr static auto operator "" _u187() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint187_t{x}; }
template <char ...digits> constexpr static auto operator "" _u188() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint188_t{x}; }
template <char ...digits> constexpr static auto operator "" _u189() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint189_t{x}; }
template <char ...digits> constexpr static auto operator "" _u180() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint180_t{x}; }
template <char ...digits> constexpr static auto operator "" _u181() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint181_t{x}; }
template <char ...digits> constexpr static auto operator "" _u182() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint182_t{x}; }
template <char ...digits> constexpr static auto operator "" _u183() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint183_t{x}; }
template <char ...digits> constexpr static auto operator "" _u184() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint184_t{x}; }
template <char ...digits> constexpr static auto operator "" _u185() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint185_t{x}; }
template <char ...digits> constexpr static auto operator "" _u186() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint186_t{x}; }
template <char ...digits> constexpr static auto operator "" _u187() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint187_t{x}; }
template <char ...digits> constexpr static auto operator "" _u188() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint188_t{x}; }
template <char ...digits> constexpr static auto operator "" _u189() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint189_t{x}; }
// 190--199
template <char ...digits> constexpr static auto operator "" _u190() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint190_t{x}; }
template <char ...digits> constexpr static auto operator "" _u191() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint191_t{x}; }
template <char ...digits> constexpr static auto operator "" _u192() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint192_t{x}; }
template <char ...digits> constexpr static auto operator "" _u193() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint193_t{x}; }
template <char ...digits> constexpr static auto operator "" _u194() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint194_t{x}; }
template <char ...digits> constexpr static auto operator "" _u195() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint195_t{x}; }
template <char ...digits> constexpr static auto operator "" _u196() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint196_t{x}; }
template <char ...digits> constexpr static auto operator "" _u197() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint197_t{x}; }
template <char ...digits> constexpr static auto operator "" _u198() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint198_t{x}; }
template <char ...digits> constexpr static auto operator "" _u199() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint199_t{x}; }
template <char ...digits> constexpr static auto operator "" _u190() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint190_t{x}; }
template <char ...digits> constexpr static auto operator "" _u191() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint191_t{x}; }
template <char ...digits> constexpr static auto operator "" _u192() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint192_t{x}; }
template <char ...digits> constexpr static auto operator "" _u193() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint193_t{x}; }
template <char ...digits> constexpr static auto operator "" _u194() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint194_t{x}; }
template <char ...digits> constexpr static auto operator "" _u195() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint195_t{x}; }
template <char ...digits> constexpr static auto operator "" _u196() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint196_t{x}; }
template <char ...digits> constexpr static auto operator "" _u197() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint197_t{x}; }
template <char ...digits> constexpr static auto operator "" _u198() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint198_t{x}; }
template <char ...digits> constexpr static auto operator "" _u199() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint199_t{x}; }
// 200--209
template <char ...digits> constexpr static auto operator "" _u200() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint200_t{x}; }
template <char ...digits> constexpr static auto operator "" _u201() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint201_t{x}; }
template <char ...digits> constexpr static auto operator "" _u202() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint202_t{x}; }
template <char ...digits> constexpr static auto operator "" _u203() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint203_t{x}; }
template <char ...digits> constexpr static auto operator "" _u204() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint204_t{x}; }
template <char ...digits> constexpr static auto operator "" _u205() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint205_t{x}; }
template <char ...digits> constexpr static auto operator "" _u206() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint206_t{x}; }
template <char ...digits> constexpr static auto operator "" _u207() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint207_t{x}; }
template <char ...digits> constexpr static auto operator "" _u208() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint208_t{x}; }
template <char ...digits> constexpr static auto operator "" _u209() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint209_t{x}; }
template <char ...digits> constexpr static auto operator "" _u200() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint200_t{x}; }
template <char ...digits> constexpr static auto operator "" _u201() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint201_t{x}; }
template <char ...digits> constexpr static auto operator "" _u202() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint202_t{x}; }
template <char ...digits> constexpr static auto operator "" _u203() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint203_t{x}; }
template <char ...digits> constexpr static auto operator "" _u204() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint204_t{x}; }
template <char ...digits> constexpr static auto operator "" _u205() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint205_t{x}; }
template <char ...digits> constexpr static auto operator "" _u206() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint206_t{x}; }
template <char ...digits> constexpr static auto operator "" _u207() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint207_t{x}; }
template <char ...digits> constexpr static auto operator "" _u208() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint208_t{x}; }
template <char ...digits> constexpr static auto operator "" _u209() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint209_t{x}; }
// 210--219
template <char ...digits> constexpr static auto operator "" _u210() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint210_t{x}; }
template <char ...digits> constexpr static auto operator "" _u211() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint211_t{x}; }
template <char ...digits> constexpr static auto operator "" _u212() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint212_t{x}; }
template <char ...digits> constexpr static auto operator "" _u213() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint213_t{x}; }
template <char ...digits> constexpr static auto operator "" _u214() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint214_t{x}; }
template <char ...digits> constexpr static auto operator "" _u215() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint215_t{x}; }
template <char ...digits> constexpr static auto operator "" _u216() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint216_t{x}; }
template <char ...digits> constexpr static auto operator "" _u217() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint217_t{x}; }
template <char ...digits> constexpr static auto operator "" _u218() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint218_t{x}; }
template <char ...digits> constexpr static auto operator "" _u219() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint219_t{x}; }
template <char ...digits> constexpr static auto operator "" _u210() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint210_t{x}; }
template <char ...digits> constexpr static auto operator "" _u211() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint211_t{x}; }
template <char ...digits> constexpr static auto operator "" _u212() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint212_t{x}; }
template <char ...digits> constexpr static auto operator "" _u213() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint213_t{x}; }
template <char ...digits> constexpr static auto operator "" _u214() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint214_t{x}; }
template <char ...digits> constexpr static auto operator "" _u215() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint215_t{x}; }
template <char ...digits> constexpr static auto operator "" _u216() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint216_t{x}; }
template <char ...digits> constexpr static auto operator "" _u217() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint217_t{x}; }
template <char ...digits> constexpr static auto operator "" _u218() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint218_t{x}; }
template <char ...digits> constexpr static auto operator "" _u219() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint219_t{x}; }
// 220--229
template <char ...digits> constexpr static auto operator "" _u220() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint220_t{x}; }
template <char ...digits> constexpr static auto operator "" _u221() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint221_t{x}; }
template <char ...digits> constexpr static auto operator "" _u222() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint222_t{x}; }
template <char ...digits> constexpr static auto operator "" _u223() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint223_t{x}; }
template <char ...digits> constexpr static auto operator "" _u224() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint224_t{x}; }
template <char ...digits> constexpr static auto operator "" _u225() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint225_t{x}; }
template <char ...digits> constexpr static auto operator "" _u226() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint226_t{x}; }
template <char ...digits> constexpr static auto operator "" _u227() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint227_t{x}; }
template <char ...digits> constexpr static auto operator "" _u228() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint228_t{x}; }
template <char ...digits> constexpr static auto operator "" _u229() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint229_t{x}; }
template <char ...digits> constexpr static auto operator "" _u220() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint220_t{x}; }
template <char ...digits> constexpr static auto operator "" _u221() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint221_t{x}; }
template <char ...digits> constexpr static auto operator "" _u222() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint222_t{x}; }
template <char ...digits> constexpr static auto operator "" _u223() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint223_t{x}; }
template <char ...digits> constexpr static auto operator "" _u224() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint224_t{x}; }
template <char ...digits> constexpr static auto operator "" _u225() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint225_t{x}; }
template <char ...digits> constexpr static auto operator "" _u226() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint226_t{x}; }
template <char ...digits> constexpr static auto operator "" _u227() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint227_t{x}; }
template <char ...digits> constexpr static auto operator "" _u228() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint228_t{x}; }
template <char ...digits> constexpr static auto operator "" _u229() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint229_t{x}; }
// 230--239
template <char ...digits> constexpr static auto operator "" _u230() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint230_t{x}; }
template <char ...digits> constexpr static auto operator "" _u231() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint231_t{x}; }
template <char ...digits> constexpr static auto operator "" _u232() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint232_t{x}; }
template <char ...digits> constexpr static auto operator "" _u233() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint233_t{x}; }
template <char ...digits> constexpr static auto operator "" _u234() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint234_t{x}; }
template <char ...digits> constexpr static auto operator "" _u235() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint235_t{x}; }
template <char ...digits> constexpr static auto operator "" _u236() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint236_t{x}; }
template <char ...digits> constexpr static auto operator "" _u237() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint237_t{x}; }
template <char ...digits> constexpr static auto operator "" _u238() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint238_t{x}; }
template <char ...digits> constexpr static auto operator "" _u239() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint239_t{x}; }
template <char ...digits> constexpr static auto operator "" _u230() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint230_t{x}; }
template <char ...digits> constexpr static auto operator "" _u231() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint231_t{x}; }
template <char ...digits> constexpr static auto operator "" _u232() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint232_t{x}; }
template <char ...digits> constexpr static auto operator "" _u233() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint233_t{x}; }
template <char ...digits> constexpr static auto operator "" _u234() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint234_t{x}; }
template <char ...digits> constexpr static auto operator "" _u235() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint235_t{x}; }
template <char ...digits> constexpr static auto operator "" _u236() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint236_t{x}; }
template <char ...digits> constexpr static auto operator "" _u237() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint237_t{x}; }
template <char ...digits> constexpr static auto operator "" _u238() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint238_t{x}; }
template <char ...digits> constexpr static auto operator "" _u239() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint239_t{x}; }
// 240--249
template <char ...digits> constexpr static auto operator "" _u240() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint240_t{x}; }
template <char ...digits> constexpr static auto operator "" _u241() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint241_t{x}; }
template <char ...digits> constexpr static auto operator "" _u242() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint242_t{x}; }
template <char ...digits> constexpr static auto operator "" _u243() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint243_t{x}; }
template <char ...digits> constexpr static auto operator "" _u244() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint244_t{x}; }
template <char ...digits> constexpr static auto operator "" _u245() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint245_t{x}; }
template <char ...digits> constexpr static auto operator "" _u246() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint246_t{x}; }
template <char ...digits> constexpr static auto operator "" _u247() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint247_t{x}; }
template <char ...digits> constexpr static auto operator "" _u248() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint248_t{x}; }
template <char ...digits> constexpr static auto operator "" _u249() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint249_t{x}; }
template <char ...digits> constexpr static auto operator "" _u240() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint240_t{x}; }
template <char ...digits> constexpr static auto operator "" _u241() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint241_t{x}; }
template <char ...digits> constexpr static auto operator "" _u242() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint242_t{x}; }
template <char ...digits> constexpr static auto operator "" _u243() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint243_t{x}; }
template <char ...digits> constexpr static auto operator "" _u244() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint244_t{x}; }
template <char ...digits> constexpr static auto operator "" _u245() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint245_t{x}; }
template <char ...digits> constexpr static auto operator "" _u246() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint246_t{x}; }
template <char ...digits> constexpr static auto operator "" _u247() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint247_t{x}; }
template <char ...digits> constexpr static auto operator "" _u248() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint248_t{x}; }
template <char ...digits> constexpr static auto operator "" _u249() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint249_t{x}; }
// 250--256
template <char ...digits> constexpr static auto operator "" _u250() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint250_t{x}; }
template <char ...digits> constexpr static auto operator "" _u251() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint251_t{x}; }
template <char ...digits> constexpr static auto operator "" _u252() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint252_t{x}; }
template <char ...digits> constexpr static auto operator "" _u253() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint253_t{x}; }
template <char ...digits> constexpr static auto operator "" _u254() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint254_t{x}; }
template <char ...digits> constexpr static auto operator "" _u255() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint255_t{x}; }
template <char ...digits> constexpr static auto operator "" _u256() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::modints::modint256_t{x}; }
template <char ...digits> constexpr static auto operator "" _u250() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint250_t{x}; }
template <char ...digits> constexpr static auto operator "" _u251() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint251_t{x}; }
template <char ...digits> constexpr static auto operator "" _u252() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint252_t{x}; }
template <char ...digits> constexpr static auto operator "" _u253() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint253_t{x}; }
template <char ...digits> constexpr static auto operator "" _u254() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint254_t{x}; }
template <char ...digits> constexpr static auto operator "" _u255() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint255_t{x}; }
template <char ...digits> constexpr static auto operator "" _u256() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::modints::modint256_t{x}; }
} // namespace modints
@ -1362,14 +1388,23 @@ class numeric_limits<dpf::modint<Nbits>>
= std::numeric_limits<typename dpf::modint<Nbits>::integral_type>::traps;
static constexpr bool tinyness_before = false;
HEDLEY_NO_THROW
static constexpr dpf::modint<Nbits> min() noexcept { return dpf::modint<Nbits>{0}; }
HEDLEY_NO_THROW
static constexpr dpf::modint<Nbits> lowest() noexcept { return dpf::modint<Nbits>{0}; }
HEDLEY_NO_THROW
static constexpr dpf::modint<Nbits> max() noexcept { return ~dpf::modint<Nbits>{0}; }
HEDLEY_NO_THROW
static constexpr dpf::modint<Nbits> epsilon() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr dpf::modint<Nbits> round_error() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr dpf::modint<Nbits> infinity() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr dpf::modint<Nbits> quiet_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr dpf::modint<Nbits> signaling_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr dpf::modint<Nbits> denorm_min() noexcept { return 0; }
};

View file

@ -157,6 +157,7 @@ template <>
struct make_from_integral_value<dpf::nyble>
{
using integral_type = std::uint8_t;
HEDLEY_NO_THROW
constexpr dpf::nyble operator()(integral_type val) const noexcept
{
return dpf::to_nyble(val);
@ -189,8 +190,11 @@ class numeric_limits<dpf::nyble> : public numeric_limits<std::uint8_t>
public:
static constexpr int digits = 4;
static constexpr int digits10 = 1;
HEDLEY_NO_THROW
static constexpr dpf::nyble min() noexcept { return dpf::nyble::zero; }
HEDLEY_NO_THROW
static constexpr dpf::nyble max() noexcept { return dpf::nyble{15}; }
HEDLEY_NO_THROW
static constexpr dpf::nyble lowest() noexcept { return min(); }
};

View file

@ -1,6 +1,14 @@
/// @file dpf/output_buffer.hpp
/// @brief
/// @details
/// @brief Move-only storage for shares written by multi-point evaluation.
/// @details Slot type follows the key. A `party_key` leaf buffer holds
/// `subtractive_share`s; a comparison buffer holds
/// `additive_share`s. `bit`, `twobit`, and `nyble` slots are packed.
/// Trivially default-constructible slots are left uninitialized
/// because evaluation overwrites every slot it is responsible for.
///
/// `eval_interval` and recipe `eval_sequence` take the buffer by
/// non-const reference. The returned iterable refers into it.
/// @snippet evaluation/output_buffers.cpp output-buffer
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
@ -9,6 +17,8 @@
#ifndef LIBDPF_INCLUDE_DPF_OUTPUT_BUFFER_HPP__
#define LIBDPF_INCLUDE_DPF_OUTPUT_BUFFER_HPP__
#include "hedley/hedley.h"
#include <cstddef>
#include <algorithm>
#include <tuple>
@ -77,9 +87,12 @@ class output_buffer_allocator : public aligned_allocator<T, Alignment>
using other = output_buffer_allocator<U, Alignment>;
};
HEDLEY_NO_THROW
output_buffer_allocator() noexcept = default;
HEDLEY_NO_THROW
output_buffer_allocator(const output_buffer_allocator &) noexcept = default;
template <typename U>
HEDLEY_NO_THROW
output_buffer_allocator(const output_buffer_allocator<U, Alignment> &) noexcept {}
template <typename U>
@ -100,6 +113,7 @@ class output_buffer_allocator : public aligned_allocator<T, Alignment>
}
template <typename U>
HEDLEY_NO_THROW
void destroy(U * p) noexcept
{
if constexpr (!std::is_trivially_destructible_v<U>)
@ -110,6 +124,7 @@ class output_buffer_allocator : public aligned_allocator<T, Alignment>
};
template <typename T, std::size_t A, typename U, std::size_t B>
HEDLEY_NO_THROW
constexpr bool operator==(const output_buffer_allocator<T, A> &,
const output_buffer_allocator<U, B> &) noexcept
{
@ -117,12 +132,15 @@ constexpr bool operator==(const output_buffer_allocator<T, A> &,
}
template <typename T, std::size_t A, typename U, std::size_t B>
HEDLEY_NO_THROW
constexpr bool operator!=(const output_buffer_allocator<T, A> & lhs,
const output_buffer_allocator<U, B> & rhs) noexcept
{
return !(lhs == rhs);
}
/// Move-only vector of `T`. Copy construction and copy assignment are
/// deleted. `at`, `operator[]`, `data`, iterators, and `size` are public.
template <typename T,
std::size_t Alignment = utils::max_align_v>
class output_buffer final
@ -135,13 +153,17 @@ class output_buffer final
using iterator = typename vector::iterator;
using const_iterator = typename vector::const_iterator;
using size_type = typename vector::size_type;
HEDLEY_NO_THROW
output_buffer() noexcept = default;
explicit output_buffer(size_type size) : vector(size) { }
HEDLEY_NO_THROW
output_buffer(output_buffer &&) noexcept = default;
output_buffer(const output_buffer &) = delete;
HEDLEY_NO_THROW
output_buffer & operator=(output_buffer &&) noexcept = default;
output_buffer & operator=(const output_buffer &) = delete;
~output_buffer() = default;
HEDLEY_NO_THROW
~output_buffer() noexcept = default;
// "selectively public" inheritance
using vector::at;
@ -161,11 +183,14 @@ class output_buffer<dpf::bit> : public dpf::dynamic_bit_array<>
using size_type = typename dpf::dynamic_bit_array<>::size_type;
public:
explicit output_buffer(size_type size) : dynamic_bit_array(size) { }
HEDLEY_NO_THROW
output_buffer(output_buffer &&) noexcept = default;
output_buffer(const output_buffer &) = delete;
HEDLEY_NO_THROW
output_buffer & operator=(output_buffer &&) noexcept = default;
output_buffer & operator=(const output_buffer &) = delete;
~output_buffer() = default;
HEDLEY_NO_THROW
~output_buffer() noexcept = default;
};
template <>
@ -175,11 +200,14 @@ class output_buffer<dpf::twobit> : public dpf::dynamic_packed_array<dpf::twobit>
public:
using size_type = typename base::size_type;
explicit output_buffer(size_type size) : base(size) { }
HEDLEY_NO_THROW
output_buffer(output_buffer &&) noexcept = default;
output_buffer(const output_buffer &) = delete;
HEDLEY_NO_THROW
output_buffer & operator=(output_buffer &&) noexcept = default;
output_buffer & operator=(const output_buffer &) = delete;
~output_buffer() = default;
HEDLEY_NO_THROW
~output_buffer() noexcept = default;
};
template <>
@ -189,11 +217,14 @@ class output_buffer<dpf::nyble> : public dpf::dynamic_packed_array<dpf::nyble>
public:
using size_type = typename base::size_type;
explicit output_buffer(size_type size) : base(size) { }
HEDLEY_NO_THROW
output_buffer(output_buffer &&) noexcept = default;
output_buffer(const output_buffer &) = delete;
HEDLEY_NO_THROW
output_buffer & operator=(output_buffer &&) noexcept = default;
output_buffer & operator=(const output_buffer &) = delete;
~output_buffer() = default;
HEDLEY_NO_THROW
~output_buffer() noexcept = default;
};
#define LIBDPF_PACKED_SHARE_BUFFER(LANE, PARTY) \
@ -205,11 +236,14 @@ class output_buffer<subtractive_share<LANE, PARTY>>
public: \
using size_type = typename base::size_type; \
explicit output_buffer(size_type size) : base(size) {} \
HEDLEY_NO_THROW \
output_buffer(output_buffer &&) noexcept = default; \
output_buffer(const output_buffer &) = delete; \
HEDLEY_NO_THROW \
output_buffer & operator=(output_buffer &&) noexcept = default; \
output_buffer & operator=(const output_buffer &) = delete; \
~output_buffer() = default; \
HEDLEY_NO_THROW \
~output_buffer() noexcept = default; \
};
LIBDPF_PACKED_SHARE_BUFFER(dpf::twobit, 0);
@ -232,12 +266,14 @@ class output_buffer<subtractive_share<dpf::bit, PARTY>>
output_buffer(const output_buffer &) = delete; \
output_buffer & operator=(output_buffer &&) noexcept = default; \
output_buffer & operator=(const output_buffer &) = delete; \
~output_buffer() = default; \
~output_buffer() noexcept = default; \
};
LIBDPF_BIT_SHARE_BUFFER(0);
LIBDPF_BIT_SHARE_BUFFER(1);
#undef LIBDPF_BIT_SHARE_BUFFER
/// Buffer sized for the closed interval `[from, to]` of output `I`.
/// On a `party_key`, elements are subtractive shares of that output.
template <typename DpfKey,
std::size_t I = 0,
typename InputT>
@ -247,9 +283,6 @@ auto make_output_buffer_for_interval(InputT from, InputT to)
using output_type = typename DpfKey::concrete_output_type<I>;
using buffer_elem = leaf_buffer_elem_t<DpfKey, output_type>;
utils::flip_msb_if_signed_integral(from);
utils::flip_msb_if_signed_integral(to);
std::size_t nodes_in_interval = utils::get_leafnodes_in_output_interval<dpf_type>(from, to);
return dpf::output_buffer<buffer_elem>(nodes_in_interval*dpf_type::outputs_per_leaf);
}
@ -285,6 +318,7 @@ inline auto make_output_buffer_for_interval(const DpfKey &, InputT from, InputT
return make_output_buffer_for_interval<DpfKey, I0, I1, Is...>(from, to);
}
/// Buffer sized for every input of output `I`.
template <typename DpfKey,
std::size_t I = 0>
auto make_output_buffer_for_full()

View file

@ -50,15 +50,18 @@ class dynamic_packed_array
class lane_ref
{
public:
HEDLEY_NO_THROW
lane_ref(word_type * word, unsigned shift) noexcept
: word_{word}, shift_{shift} {}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
operator LaneT() const noexcept
{
return static_cast<LaneT>((*word_ >> shift_) & lane_mask);
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
lane_ref & operator=(LaneT value) noexcept
{
@ -69,23 +72,28 @@ class dynamic_packed_array
return *this;
}
HEDLEY_NO_THROW
lane_ref & operator=(const lane_ref & other) noexcept
{
return (*this = static_cast<LaneT>(other));
}
HEDLEY_NO_THROW
friend bool operator==(lane_ref lhs, LaneT rhs) noexcept
{
return static_cast<LaneT>(lhs) == rhs;
}
HEDLEY_NO_THROW
friend bool operator==(LaneT lhs, lane_ref rhs) noexcept
{
return rhs == lhs;
}
HEDLEY_NO_THROW
friend bool operator!=(lane_ref lhs, LaneT rhs) noexcept
{
return !(lhs == rhs);
}
HEDLEY_NO_THROW
friend bool operator!=(LaneT lhs, lane_ref rhs) noexcept
{
return !(rhs == lhs);
@ -108,25 +116,33 @@ class dynamic_packed_array
using pointer = void;
using reference = lane_ref;
HEDLEY_NO_THROW
iterator() noexcept = default;
HEDLEY_NO_THROW
iterator(word_type * data, size_type index) noexcept
: data_{data}, index_{index} {}
HEDLEY_NO_THROW
lane_ref operator*() const noexcept { return ref_at(index_); }
HEDLEY_NO_THROW
lane_ref operator[](difference_type n) const noexcept
{
return ref_at(static_cast<size_type>(
static_cast<difference_type>(index_) + n));
}
HEDLEY_NO_THROW
iterator & operator++() noexcept { ++index_; return *this; }
HEDLEY_NO_THROW
iterator operator++(int) noexcept
{
iterator prev = *this;
++*this;
return prev;
}
HEDLEY_NO_THROW
iterator & operator--() noexcept { --index_; return *this; }
HEDLEY_NO_THROW
iterator operator--(int) noexcept
{
iterator prev = *this;
@ -134,53 +150,66 @@ class dynamic_packed_array
return prev;
}
HEDLEY_NO_THROW
iterator & operator+=(difference_type n) noexcept
{
index_ = static_cast<size_type>(
static_cast<difference_type>(index_) + n);
return *this;
}
HEDLEY_NO_THROW
iterator & operator-=(difference_type n) noexcept
{
return *this += -n;
}
HEDLEY_NO_THROW
friend iterator operator+(iterator it, difference_type n) noexcept
{
it += n;
return it;
}
HEDLEY_NO_THROW
friend iterator operator+(difference_type n, iterator it) noexcept
{
return it + n;
}
HEDLEY_NO_THROW
friend iterator operator-(iterator it, difference_type n) noexcept
{
it -= n;
return it;
}
HEDLEY_NO_THROW
friend difference_type operator-(iterator a, iterator b) noexcept
{
return static_cast<difference_type>(a.index_)
- static_cast<difference_type>(b.index_);
}
HEDLEY_NO_THROW
friend bool operator==(iterator a, iterator b) noexcept
{
return a.index_ == b.index_;
}
HEDLEY_NO_THROW
friend bool operator!=(iterator a, iterator b) noexcept
{
return !(a == b);
}
HEDLEY_NO_THROW
friend bool operator<(iterator a, iterator b) noexcept
{
return a.index_ < b.index_;
}
HEDLEY_NO_THROW
friend bool operator>(iterator a, iterator b) noexcept { return b < a; }
HEDLEY_NO_THROW
friend bool operator<=(iterator a, iterator b) noexcept { return !(b < a); }
HEDLEY_NO_THROW
friend bool operator>=(iterator a, iterator b) noexcept { return !(a < b); }
private:
HEDLEY_NO_THROW
lane_ref ref_at(size_type index) const noexcept
{
const size_type bit = index * lane_bits;
@ -213,12 +242,14 @@ class dynamic_packed_array
dynamic_packed_array(const dynamic_packed_array &) = delete;
dynamic_packed_array & operator=(const dynamic_packed_array &) = delete;
HEDLEY_NO_THROW
dynamic_packed_array(dynamic_packed_array && other) noexcept
: nlanes_{std::exchange(other.nlanes_, 0)},
nwords_{std::exchange(other.nwords_, 0)},
data_{std::move(other.data_)}
{}
HEDLEY_NO_THROW
dynamic_packed_array & operator=(dynamic_packed_array && other) noexcept
{
if (this != &other)
@ -232,13 +263,19 @@ class dynamic_packed_array
~dynamic_packed_array() = default;
HEDLEY_NO_THROW
size_type size() const noexcept { return nlanes_; }
HEDLEY_NO_THROW
bool empty() const noexcept { return nlanes_ == 0; }
HEDLEY_NO_THROW
size_type data_length() const noexcept { return nwords_; }
HEDLEY_NO_THROW
word_type * data() noexcept { return data_.get(); }
HEDLEY_NO_THROW
const word_type * data() const noexcept { return data_.get(); }
HEDLEY_NO_THROW
LaneT operator[](size_type i) const noexcept
{
assert(i < nlanes_);
@ -247,6 +284,7 @@ class dynamic_packed_array
return static_cast<LaneT>((data_[bit / 64u] >> shift) & lane_mask);
}
HEDLEY_NO_THROW
lane_ref operator[](size_type i) noexcept
{
assert(i < nlanes_);
@ -255,11 +293,17 @@ class dynamic_packed_array
static_cast<unsigned>(bit % 64u));
}
HEDLEY_NO_THROW
iterator begin() noexcept { return iterator{data(), 0}; }
HEDLEY_NO_THROW
iterator end() noexcept { return iterator{data(), nlanes_}; }
HEDLEY_NO_THROW
iterator begin() const noexcept { return iterator{data_.get(), 0}; }
HEDLEY_NO_THROW
iterator end() const noexcept { return iterator{data_.get(), nlanes_}; }
HEDLEY_NO_THROW
iterator cbegin() const noexcept { return begin(); }
HEDLEY_NO_THROW
iterator cend() const noexcept { return end(); }
private:
@ -284,25 +328,30 @@ class packed_share_output : public dynamic_packed_array<LaneT>
class reference
{
public:
HEDLEY_NO_THROW
explicit reference(typename lanes::reference lane) noexcept : lane_{lane} {}
HEDLEY_NO_THROW
operator share_type() const noexcept
{
return share_type::from_raw(static_cast<LaneT>(lane_));
}
HEDLEY_NO_THROW
reference & operator=(const share_type & share) noexcept
{
lane_ = share.raw();
return *this;
}
HEDLEY_NO_THROW
reference & operator=(LaneT value) noexcept
{
lane_ = value;
return *this;
}
HEDLEY_NO_THROW
reference & operator=(const reference & other) noexcept
{
return (*this = static_cast<share_type>(other));
@ -321,48 +370,68 @@ class packed_share_output : public dynamic_packed_array<LaneT>
using pointer = void;
using reference = share_type;
HEDLEY_NO_THROW
iterator() noexcept = default;
HEDLEY_NO_THROW
explicit iterator(typename lanes::iterator it) noexcept : it_{it} {}
HEDLEY_NO_THROW
share_type operator*() const noexcept
{
return share_type::from_raw(static_cast<LaneT>(*it_));
}
HEDLEY_NO_THROW
share_type operator[](difference_type n) const noexcept
{
return share_type::from_raw(static_cast<LaneT>(it_[n]));
}
HEDLEY_NO_THROW
iterator & operator++() noexcept { ++it_; return *this; }
HEDLEY_NO_THROW
iterator operator++(int) noexcept { iterator p = *this; ++*this; return p; }
HEDLEY_NO_THROW
iterator & operator--() noexcept { --it_; return *this; }
HEDLEY_NO_THROW
iterator operator--(int) noexcept { iterator p = *this; --*this; return p; }
HEDLEY_NO_THROW
iterator & operator+=(difference_type n) noexcept { it_ += n; return *this; }
HEDLEY_NO_THROW
iterator & operator-=(difference_type n) noexcept { it_ -= n; return *this; }
HEDLEY_NO_THROW
friend iterator operator+(iterator it, difference_type n) noexcept
{
it += n;
return it;
}
HEDLEY_NO_THROW
friend iterator operator+(difference_type n, iterator it) noexcept
{
return it + n;
}
HEDLEY_NO_THROW
friend iterator operator-(iterator it, difference_type n) noexcept
{
it -= n;
return it;
}
HEDLEY_NO_THROW
friend difference_type operator-(iterator a, iterator b) noexcept
{
return a.it_ - b.it_;
}
HEDLEY_NO_THROW
friend bool operator==(iterator a, iterator b) noexcept { return a.it_ == b.it_; }
HEDLEY_NO_THROW
friend bool operator!=(iterator a, iterator b) noexcept { return !(a == b); }
HEDLEY_NO_THROW
friend bool operator<(iterator a, iterator b) noexcept { return a.it_ < b.it_; }
HEDLEY_NO_THROW
friend bool operator>(iterator a, iterator b) noexcept { return b < a; }
HEDLEY_NO_THROW
friend bool operator<=(iterator a, iterator b) noexcept { return !(b < a); }
HEDLEY_NO_THROW
friend bool operator>=(iterator a, iterator b) noexcept { return !(a < b); }
private:
@ -375,7 +444,9 @@ class packed_share_output : public dynamic_packed_array<LaneT>
packed_share_output(const packed_share_output &) = delete;
packed_share_output & operator=(const packed_share_output &) = delete;
HEDLEY_NO_THROW
packed_share_output(packed_share_output &&) noexcept = default;
HEDLEY_NO_THROW
packed_share_output & operator=(packed_share_output &&) noexcept = default;
~packed_share_output() = default;
@ -383,26 +454,34 @@ class packed_share_output : public dynamic_packed_array<LaneT>
using lanes::empty;
using lanes::size;
HEDLEY_NO_THROW
reference operator[](size_type i) noexcept
{
return reference{lanes::operator[](i)};
}
HEDLEY_NO_THROW
share_type operator[](size_type i) const noexcept
{
return share_type::from_raw(lanes::operator[](i));
}
HEDLEY_NO_THROW
iterator begin() noexcept { return iterator{lanes::begin()}; }
HEDLEY_NO_THROW
iterator end() noexcept { return iterator{lanes::end()}; }
HEDLEY_NO_THROW
iterator begin() const noexcept
{
return iterator{typename lanes::iterator{this->data(), 0}};
}
HEDLEY_NO_THROW
iterator end() const noexcept
{
return iterator{typename lanes::iterator{this->data(), this->size()}};
}
HEDLEY_NO_THROW
iterator cbegin() const noexcept { return begin(); }
HEDLEY_NO_THROW
iterator cend() const noexcept { return end(); }
};

View file

@ -34,6 +34,7 @@ namespace detail
template <unsigned Bits>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
unsigned lane_at(unsigned byte, unsigned shift) noexcept
{
constexpr unsigned mask = (1u << Bits) - 1u;
@ -67,6 +68,7 @@ NodeT apply_bytes(const NodeT & a, const NodeT & b, Op op) noexcept
return out;
}
HEDLEY_NO_THROW
inline simde__m128i nibble_lut(const unsigned char lut[16]) noexcept
{
simde__m128i table;
@ -74,6 +76,7 @@ inline simde__m128i nibble_lut(const unsigned char lut[16]) noexcept
return table;
}
HEDLEY_NO_THROW
inline simde__m256i nibble_lut256(const unsigned char lut[16]) noexcept
{
alignas(32) unsigned char both[32];
@ -84,6 +87,7 @@ inline simde__m256i nibble_lut256(const unsigned char lut[16]) noexcept
return table;
}
HEDLEY_NO_THROW
inline void fill_epi2_mul_lut(unsigned k, unsigned char lut[16]) noexcept
{
k &= 3u;
@ -95,6 +99,7 @@ inline void fill_epi2_mul_lut(unsigned k, unsigned char lut[16]) noexcept
}
}
HEDLEY_NO_THROW
inline void fill_epi4_mul_lut(unsigned k, unsigned char lut[16]) noexcept
{
k &= 0x0fu;
@ -104,6 +109,7 @@ inline void fill_epi4_mul_lut(unsigned k, unsigned char lut[16]) noexcept
}
}
HEDLEY_NO_THROW
inline simde__m128i shuffle_nibbles(simde__m128i table, simde__m128i a) noexcept
{
const auto m = simde_mm_set1_epi8(0x0f);
@ -114,6 +120,7 @@ inline simde__m128i shuffle_nibbles(simde__m128i table, simde__m128i a) noexcept
simde_mm_slli_epi16(simde_mm_and_si128(hi, m), 4));
}
HEDLEY_NO_THROW
inline simde__m256i shuffle_nibbles(simde__m256i table, simde__m256i a) noexcept
{
const auto m = simde_mm256_set1_epi8(0x0f);
@ -126,6 +133,7 @@ inline simde__m256i shuffle_nibbles(simde__m256i table, simde__m256i a) noexcept
/// Low nibble of every byte, product mod 16. Even and odd bytes are split
/// so a product in one byte cannot land in the next.
HEDLEY_NO_THROW
inline simde__m128i mul_low_nibbles(simde__m128i a, simde__m128i b) noexcept
{
const auto lane = simde_mm_set1_epi16(0x000f);
@ -138,6 +146,7 @@ inline simde__m128i mul_low_nibbles(simde__m128i a, simde__m128i b) noexcept
return simde_mm_or_si128(pe, simde_mm_slli_epi16(po, 8));
}
HEDLEY_NO_THROW
inline simde__m256i mul_low_nibbles(simde__m256i a, simde__m256i b) noexcept
{
const auto lane = simde_mm256_set1_epi16(0x000f);

View file

@ -143,9 +143,11 @@ class parallel_const_bit_iterator
using const_reference = const value_type &;
using pointer = std::add_pointer_t<value_type>;
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr
parallel_const_bit_iterator(parallel_const_bit_iterator &&) noexcept = default;
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr
parallel_const_bit_iterator(const parallel_const_bit_iterator &) noexcept = default;
@ -273,6 +275,7 @@ class parallel_const_bit_iterator
std::make_index_sequence<batch_size>());
}
HEDLEY_NO_THROW
explicit constexpr parallel_const_bit_iterator(
const word_pointer_array & arr) noexcept
: iter_{arr},
@ -293,7 +296,9 @@ class parallel_const_bit_iterator
simde_type vec_mask_;
simde_array all_vecs_;
HEDLEY_NO_THROW
friend parallel_const_bit_iterator parallel_bit_iterable<batch_size, ChildT>::begin() const noexcept;
HEDLEY_NO_THROW
friend parallel_const_bit_iterator parallel_bit_iterable<batch_size, ChildT>::end() const noexcept;
}; // class dpf::parallel_const_bit_iterator
@ -302,6 +307,7 @@ template <std::size_t N,
typename Iter>
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
auto batch_of(Iter it) noexcept
{
return dpf::parallel_bit_iterable<N, ChildT>{it};
@ -311,6 +317,7 @@ template <typename ChildT,
typename ...Ts>
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
auto batch_of(const dpf::bit_array_base<ChildT> & t, const Ts & ...ts) noexcept
{
return dpf::parallel_bit_iterable<1+sizeof...(Ts), ChildT>{t, ts...};

View file

@ -75,10 +75,12 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
static constexpr auto left_shift = simde_mm256_slli_epi64;
static constexpr auto right_shift = simde_mm256_srli_epi64;
static constexpr auto bit_and = simde_mm256_and_si256;
HEDLEY_NO_THROW
static auto get_mask() noexcept
{
return simde_mm256_set1_epi64x(1);
}
HEDLEY_NO_THROW
static simde_array build_vecs(const word_type * cur_word, std::size_t nwords) noexcept
{
return { loadu_word_vec(cur_word, nwords, 0) };
@ -104,10 +106,12 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
static constexpr auto left_shift = simde_mm256_slli_epi64;
static constexpr auto right_shift = simde_mm256_srli_epi64;
static constexpr auto bit_and = simde_mm256_and_si256;
HEDLEY_NO_THROW
static auto get_mask() noexcept
{
return simde_mm256_set1_epi32(1);
}
HEDLEY_NO_THROW
static simde_array build_vecs(const typename dpf::bit_array_base<ChildT>::word_type * cur_word,
std::size_t nwords) noexcept
{
@ -149,10 +153,12 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
static constexpr auto left_shift = simde_mm256_slli_epi64;
static constexpr auto right_shift = simde_mm256_srli_epi64;
static constexpr auto bit_and = simde_mm256_and_si256;
HEDLEY_NO_THROW
static auto get_mask() noexcept
{
return simde_mm256_set1_epi16(1);
}
HEDLEY_NO_THROW
static simde_array build_vecs(const typename dpf::bit_array_base<ChildT>::word_type * cur_word,
std::size_t nwords) noexcept
{
@ -215,10 +221,12 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
static constexpr auto left_shift = simde_mm256_slli_epi64;
static constexpr auto right_shift = simde_mm256_srli_epi64;
static constexpr auto bit_and = simde_mm256_and_si256;
HEDLEY_NO_THROW
static auto get_mask() noexcept
{
return simde_mm256_set1_epi8(1);
}
HEDLEY_NO_THROW
static simde_array build_vecs(const typename dpf::bit_array_base<ChildT>::word_type * cur_word,
std::size_t nwords) noexcept
{

View file

@ -1,6 +1,13 @@
/// @file dpf/path_memoizer.hpp
/// @brief
/// @details
/// @brief Workspaces that resume a root-to-leaf DPF walk.
/// @details `basic_path_memoizer` keeps one interior node per level. The next
/// `eval_point` recomputes the suffix after the common prefix with
/// the previous input. `nonmemoizing_path_memoizer` keeps a single
/// node and starts at the root on every call.
///
/// Pass a mutable lvalue to `eval_point`. The factories unwrap
/// `party_key`, so a memoizer built from either party's type
/// accepts both parties. A different root restarts the path.
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
@ -44,13 +51,21 @@ struct path_memoizer_base
using return_type = ReturnT;
using iterator_type = return_type;
HEDLEY_NO_THROW
virtual std::size_t assign_x(const dpf_type &, input_type) noexcept = 0;
HEDLEY_NO_THROW
virtual node_type & operator[](std::size_t) noexcept = 0;
HEDLEY_NO_THROW
virtual return_type begin() const noexcept = 0;
HEDLEY_NO_THROW
virtual return_type end() const noexcept = 0;
};
/// One interior node per level. `assign_x` returns the first level that the
/// next walk must recompute. `filled_to` is the deepest level already
/// written for the current input. Callers pass this object to `eval_point`;
/// they do not call `assign_x` themselves.
template <typename DpfKey>
struct alignas(alignof(typename path_memoizer_key_t<DpfKey>::interior_node))
basic_path_memoizer final
@ -69,12 +84,15 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
basic_path_memoizer()
: dpf_{std::nullopt}, x_{std::nullopt}, filled_to_{0} { }
HEDLEY_NO_THROW
basic_path_memoizer(basic_path_memoizer &&) noexcept = default;
basic_path_memoizer(const basic_path_memoizer &) = default;
HEDLEY_NO_THROW
basic_path_memoizer & operator=(basic_path_memoizer &&) noexcept = default;
basic_path_memoizer & operator=(const basic_path_memoizer &) = default;
~basic_path_memoizer() = default;
HEDLEY_NO_THROW
std::size_t assign_x(const dpf_type & dpf, input_type new_x) noexcept override
{
static constexpr auto clz_xor = utils::countl_zero_symmetric_difference<input_type>{};
@ -100,11 +118,13 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
return 1;
}
HEDLEY_NO_THROW
node_type & operator[](std::size_t i) noexcept override
{
return arr_[i];
}
HEDLEY_NO_THROW
return_type begin() const noexcept override
{
if (x_.has_value() == true)
@ -117,14 +137,17 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
}
}
HEDLEY_NO_THROW
return_type end() const noexcept override
{
return std::addressof(arr_[depth+1]);
}
/// Inclusive high-water: `arr_[0..filled_to_]` are valid for the current x.
HEDLEY_NO_THROW
std::size_t filled_to() const noexcept { return filled_to_; }
HEDLEY_NO_THROW
void note_filled(std::size_t level) noexcept
{
if (level > filled_to_)
@ -143,6 +166,7 @@ HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
HEDLEY_PRAGMA(GCC diagnostic pop)
};
/// A single interior node. Every `assign_x` restarts at the root.
template <typename DpfKey>
struct nonmemoizing_path_memoizer final
: public path_memoizer_base<path_memoizer_key_t<DpfKey>>
@ -159,8 +183,10 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
nonmemoizing_path_memoizer()
: dpf_{std::nullopt} { }
HEDLEY_NO_THROW
nonmemoizing_path_memoizer(nonmemoizing_path_memoizer &&) noexcept = default;
nonmemoizing_path_memoizer(const nonmemoizing_path_memoizer &) = default;
HEDLEY_NO_THROW
nonmemoizing_path_memoizer & operator=(nonmemoizing_path_memoizer &&) noexcept = default;
nonmemoizing_path_memoizer & operator=(const nonmemoizing_path_memoizer &) = default;
~nonmemoizing_path_memoizer() = default;
@ -186,6 +212,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
return std::addressof(v);
}
HEDLEY_NO_THROW
return_type end() const noexcept override
{
return std::addressof(v) + 1;
@ -260,6 +287,9 @@ void ensure_level(const DpfKey & dpf, typename DpfKey::input_type x,
} // namespace detail
/// Path workspace for `DpfKey`. A `party_key` argument is unwrapped, and the
/// result accepts both parties.
/// @snippet evaluation/memoizers.cpp path-memoizer
template <typename DpfKey>
auto make_basic_path_memoizer()
{
@ -272,6 +302,7 @@ auto make_basic_path_memoizer(const DpfKey &)
return make_basic_path_memoizer<DpfKey>();
}
/// Single-node path workspace. Suitable for one query.
template <typename DpfKey>
auto make_nonmemoizing_path_memoizer()
{

View file

@ -64,7 +64,8 @@ template <typename T> inline constexpr bool is_at_v = is_at<T>::value;
/// `OutBits` is the comparison output group width (bits of the β payload),
/// so the value CWs / addend can be stored at group width instead of a full
/// padded `uint64_t` per level.
template <std::size_t Depth, std::size_t OutBits = 0, bool Wild = false>
template <std::size_t Depth, std::size_t OutBits = 0, bool Wild = false,
std::size_t BlockWidth = 0, bool Incremental = false>
struct cmp_channel_tag
{
static constexpr std::size_t depth = Depth;
@ -73,11 +74,17 @@ struct cmp_channel_tag
/// after keygen. Concrete (non-wildcard) cmp keys keep `Wild == false`
/// so their layout / type name is unchanged.
static constexpr bool wild = Wild;
/// 0 keeps the per-level path-sum. `B >= 1` selects blocked checkpoints
/// of target width `B`.
static constexpr std::size_t block_width = BlockWidth;
/// Save a final correction at every depth (`idcf`).
static constexpr bool incremental = Incremental;
};
template <typename T> struct is_cmp_channel_tag : std::false_type {};
template <std::size_t Depth, std::size_t OutBits, bool Wild>
struct is_cmp_channel_tag<cmp_channel_tag<Depth, OutBits, Wild>>
template <std::size_t Depth, std::size_t OutBits, bool Wild, std::size_t Block,
bool Incremental>
struct is_cmp_channel_tag<cmp_channel_tag<Depth, OutBits, Wild, Block, Incremental>>
: std::true_type {};
template <typename T>
inline constexpr bool is_cmp_channel_tag_v =
@ -107,6 +114,28 @@ struct is_placed<placed<N, O>> : std::true_type {};
template <typename T>
inline constexpr bool is_placed_v = is_placed<std::decay_t<T>>::value;
/// Heavy-hitters incremental point function: one payload per prefix length.
/// `levels[i]` is the bit length of slot `i`.
template <typename LevelSeq, typename ...Betas>
struct idpf_pack;
template <std::size_t ...Levels, typename ...Betas>
struct idpf_pack<std::index_sequence<Levels...>, Betas...>
{
static constexpr bool is_idpf = true;
static constexpr std::size_t n = sizeof...(Levels);
static constexpr std::array<std::size_t, n == 0 ? 1 : n> levels{
Levels...};
std::tuple<Betas...> values;
};
template <typename T> struct is_idpf : std::false_type {};
template <std::size_t ...Levels, typename ...Betas>
struct is_idpf<idpf_pack<std::index_sequence<Levels...>, Betas...>>
: std::true_type {};
template <typename T>
inline constexpr bool is_idpf_v = is_idpf<std::decay_t<T>>::value;
template <typename NodeT, typename OutputT>
inline constexpr std::size_t out_bits_v =
utils::bitlength_of_output_v<concrete_type_t<OutputT>, NodeT>;
@ -385,6 +414,8 @@ struct normalize_one
static constexpr std::size_t cmp_depth = 0;
static constexpr std::size_t cmp_out_bits = 0;
static constexpr bool cmp_wild = false;
static constexpr std::size_t cmp_block = 0;
static constexpr bool cmp_idcf = false;
};
template <std::size_t BitLen, std::size_t N, typename T>
struct normalize_one<BitLen, placed<N, T>>
@ -393,14 +424,20 @@ struct normalize_one<BitLen, placed<N, T>>
static constexpr std::size_t cmp_depth = 0;
static constexpr std::size_t cmp_out_bits = 0;
static constexpr bool cmp_wild = false;
static constexpr std::size_t cmp_block = 0;
static constexpr bool cmp_idcf = false;
};
template <std::size_t BitLen, std::size_t Depth, std::size_t OutBits, bool Wild>
struct normalize_one<BitLen, cmp_channel_tag<Depth, OutBits, Wild>>
template <std::size_t BitLen, std::size_t Depth, std::size_t OutBits, bool Wild,
std::size_t Block, bool Incremental>
struct normalize_one<BitLen,
cmp_channel_tag<Depth, OutBits, Wild, Block, Incremental>>
{
using placed_tuple = std::tuple<>;
static constexpr std::size_t cmp_depth = Depth;
static constexpr std::size_t cmp_out_bits = OutBits;
static constexpr bool cmp_wild = Wild;
static constexpr std::size_t cmp_block = Block;
static constexpr bool cmp_idcf = Incremental;
};
template <std::size_t BitLen, typename ...Elems>
@ -419,6 +456,10 @@ struct normalize_pack
// wildcard flag (false when there is no cmp channel).
static constexpr bool cmp_wild =
(false || ... || normalize_one<BitLen, Elems>::cmp_wild);
static constexpr std::size_t cmp_block =
(std::size_t{0} + ... + normalize_one<BitLen, Elems>::cmp_block);
static constexpr bool cmp_idcf =
(false || ... || normalize_one<BitLen, Elems>::cmp_idcf);
};
template <std::size_t BitLen>
@ -428,6 +469,8 @@ struct normalize_pack<BitLen>
static constexpr std::size_t cmp_depth = 0;
static constexpr std::size_t cmp_out_bits = 0;
static constexpr bool cmp_wild = false;
static constexpr std::size_t cmp_block = 0;
static constexpr bool cmp_idcf = false;
};
/// True iff the pack is "classic-shaped": every element is a bare output (no
@ -439,6 +482,32 @@ inline constexpr bool is_classic_pack_v =
} // namespace incr
} // namespace detail
/// Sparse heavy-hitters IDPF. Slot `i` is the point function on prefix
/// `Levels[i]`, evaluated with `out<i>`.
template <std::size_t ...Levels, typename ...Betas>
auto idpf_at(Betas ...betas)
{
static_assert(sizeof...(Levels) == sizeof...(Betas),
"idpf_at: one payload per prefix length");
return detail::incr::idpf_pack<std::index_sequence<Levels...>,
std::decay_t<Betas>...>{
std::tuple<std::decay_t<Betas>...>{std::move(betas)...}};
}
template <std::size_t ...I, typename ...Betas>
auto idpf_from_seq(std::index_sequence<I...>, Betas ...betas)
{
return idpf_at<(I + 1)...>(std::move(betas)...);
}
/// Consecutive prefixes of length 1, 2, …, `sizeof...(Betas)`.
template <typename ...Betas>
auto idpf(Betas ...betas)
{
return idpf_from_seq(std::make_index_sequence<sizeof...(Betas)>{},
std::move(betas)...);
}
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_PLACEMENT_HPP__

View file

@ -13,9 +13,11 @@
#include <atomic>
#include <cstring>
#include <stdexcept>
#include <type_traits>
#include "dpf/prg_aes.hpp"
#include "dpf/prg_chacha.hpp"
#include "dpf/prg_dummy.hpp"
#include "dpf/prg_lowmc.hpp"
#include "dpf/secret_share.hpp"
@ -63,23 +65,34 @@ auto expand_as_share(typename PRG::block_type seed,
template <typename AesKey>
template <typename T, std::size_t Party>
HEDLEY_NO_THROW
auto aes<AesKey>::expand(block_type seed, psnip_uint32_t pos) noexcept
{
return detail::expand_as_share<aes<AesKey>, T, Party>(seed, pos);
}
template <typename T, std::size_t Party>
HEDLEY_NO_THROW
auto dummy::expand(block_type seed, psnip_uint32_t pos) noexcept
{
return detail::expand_as_share<dummy, T, Party>(seed, pos);
}
template <typename T, std::size_t Party>
HEDLEY_NO_THROW
auto lowmc128::expand(block_type seed, psnip_uint32_t pos) noexcept
{
return detail::expand_as_share<lowmc128, T, Party>(seed, pos);
}
template <unsigned Rounds>
template <typename T, std::size_t Party>
HEDLEY_NO_THROW
auto chacha<Rounds>::expand(block_type seed, psnip_uint32_t pos) noexcept
{
return detail::expand_as_share<chacha<Rounds>, T, Party>(seed, pos);
}
template <typename PRG>
struct counter_wrapper final
{
@ -101,17 +114,22 @@ struct counter_wrapper final
return PRG::eval01(seed);
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
static void eval(block_type seed, block_type * HEDLEY_RESTRICT output,
psnip_uint32_t count, psnip_uint32_t pos = 0) noexcept
psnip_uint32_t count, psnip_uint32_t pos = 0)
{
if (count > 1 &&
pos > static_cast<psnip_uint32_t>(~static_cast<psnip_uint32_t>(0)) - (count - 1u))
{
throw std::invalid_argument("prg lane index is out of range");
}
count_.fetch_add(count, std::memory_order::memory_order_relaxed);
PRG::eval(seed, output, count, pos);
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2, 3)
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT left,
block_type * HEDLEY_RESTRICT right) noexcept
@ -122,6 +140,7 @@ struct counter_wrapper final
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
{
@ -131,6 +150,7 @@ struct counter_wrapper final
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
{

View file

@ -13,6 +13,7 @@
#include <cstddef>
#include <cstdint>
#include <array>
#include <stdexcept>
#include "hedley/hedley.h"
#include "simde/simde/x86/avx2.h"
@ -103,15 +104,21 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
/// `eval` / `eval01` (`set_epi64x(0, pos)`). The first AddRoundKey
/// includes `rd_key[0]` so this matches the one-block `eval` for any
/// key, not only the all-zero key this PRG currently installs.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
static void eval(block_type seed, block_type * HEDLEY_RESTRICT output,
psnip_uint32_t count, psnip_uint32_t pos = 0) noexcept
psnip_uint32_t count, psnip_uint32_t pos = 0)
{
if (HEDLEY_UNLIKELY(count == 0))
{
return;
}
// `pos + i` is a uint32 add. A span that passes UINT32_MAX must
// fail the same way buffered_prg does, rather than wrap to 0.
if (count > 1 &&
pos > static_cast<psnip_uint32_t>(~static_cast<psnip_uint32_t>(0)) - (count - 1u))
{
throw std::invalid_argument("prg lane index is out of range");
}
require_block_aligned(output);
block_type * HEDLEY_RESTRICT out =
@ -161,6 +168,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
/// `left[i] == eval(seeds[i], 0)`, `right[i] == eval(seeds[i], 1)`.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2, 3)
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT left,
block_type * HEDLEY_RESTRICT right) noexcept
@ -193,6 +201,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
/// Four independent `eval(seed, pos)` as one 4-block round-major AES.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos) noexcept
{
@ -223,6 +232,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
/// Eight independent `eval(seed, pos)` as one 8-block round-major AES.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos) noexcept
{
@ -252,6 +262,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
/// Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`).
template <typename T, std::size_t Party>
HEDLEY_NO_THROW
static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept;
private:
@ -261,6 +272,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
/// rounds 1..last and the MMO feed-forward `XOR seed[i]`.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void aes_mmo_rounds_x4(block_type * HEDLEY_RESTRICT blk,
const block_type * HEDLEY_RESTRICT seed) noexcept
{
@ -283,6 +295,7 @@ HEDLEY_PRAGMA(GCC unroll(14))
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void aes_mmo_rounds_x8(block_type * HEDLEY_RESTRICT blk,
const block_type * HEDLEY_RESTRICT seed) noexcept
{

458
include/dpf/prg_chacha.hpp Normal file
View file

@ -0,0 +1,458 @@
/// @file dpf/prg_chacha.hpp
/// @brief ChaCha stream PRG. Same 128-bit block interface as `aes128`.
/// @details `eval(seed, pos)` is the `pos`-th 16-byte chunk of ChaCha
/// keystream (RFC 8439). The 256-bit key is the 128-bit seed
/// followed by the fixed domain separator `"dpf-chacha-prg\0\0"`.
/// The nonce is zero. The ChaCha block counter is `pos / 4`, and
/// the chunk inside that block is `pos % 4`.
///
/// `chacha20` is the RFC round count. `chacha12` and `chacha8` are
/// the same construction with fewer rounds. `chacha<R>` accepts any
/// positive even round count.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
/// see [LICENSE.md](@ref license) for details.
#ifndef LIBDPF_INCLUDE_DPF_PRG_CHACHA_HPP__
#define LIBDPF_INCLUDE_DPF_PRG_CHACHA_HPP__
#include <array>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <stdexcept>
#include "hedley/hedley.h"
#include "simde/simde/x86/avx2.h"
#include "portable-snippets/exact-int/exact-int.h"
namespace dpf
{
namespace prg
{
namespace chacha_detail
{
inline constexpr std::uint32_t zero_nonce[3] = {0, 0, 0};
/// ASCII `"dpf-chacha-prg"` plus two zero bytes. Public second half of the key.
inline constexpr std::uint8_t domain[16] = {
'd', 'p', 'f', '-', 'c', 'h', 'a', 'c',
'h', 'a', '-', 'p', 'r', 'g', 0, 0
};
HEDLEY_PURE
HEDLEY_NON_NULL(1)
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr std::uint32_t load_le32(const std::uint8_t * p) noexcept
{
return static_cast<std::uint32_t>(p[0])
| (static_cast<std::uint32_t>(p[1]) << 8)
| (static_cast<std::uint32_t>(p[2]) << 16)
| (static_cast<std::uint32_t>(p[3]) << 24);
}
HEDLEY_NON_NULL(1)
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
void store_le32(std::uint8_t * p, std::uint32_t w) noexcept
{
p[0] = static_cast<std::uint8_t>(w);
p[1] = static_cast<std::uint8_t>(w >> 8);
p[2] = static_cast<std::uint8_t>(w >> 16);
p[3] = static_cast<std::uint8_t>(w >> 24);
}
HEDLEY_PURE
HEDLEY_NON_NULL(1)
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
simde__m128i load_block(const std::uint8_t * p) noexcept
{
simde__m128i out;
std::memcpy(&out, p, sizeof(out));
return out;
}
/// 128-bit seed in the low half, `domain` in the high half, both little-endian.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
void seed_key(simde__m128i seed, std::uint32_t key[8]) noexcept
{
std::uint8_t raw[16];
std::memcpy(raw, &seed, sizeof(raw));
for (int i = 0; i < 4; ++i)
{
key[i] = load_le32(raw + 4 * i);
key[4 + i] = load_le32(domain + 4 * i);
}
}
template <int N>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr std::uint32_t rotl(std::uint32_t x) noexcept
{
static_assert(N > 0 && N < 32, "ChaCha rotation is between 1 and 31");
return (x << N) | (x >> (32 - N));
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
void quarter(std::uint32_t & a, std::uint32_t & b,
std::uint32_t & c, std::uint32_t & d) noexcept
{
a += b; d ^= a; d = rotl<16>(d);
c += d; b ^= c; b = rotl<12>(b);
a += b; d ^= a; d = rotl<8>(d);
c += d; b ^= c; b = rotl<7>(b);
}
template <int N>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
simde__m128i rotl_epi32(simde__m128i v) noexcept
{
static_assert(N > 0 && N < 32, "ChaCha rotation is between 1 and 31");
return simde_mm_or_si128(simde_mm_slli_epi32(v, N),
simde_mm_srli_epi32(v, 32 - N));
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
void quarter(simde__m128i x[], int a, int b, int c, int d) noexcept
{
x[a] = simde_mm_add_epi32(x[a], x[b]);
x[d] = rotl_epi32<16>(simde_mm_xor_si128(x[d], x[a]));
x[c] = simde_mm_add_epi32(x[c], x[d]);
x[b] = rotl_epi32<12>(simde_mm_xor_si128(x[b], x[c]));
x[a] = simde_mm_add_epi32(x[a], x[b]);
x[d] = rotl_epi32<8>(simde_mm_xor_si128(x[d], x[a]));
x[c] = simde_mm_add_epi32(x[c], x[d]);
x[b] = rotl_epi32<7>(simde_mm_xor_si128(x[b], x[c]));
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
std::uint32_t epi32_lane(simde__m128i v, int lane) noexcept
{
// Shuffle control is an immediate, so each lane is its own case.
switch (lane)
{
case 1: v = simde_mm_shuffle_epi32(v, 0x01); break;
case 2: v = simde_mm_shuffle_epi32(v, 0x02); break;
case 3: v = simde_mm_shuffle_epi32(v, 0x03); break;
default: break;
}
return static_cast<std::uint32_t>(simde_mm_cvtsi128_si32(v));
}
/// One ChaCha block. `key` is 8 little-endian words. `nonce` is 3 words.
template <unsigned Rounds>
HEDLEY_NO_THROW
void block(const std::uint32_t key[8], std::uint32_t counter,
const std::uint32_t nonce[3], std::uint8_t out[64]) noexcept
{
static_assert(Rounds >= 2 && Rounds % 2 == 0,
"ChaCha rounds must be a positive even number");
std::uint32_t s[16] = {
0x61707865u, 0x3320646eu, 0x79622d32u, 0x6b206574u,
key[0], key[1], key[2], key[3],
key[4], key[5], key[6], key[7],
counter, nonce[0], nonce[1], nonce[2]
};
std::uint32_t orig[16];
std::memcpy(orig, s, sizeof(orig));
HEDLEY_PRAGMA(GCC unroll 16)
for (unsigned r = 0; r < Rounds; r += 2)
{
quarter(s[0], s[4], s[8], s[12]);
quarter(s[1], s[5], s[9], s[13]);
quarter(s[2], s[6], s[10], s[14]);
quarter(s[3], s[7], s[11], s[15]);
quarter(s[0], s[5], s[10], s[15]);
quarter(s[1], s[6], s[11], s[12]);
quarter(s[2], s[7], s[8], s[13]);
quarter(s[3], s[4], s[9], s[14]);
}
for (int i = 0; i < 16; ++i)
{
store_le32(out + 4 * i, s[i] + orig[i]);
}
}
/// Four independent ChaCha blocks. Lane `i` uses `key[i]` and `counter[i]`.
/// Nonce is zero. Each `out[i]` receives 64 bytes.
template <unsigned Rounds>
HEDLEY_NO_THROW
void block4(const std::uint32_t key[][8], const std::uint32_t counter[4],
std::uint8_t out[][64]) noexcept
{
static_assert(Rounds >= 2 && Rounds % 2 == 0,
"ChaCha rounds must be a positive even number");
simde__m128i x[16];
x[0] = simde_mm_set1_epi32(static_cast<int>(0x61707865u));
x[1] = simde_mm_set1_epi32(static_cast<int>(0x3320646eu));
x[2] = simde_mm_set1_epi32(static_cast<int>(0x79622d32u));
x[3] = simde_mm_set1_epi32(static_cast<int>(0x6b206574u));
for (int w = 0; w < 8; ++w)
{
x[4 + w] = simde_mm_set_epi32(
static_cast<int>(key[3][w]),
static_cast<int>(key[2][w]),
static_cast<int>(key[1][w]),
static_cast<int>(key[0][w]));
}
x[12] = simde_mm_set_epi32(
static_cast<int>(counter[3]),
static_cast<int>(counter[2]),
static_cast<int>(counter[1]),
static_cast<int>(counter[0]));
x[13] = simde_mm_setzero_si128();
x[14] = simde_mm_setzero_si128();
x[15] = simde_mm_setzero_si128();
simde__m128i orig[16];
for (int i = 0; i < 16; ++i)
{
orig[i] = x[i];
}
HEDLEY_PRAGMA(GCC unroll 16)
for (unsigned r = 0; r < Rounds; r += 2)
{
quarter(x, 0, 4, 8, 12);
quarter(x, 1, 5, 9, 13);
quarter(x, 2, 6, 10, 14);
quarter(x, 3, 7, 11, 15);
quarter(x, 0, 5, 10, 15);
quarter(x, 1, 6, 11, 12);
quarter(x, 2, 7, 8, 13);
quarter(x, 3, 4, 9, 14);
}
for (int lane = 0; lane < 4; ++lane)
{
for (int w = 0; w < 16; ++w)
{
std::uint32_t sum = epi32_lane(x[w], lane) + epi32_lane(orig[w], lane);
store_le32(out[lane] + 4 * w, sum);
}
}
}
} // namespace chacha_detail
/// ChaCha stream PRG with `Rounds` rounds (20 is RFC 8439).
template <unsigned Rounds = 20>
struct chacha final
{
static_assert(Rounds >= 2 && Rounds % 2 == 0,
"ChaCha rounds must be a positive even number");
using block_type = simde__m128i;
static constexpr unsigned rounds = Rounds;
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
static block_type eval(block_type seed, psnip_uint32_t pos) noexcept
{
std::uint32_t key[8];
chacha_detail::seed_key(seed, key);
std::uint8_t buf[64];
chacha_detail::block<Rounds>(key, pos >> 2,
chacha_detail::zero_nonce, buf);
return chacha_detail::load_block(buf + 16 * (pos & 3u));
}
/// Positions 0 and 1, one ChaCha block (the first 32 keystream bytes).
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
static auto eval01(block_type seed) noexcept
{
std::uint32_t key[8];
chacha_detail::seed_key(seed, key);
std::uint8_t buf[64];
chacha_detail::block<Rounds>(key, 0, chacha_detail::zero_nonce, buf);
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
return std::array<block_type, 2>{
chacha_detail::load_block(buf),
chacha_detail::load_block(buf + 16)
};
HEDLEY_PRAGMA(GCC diagnostic pop)
}
HEDLEY_ALWAYS_INLINE
static void eval(block_type seed, block_type * HEDLEY_RESTRICT output,
psnip_uint32_t count, psnip_uint32_t pos = 0)
{
if (HEDLEY_UNLIKELY(count == 0))
{
return;
}
if (count > 1 &&
pos > static_cast<psnip_uint32_t>(~static_cast<psnip_uint32_t>(0)) - (count - 1u))
{
throw std::invalid_argument("prg lane index is out of range");
}
if (count == 1)
{
output[0] = eval(seed, pos);
return;
}
if (count == 2 && pos == 0)
{
auto kids = eval01(seed);
output[0] = kids[0];
output[1] = kids[1];
return;
}
std::uint32_t key[8];
chacha_detail::seed_key(seed, key);
psnip_uint32_t i = 0;
// `pos` may begin mid-block. Those chunks share one ChaCha block.
if ((pos & 3u) != 0u)
{
std::uint8_t buf[64];
chacha_detail::block<Rounds>(key, pos >> 2,
chacha_detail::zero_nonce, buf);
while (i < count && ((pos + i) & 3u) != 0u)
{
output[i] = chacha_detail::load_block(
buf + 16 * ((pos + i) & 3u));
++i;
}
}
// Four consecutive counters cover 16 output blocks.
while (i + 16u <= count)
{
std::uint32_t base = (pos + i) >> 2;
std::uint32_t keys[4][8];
std::uint32_t counters[4];
for (int lane = 0; lane < 4; ++lane)
{
std::memcpy(keys[lane], key, sizeof(key));
counters[lane] = base + static_cast<std::uint32_t>(lane);
}
std::uint8_t buf[4][64];
chacha_detail::block4<Rounds>(keys, counters, buf);
for (int lane = 0; lane < 4; ++lane)
{
for (int chunk = 0; chunk < 4; ++chunk)
{
output[i++] = chacha_detail::load_block(
buf[lane] + 16 * chunk);
}
}
}
while (i + 4u <= count)
{
std::uint8_t buf[64];
chacha_detail::block<Rounds>(key, (pos + i) >> 2,
chacha_detail::zero_nonce, buf);
for (int chunk = 0; chunk < 4; ++chunk)
{
output[i++] = chacha_detail::load_block(buf + 16 * chunk);
}
}
if (i < count)
{
std::uint8_t buf[64];
chacha_detail::block<Rounds>(key, (pos + i) >> 2,
chacha_detail::zero_nonce, buf);
unsigned chunk = 0;
while (i < count)
{
output[i++] = chacha_detail::load_block(buf + 16 * chunk);
++chunk;
}
}
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2, 3)
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT left,
block_type * HEDLEY_RESTRICT right) noexcept
{
std::uint32_t keys[4][8];
std::uint32_t counters[4] = {0, 0, 0, 0};
for (int lane = 0; lane < 4; ++lane)
{
chacha_detail::seed_key(seeds[lane], keys[lane]);
}
std::uint8_t buf[4][64];
chacha_detail::block4<Rounds>(keys, counters, buf);
for (int lane = 0; lane < 4; ++lane)
{
left[lane] = chacha_detail::load_block(buf[lane]);
right[lane] = chacha_detail::load_block(buf[lane] + 16);
}
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
{
std::uint32_t keys[4][8];
std::uint32_t ctr = pos >> 2;
std::uint32_t counters[4] = {ctr, ctr, ctr, ctr};
for (int lane = 0; lane < 4; ++lane)
{
chacha_detail::seed_key(seeds[lane], keys[lane]);
}
std::uint8_t buf[4][64];
chacha_detail::block4<Rounds>(keys, counters, buf);
unsigned chunk = pos & 3u;
for (int lane = 0; lane < 4; ++lane)
{
output[lane] = chacha_detail::load_block(buf[lane] + 16 * chunk);
}
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
{
eval_x4(seeds, output, pos);
eval_x4(seeds + 4, output + 4, pos);
}
/// Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`).
template <typename T, std::size_t Party>
HEDLEY_NO_THROW
static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept;
}; // struct chacha
/// RFC 8439 ChaCha20.
using chacha20 = chacha<20>;
/// ChaCha12. Same keying as `chacha20`, 12 rounds.
using chacha12 = chacha<12>;
/// ChaCha8. Same keying as `chacha20`, 8 rounds.
using chacha8 = chacha<8>;
} // namespace prg
} // namespace dpf
#endif // LIBDPF_INCLUDE_DPF_PRG_CHACHA_HPP__

View file

@ -1,6 +1,8 @@
/// @file dpf/prg_dummy.hpp
/// @brief
/// @details
/// @brief Identity PRG. `eval` returns the seed and ignores the lane.
/// @details Used where a PRG-shaped type is required and the block must stay
/// equal to the seed. The batched entry points write that seed into
/// every output lane.
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license;
@ -57,6 +59,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2, 3)
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT left,
block_type * HEDLEY_RESTRICT right) noexcept
@ -69,6 +72,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT output, psnip_uint32_t = 0) noexcept
{
@ -77,6 +81,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT output, psnip_uint32_t = 0) noexcept
{
@ -85,6 +90,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
/// Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`).
template <typename T, std::size_t Party>
HEDLEY_NO_THROW
static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept;
}; // struct dummy

View file

@ -11,6 +11,7 @@
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <stdexcept>
#include "hedley/hedley.h"
#include "simde/simde/x86/avx2.h"
@ -49,11 +50,15 @@ HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
HEDLEY_PRAGMA(GCC diagnostic pop)
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
static void eval(block_type seed, block_type * HEDLEY_RESTRICT output,
psnip_uint32_t count, psnip_uint32_t pos = 0) noexcept
psnip_uint32_t count, psnip_uint32_t pos = 0)
{
if (count > 1 &&
pos > static_cast<psnip_uint32_t>(~static_cast<psnip_uint32_t>(0)) - (count - 1u))
{
throw std::invalid_argument("prg lane index is out of range");
}
for (psnip_uint32_t i = 0; i < count; ++i)
{
output[i] = eval(seed, pos + i);
@ -62,6 +67,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2, 3)
static void eval01_x4(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT left,
block_type * HEDLEY_RESTRICT right) noexcept
@ -76,6 +82,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void eval_x4(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
{
@ -87,6 +94,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL(1, 2)
static void eval_x8(const block_type * HEDLEY_RESTRICT seeds,
block_type * HEDLEY_RESTRICT output, psnip_uint32_t pos = 0) noexcept
{
@ -98,9 +106,11 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
/// Raw-bit subtractive share of `T` for party `Party` (see `prg.hpp`).
template <typename T, std::size_t Party>
HEDLEY_NO_THROW
static auto expand(block_type seed, psnip_uint32_t pos = 0) noexcept;
private:
HEDLEY_NO_THROW
static lowmc::block to_block(block_type x) noexcept
{
std::uint64_t lane[2];
@ -114,6 +124,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
return b;
}
HEDLEY_NO_THROW
static block_type from_block(const lowmc::block & b) noexcept
{
std::uint64_t lane[2] = {0, 0};
@ -127,6 +138,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
return x;
}
HEDLEY_NO_THROW
static block_type permute(block_type x) noexcept
{
static lowmc::LowMC cipher;

View file

@ -39,6 +39,7 @@ inline thread_local void (*uniform_bytes_hook)(void *, std::size_t) = nullptr;
template <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
bool fill_from_hook(T & buf) noexcept
{
if (uniform_bytes_hook == nullptr)
@ -52,6 +53,7 @@ bool fill_from_hook(T & buf) noexcept
/// `bool` and `enum : bool` (including `dpf::bit`) have only two valid
/// representations. Filling them with a raw entropy byte is undefined.
template <typename T>
HEDLEY_NO_THROW
constexpr bool is_boolean_representation() noexcept
{
using U = std::remove_cv_t<T>;
@ -94,7 +96,8 @@ struct entropy_source
entropy_source(entropy_source &&) = delete;
entropy_source & operator=(entropy_source &&) = delete;
~entropy_source()
HEDLEY_NO_THROW
~entropy_source() noexcept
{
if (fp != nullptr)
{

View file

@ -1,3 +1,8 @@
/// @file dpf/rotated_iterable.hpp
/// @brief Retired container rotation view. The live type is `rotation_iterable`.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
// /// @file dpf/rotated_iterable.hpp
// /// @author Ryan Henry <ryan.henry@ucalgary.ca>
// /// @brief defines `dpf::rotated_iterable` and associated helpers

View file

@ -1,3 +1,11 @@
/// @file dpf/rotation_iterable.hpp
/// @brief A rotated view of an iterator range.
/// @details `begin()` starts `distance` elements into the wrapped range and
/// wraps back to the original begin. Indexing is O(1) when the
/// wrapped iterator is random-access.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
#ifndef LIBDPF_INCLUDE_DPF_ROTATED_VIEW_HPP__
#define LIBDPF_INCLUDE_DPF_ROTATED_VIEW_HPP__
@ -30,20 +38,14 @@ struct rotation_iterable
constexpr rotation_iterable(wrapped_iterator begin, wrapped_iterator end,
difference_type distance)
: size_{std::distance(begin, end)},
distance_{
[this, &distance]()
{
distance %= this->size_;
if (distance < 0)
{
distance += this->size_;
}
return distance;
}()},
begin_{std::next(begin, static_cast<difference_type>(distance_))},
distance_{normalize_distance(distance, size_)},
begin_{size_ == 0 ? begin : std::next(begin, distance_)},
wrap_to_{begin},
wrap_after_{std::next(end, -difference_type(distance_ > 0))},
end_after_{std::next(wrap_to_, static_cast<difference_type>(distance_-1))},
wrap_after_{size_ == 0 || distance_ == 0
? end : std::next(end, difference_type{-1})},
end_after_{size_ == 0 ? begin
: (distance_ == 0 ? std::next(end, difference_type{-1})
: std::next(begin, distance_ - 1))},
end_{end}
{ }
@ -52,11 +54,11 @@ struct rotation_iterable
constexpr rotation_iterable(wrapped_iterator begin, wrapped_iterator end,
wrapped_iterator middle)
: size_{std::distance(begin, end)},
distance_{std::distance(begin, middle)},
distance_{size_ == 0 ? difference_type{0} : std::distance(begin, middle)},
begin_{middle},
wrap_to_{begin},
wrap_after_{std::next(end, difference_type(-1))},
end_after_{std::next(middle, difference_type(-1))},
wrap_after_{size_ == 0 ? end : std::next(end, difference_type{-1})},
end_after_{size_ == 0 ? begin : std::next(middle, difference_type{-1})},
end_{end}
{ }
@ -136,6 +138,19 @@ struct rotation_iterable
}
private:
HEDLEY_CONST
HEDLEY_NO_THROW
static constexpr difference_type normalize_distance(difference_type distance,
difference_type size) noexcept
{
if (size == 0)
return difference_type{0};
distance %= size;
if (distance < 0)
distance += size;
return distance;
}
difference_type size_;
difference_type distance_;
wrapped_iterator begin_;
@ -163,6 +178,7 @@ struct rotation_iterator_base
std::add_const_t<reference>>;
using pointer = typename std::iterator_traits<WrappedIterator>::pointer;
HEDLEY_NO_THROW
rotation_iterator_base(const rotation_iterable<wrapped_iterator> & iterable_,
wrapped_iterator iterator) noexcept
: iterable{iterable_}, it{iterator} { }
@ -256,6 +272,7 @@ struct rotation_iterator final
using wrapped_iterator = WrappedIterator;
using reference = typename base::reference;
HEDLEY_NO_THROW
rotation_iterator(const rotation_iterable<WrappedIterator> & iterable,
wrapped_iterator iterator) noexcept
: base{iterable, iterator} {}
@ -277,6 +294,7 @@ struct rotation_const_iterator final
using wrapped_iterator = WrappedIterator;
using const_reference = typename base::const_reference;
HEDLEY_NO_THROW
rotation_const_iterator(const rotation_iterable<WrappedIterator> & iterable,
wrapped_iterator iterator) noexcept
: base{iterable, iterator} {}

View file

@ -99,10 +99,19 @@ namespace detail
template <sharing Scheme, std::size_t Party, typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr T party_coeff_times(const T & v) noexcept
{
if constexpr (Scheme == sharing::additive || Party == 0)
return v;
else if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
{
// Signed negation of the minimum is undefined. The two's-complement
// negation is well-defined on the unsigned width.
using unsigned_type = std::make_unsigned_t<T>;
return static_cast<T>(static_cast<unsigned_type>(0)
- static_cast<unsigned_type>(v));
}
else
return static_cast<T>(-v);
}
@ -122,13 +131,18 @@ struct secret_share
T value{};
secret_share() = default;
HEDLEY_NO_THROW
secret_share(const secret_share &) noexcept = default;
HEDLEY_NO_THROW
secret_share(secret_share &&) noexcept = default;
HEDLEY_NO_THROW
secret_share & operator=(const secret_share &) noexcept = default;
HEDLEY_NO_THROW
secret_share & operator=(secret_share &&) noexcept = default;
~secret_share() = default;
/// Bit-preserving construction. Does not apply a party coefficient.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
static constexpr secret_share from_raw(T v) noexcept
@ -138,15 +152,18 @@ struct secret_share
return s;
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
constexpr const T & raw() const noexcept { return value; }
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
constexpr T & raw() noexcept { return value; }
/// Secret-preserving conversion to an additive share of the same party.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
constexpr additive_share<T, Party> as_additive() const noexcept
@ -159,6 +176,7 @@ struct secret_share
}
/// Secret-preserving conversion to a subtractive share of the same party.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
constexpr subtractive_share<T, Party> as_subtractive() const noexcept
@ -174,18 +192,28 @@ struct secret_share
template <sharing NewScheme, std::size_t NewParty = Party>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, NewParty, NewScheme> retag() const noexcept
{
return secret_share<T, NewParty, NewScheme>::from_raw(value);
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
constexpr secret_share operator-() const noexcept
{
return from_raw(static_cast<T>(-value));
if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
{
using unsigned_type = std::make_unsigned_t<T>;
return from_raw(static_cast<T>(static_cast<unsigned_type>(0)
- static_cast<unsigned_type>(value)));
}
else
return from_raw(static_cast<T>(-value));
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr secret_share & operator+=(const secret_share & rhs) noexcept
{
@ -193,6 +221,7 @@ struct secret_share
return *this;
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr secret_share & operator-=(const secret_share & rhs) noexcept
{
@ -203,6 +232,7 @@ struct secret_share
template <typename Scalar,
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator*=(const Scalar & c) noexcept
{
value = static_cast<T>(value * static_cast<T>(c));
@ -214,6 +244,7 @@ struct secret_share
std::enable_if_t<!is_secret_share_v<Plain>
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator+=(const Plain & c) noexcept
{
if constexpr (Party == 0)
@ -225,6 +256,7 @@ struct secret_share
std::enable_if_t<!is_secret_share_v<Plain>
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr secret_share & operator-=(const Plain & c) noexcept
{
if constexpr (Party == 0)
@ -240,6 +272,7 @@ struct secret_share
template <typename T, std::size_t Party, sharing Scheme>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator+(
secret_share<T, Party, Scheme> lhs,
const secret_share<T, Party, Scheme> & rhs) noexcept
@ -251,6 +284,7 @@ constexpr secret_share<T, Party, Scheme> operator+(
template <typename T, std::size_t Party, sharing Scheme>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator-(
secret_share<T, Party, Scheme> lhs,
const secret_share<T, Party, Scheme> & rhs) noexcept
@ -263,6 +297,7 @@ template <typename T, std::size_t Party, sharing Scheme, typename Scalar,
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator*(
secret_share<T, Party, Scheme> lhs, const Scalar & c) noexcept
{
@ -274,6 +309,7 @@ template <typename T, std::size_t Party, sharing Scheme, typename Scalar,
std::enable_if_t<!is_secret_share_v<Scalar>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator*(
const Scalar & c, secret_share<T, Party, Scheme> rhs) noexcept
{
@ -290,6 +326,7 @@ template <typename T, std::size_t Party, sharing LhsScheme, sharing RhsScheme,
std::enable_if_t<LhsScheme != RhsScheme, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, LhsScheme> operator+(
const secret_share<T, Party, LhsScheme> & lhs,
const secret_share<T, Party, RhsScheme> & rhs) noexcept
@ -306,6 +343,7 @@ template <typename T, std::size_t Party, sharing LhsScheme, sharing RhsScheme,
std::enable_if_t<LhsScheme != RhsScheme, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, LhsScheme> operator-(
const secret_share<T, Party, LhsScheme> & lhs,
const secret_share<T, Party, RhsScheme> & rhs) noexcept
@ -327,6 +365,7 @@ template <typename T, std::size_t Party, sharing Scheme, typename Plain,
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator+(
secret_share<T, Party, Scheme> lhs, const Plain & c) noexcept
{
@ -339,6 +378,7 @@ template <typename T, std::size_t Party, sharing Scheme, typename Plain,
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator+(
const Plain & c, secret_share<T, Party, Scheme> rhs) noexcept
{
@ -351,6 +391,7 @@ template <typename T, std::size_t Party, sharing Scheme, typename Plain,
&& std::is_convertible_v<Plain, T>, int> = 0>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr secret_share<T, Party, Scheme> operator-(
secret_share<T, Party, Scheme> lhs, const Plain & c) noexcept
{
@ -365,6 +406,7 @@ constexpr secret_share<T, Party, Scheme> operator-(
template <typename T, std::size_t Party, sharing Scheme>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr bool operator==(const secret_share<T, Party, Scheme> & lhs,
const secret_share<T, Party, Scheme> & rhs) noexcept
{
@ -374,6 +416,7 @@ constexpr bool operator==(const secret_share<T, Party, Scheme> & lhs,
template <typename T, std::size_t Party, sharing Scheme>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr bool operator!=(const secret_share<T, Party, Scheme> & lhs,
const secret_share<T, Party, Scheme> & rhs) noexcept
{
@ -387,10 +430,21 @@ constexpr bool operator!=(const secret_share<T, Party, Scheme> & lhs,
template <typename T, sharing Scheme>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr T reconstruct(const secret_share<T, 0, Scheme> & s0,
const secret_share<T, 1, Scheme> & s1) noexcept
{
if constexpr (Scheme == sharing::additive)
if constexpr (std::is_integral_v<T> && std::is_signed_v<T>)
{
using unsigned_type = std::make_unsigned_t<T>;
if constexpr (Scheme == sharing::additive)
return static_cast<T>(static_cast<unsigned_type>(s0.raw())
+ static_cast<unsigned_type>(s1.raw()));
else
return static_cast<T>(static_cast<unsigned_type>(s0.raw())
- static_cast<unsigned_type>(s1.raw()));
}
else if constexpr (Scheme == sharing::additive)
return static_cast<T>(s0.raw() + s1.raw());
else
return static_cast<T>(s0.raw() - s1.raw());
@ -399,6 +453,7 @@ constexpr T reconstruct(const secret_share<T, 0, Scheme> & s0,
template <typename T, sharing Scheme>
HEDLEY_ALWAYS_INLINE
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr T reconstruct(const secret_share<T, 1, Scheme> & s1,
const secret_share<T, 0, Scheme> & s0) noexcept
{
@ -412,6 +467,7 @@ constexpr T reconstruct(const secret_share<T, 1, Scheme> & s1,
template <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
constexpr auto make_additive_shares(T secret) noexcept
{
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
@ -423,6 +479,7 @@ constexpr auto make_additive_shares(T secret) noexcept
template <typename T>
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
HEDLEY_NO_THROW
constexpr auto make_subtractive_shares(T secret) noexcept
{
using T_ = std::remove_cv_t<std::remove_reference_t<T>>;
@ -460,13 +517,16 @@ struct party_key : Key
#endif
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
Key & key() noexcept { return static_cast<Key &>(*this); }
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
const Key & key() const noexcept { return static_cast<const Key &>(*this); }
/// Party-tagged additive share of the comparison absorb addend.
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
auto cmp_addend() const noexcept
{

View file

@ -1,6 +1,13 @@
/// @file dpf/sequence_memoizer.hpp
/// @brief
/// @details
/// @brief Workspaces for a `sequence_recipe` traversal.
/// @details The memoizer stores a reference to the recipe it was built from
/// and later calls must pass that same object (`std::logic_error`
/// otherwise). The factories unwrap `party_key`.
///
/// `inplace_reversing_sequence_memoizer` keeps one level.
/// `double_space_sequence_memoizer` keeps two and is the default
/// inside `eval_sequence(key, recipe, buffer)`.
/// `full_tree_sequence_memoizer` keeps every level.
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
@ -22,6 +29,7 @@
#include <stdexcept>
#include <optional>
#include "dpf/secret_share.hpp"
#include "dpf/sequence_recipe.hpp"
namespace dpf
@ -42,10 +50,13 @@ struct sequence_recipe_memoizer_base : public sequence_memoizer_tag_
// level 0 should access the root
// level goes up to (and including) depth
HEDLEY_NO_THROW
virtual return_type operator[](std::size_t) const noexcept = 0;
// iterators should access most recently completed level
HEDLEY_NO_THROW
virtual return_type begin() const noexcept = 0;
HEDLEY_NO_THROW
virtual return_type end() const noexcept = 0;
virtual std::size_t assign_dpf(const dpf_type & dpf, const sequence_recipe & r)
@ -210,11 +221,13 @@ struct pointer_facade
return *this;
}
HEDLEY_NO_THROW
pointer_facade operator+(std::size_t n) const noexcept
{
return pointer_facade(flip_, it_ + n, rit_ + n);
}
HEDLEY_NO_THROW
pointer_facade & operator-=(std::size_t n) noexcept
{
it_ -= n;
@ -222,11 +235,13 @@ struct pointer_facade
return *this;
}
HEDLEY_NO_THROW
pointer_facade operator-(std::size_t n) const noexcept
{
return pointer_facade(flip_, it_ - n, rit_ - n);
}
HEDLEY_NO_THROW
difference_type operator-(pointer_facade rhs) const noexcept
{
return std::make_pair(it_ - rhs.it_, rit_ - rhs.rit_);
@ -235,7 +250,7 @@ struct pointer_facade
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
HEDLEY_PURE
reference operator[](std::size_t i)
reference operator[](std::size_t i) noexcept
{
return flip_ ? rit_[i] : it_[i];
}
@ -243,7 +258,7 @@ struct pointer_facade
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
HEDLEY_PURE
const_reference operator[](std::size_t i) const
const_reference operator[](std::size_t i) const noexcept
{
return flip_ ? rit_[i] : it_[i];
}
@ -270,6 +285,8 @@ struct pointer_facade
} // namespace detail
/// One level. The buffer is traversed in the opposite direction on
/// alternate levels.
template <typename DpfKey,
typename Allocator = aligned_allocator<typename DpfKey::interior_node>>
struct inplace_reversing_sequence_memoizer final
@ -364,6 +381,8 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
unique_ptr buf;
};
/// Two levels, so a level can be built while the previous level is still
/// intact. Default workspace for `eval_sequence` on a recipe.
template <typename DpfKey,
typename Allocator = aligned_allocator<typename DpfKey::interior_node>>
struct double_space_sequence_memoizer final
@ -416,6 +435,7 @@ HEDLEY_PRAGMA(GCC diagnostic pop)
unique_ptr buf;
};
/// Every level of the recipe's traversal.
template <typename DpfKey,
typename Allocator = aligned_allocator<typename DpfKey::interior_node>>
struct full_tree_sequence_memoizer final
@ -482,10 +502,15 @@ auto make_sequence_memoizer(const sequence_recipe & recipe)
HEDLEY_PRAGMA(GCC diagnostic push)
HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes")
/// One-level sequence workspace bound to `recipe`.
/// @param recipe The object later passed to `eval_sequence`. The memoizer
/// holds a reference to it.
/// @snippet evaluation/memoizers.cpp sequence-memoizer
template <typename DpfKey>
inline auto make_inplace_reversing_sequence_memoizer(const sequence_recipe & recipe)
{
return detail::make_sequence_memoizer<inplace_reversing_sequence_memoizer<DpfKey>>(recipe);
using key_t = unwrap_party_key_t<DpfKey>;
return detail::make_sequence_memoizer<inplace_reversing_sequence_memoizer<key_t>>(recipe);
}
template <typename DpfKey>
@ -494,10 +519,13 @@ inline auto make_inplace_reversing_sequence_memoizer(const DpfKey &, const seque
return make_inplace_reversing_sequence_memoizer<DpfKey>(recipe);
}
/// Two-level sequence workspace bound to `recipe`.
/// @snippet evaluation/eval_sequence.cpp eval-sequence-recipe
template <typename DpfKey>
inline auto make_double_space_sequence_memoizer(const sequence_recipe & recipe)
{
return detail::make_sequence_memoizer<double_space_sequence_memoizer<DpfKey>>(recipe);
using key_t = unwrap_party_key_t<DpfKey>;
return detail::make_sequence_memoizer<double_space_sequence_memoizer<key_t>>(recipe);
}
template <typename DpfKey>
@ -506,10 +534,12 @@ inline auto make_double_space_sequence_memoizer(const DpfKey &, const sequence_r
return make_double_space_sequence_memoizer<DpfKey>(recipe);
}
/// Full-tree sequence workspace bound to `recipe`.
template <typename DpfKey>
inline auto make_full_tree_sequence_memoizer(const sequence_recipe & recipe)
{
return detail::make_sequence_memoizer<full_tree_sequence_memoizer<DpfKey>>(recipe);
using key_t = unwrap_party_key_t<DpfKey>;
return detail::make_sequence_memoizer<full_tree_sequence_memoizer<key_t>>(recipe);
}
template <typename DpfKey>

View file

@ -1,6 +1,9 @@
/// @file dpf/sequence_recipe.hpp
/// @brief
/// @details
/// @brief Compiled traversal of a sorted DPF point list.
/// @details `make_sequence_recipe` requires a nondecreasing range and throws
/// `std::runtime_error` otherwise. The recipe is independent of
/// correction words, so one recipe serves every key of that input
/// type. A sequence memoizer is bound to a particular recipe object.
/// @author Ryan Henry <ryan.henry@ucalgary.ca>
/// @author Christopher Jiang <christopher.jiang@ucalgary.ca>
/// @copyright Copyright (c) 2019-2024 Ryan Henry and [others](@ref authors)
@ -10,6 +13,8 @@
#ifndef LIBDPF_INCLUDE_DPF_SEQUENCE_RECIPE_HPP__
#define LIBDPF_INCLUDE_DPF_SEQUENCE_RECIPE_HPP__
#include "hedley/hedley.h"
#include <cstddef>
#include <type_traits>
#include <algorithm>
@ -21,6 +26,7 @@
namespace dpf
{
/// Steps, leaf count, and per-level endpoints for one sorted point list.
struct sequence_recipe
{
public:
@ -34,10 +40,21 @@ struct sequence_recipe
level_endpoints_{level_endpoints}
{ }
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr const std::vector<int8_t> & recipe_steps() const noexcept { return recipe_steps_; }
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr const std::vector<std::size_t> & output_indices() const noexcept { return output_indices_; }
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr std::size_t num_leaf_nodes() const noexcept { return num_leaf_nodes_; }
HEDLEY_PURE
HEDLEY_NO_THROW
constexpr const std::vector<std::size_t> & level_endpoints() const noexcept { return level_endpoints_; }
/// `level_endpoints().size() - 1`. Not `constexpr`: `std::vector::size` is not a constant expression in C++17.
HEDLEY_PURE
HEDLEY_NO_THROW
std::size_t depth() const noexcept { return level_endpoints_.size()-1; }
private:
@ -122,6 +139,10 @@ auto make_sequence_recipe(ForwardIterator begin, ForwardIterator end)
} // namespace detail
/// Compile `[begin, end)` into a recipe for `DpfKey`'s input type.
/// @tparam DpfKey Key type, or a `party_key` of that key. Only the input
/// type and depth are used.
/// @throws std::runtime_error if the range is not sorted nondecreasing.
template <typename DpfKey,
typename ForwardIterator>
auto make_sequence_recipe(ForwardIterator begin, ForwardIterator end)

View file

@ -1,14 +1,23 @@
/// @file dpf/sequence_utils.hpp
/// @brief Tags that select how `eval_sequence` stores each visited leaf.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
#ifndef LIBDPF_INCLUDE_DPF_SEQUENCE_UTILS_HPP__
#define LIBDPF_INCLUDE_DPF_SEQUENCE_UTILS_HPP__
namespace dpf
{
/// Tag base for `eval_sequence` storage layout.
struct return_type_tag_{};
/// Store whole leaves. Default for `eval_sequence`. The iterable still
/// yields one share per listed point.
struct return_entire_node_tag_ final : public return_type_tag_ {};
// static constexpr auto return_entire_node_tag = return_entire_node_tag_{};
/// Store one share per listed point.
struct return_output_only_tag_ final : public return_type_tag_ {};
// static constexpr auto return_output_only_tag = return_output_only_tag_{};

View file

@ -43,7 +43,10 @@ class setbit_index_iterable
explicit setbit_index_iterable(iter it) noexcept
: it_{std::move(it)},
begin_{it_.it_.word_ptr_},
end_{begin_ + it_.buf_size_},
// `buf_size_` is a bit count (`bit_array::size()`). The sentinel word
// sits one past the last data word, and the end iterator has to land
// on it rather than `buf_size_` words later.
end_{begin_ + utils::quotient_ceiling(it_.buf_size_, bits_per_word)},
base_index_{calc_base_index(it_)},
length_{calc_length(it_)}
{
@ -89,6 +92,7 @@ class setbit_index_iterable
return it.outputs_ == 0 ? it.from_ : utils::quotient_floor(it.from_, it.outputs_) * it.outputs_;
}
HEDLEY_NO_THROW
static constexpr size_type calc_length(const iter & it) noexcept
{
return it.outputs_ == 0 ? utils::quotient_ceiling(it.to_, bits_per_word) * bits_per_word : utils::quotient_ceiling(it.to_, it.outputs_) * it.outputs_;
@ -102,7 +106,7 @@ class setbit_index_iterable
// while other words just need some bits to be masked out
constexpr void update_bit_array()
{
if (it_.outputs_ == 0)
if (it_.outputs_ == 0 || begin_ == end_)
{
return;
}
@ -124,11 +128,16 @@ class setbit_index_iterable
cur = end_-1;
loc = it_.to_ % it_.outputs_;
while (loc < it_.outputs_ - bits_per_word)
// `outputs_ - bits_per_word` underflows when a leaf is narrower than
// a word, and the loop then walks off the front of the buffer.
if (it_.outputs_ > bits_per_word)
{
*cur = zero;
--cur;
loc += bits_per_word;
while (loc < it_.outputs_ - bits_per_word)
{
*cur = zero;
--cur;
loc += bits_per_word;
}
}
*cur &= mask;
}
@ -151,12 +160,16 @@ class const_setbit_iterator
using difference_type = std::ptrdiff_t;
static constexpr auto bits_per_word = array_type::bits_per_word;
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr const_setbit_iterator(const_setbit_iterator &&) noexcept = default;
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr const_setbit_iterator(const const_setbit_iterator &) noexcept = default;
HEDLEY_NO_THROW
~const_setbit_iterator() noexcept = default;
HEDLEY_NO_THROW
const_setbit_iterator & operator=(const_setbit_iterator &&) noexcept = default;
const_setbit_iterator & operator=(const const_setbit_iterator &) = default;
@ -279,6 +292,7 @@ class const_setbit_iterator
struct const_iterator_end_tag final {};
struct const_iterator_begin_tag final {};
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL()
explicit constexpr const_setbit_iterator(word_pointer word_ptr,
@ -290,6 +304,7 @@ class const_setbit_iterator
seek_to_next_bit();
}
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_NON_NULL()
explicit constexpr const_setbit_iterator(word_pointer word_ptr,
@ -303,13 +318,16 @@ class const_setbit_iterator
word_type current_word_;
size_type base_index_;
HEDLEY_NO_THROW
friend const_setbit_iterator setbit_index_iterable<ChildT, WordT>::begin() const noexcept;
HEDLEY_NO_THROW
friend const_setbit_iterator setbit_index_iterable<ChildT, WordT>::end() const noexcept;
}; // class dpf::const_setbit_iterator
template <typename ChildT,
typename WordT>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
dpf::setbit_index_iterable<ChildT, WordT> indices_set_in(const subinterval_iterable<bit_iterator<ChildT, WordT>> & iter) noexcept
{
return dpf::setbit_index_iterable<ChildT, WordT>{iter};
@ -318,6 +336,7 @@ dpf::setbit_index_iterable<ChildT, WordT> indices_set_in(const subinterval_itera
template <typename ChildT,
typename WordT>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
dpf::setbit_index_iterable<ChildT, WordT> indices_set_in(subinterval_iterable<bit_iterator<ChildT, WordT>> && iter) noexcept
{
return dpf::setbit_index_iterable<ChildT, WordT>{std::forward<subinterval_iterable<bit_iterator<ChildT, WordT>>>(iter)};

View file

@ -80,16 +80,20 @@ class subsequence_iterable
using subsequence_iterator_type = points_iterator;
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr const_iterator(output_iterator out_it, subsequence_iterator_type it) noexcept
: out_it_{out_it}, it_{it}
{ }
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr const_iterator(const_iterator &&) noexcept = default;
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr const_iterator(const const_iterator &) noexcept = default;
HEDLEY_NO_THROW
const_iterator & operator=(const_iterator &&) noexcept = default;
const_iterator & operator=(const const_iterator &) = default;
~const_iterator() = default;
@ -143,11 +147,13 @@ class subsequence_iterable
return *this;
}
HEDLEY_NO_THROW
const_iterator operator+(std::size_t n) const noexcept
{
return const_iterator(out_it_ + outputs_per_leaf*n, it_ + n);
}
HEDLEY_NO_THROW
const_iterator & operator-=(std::size_t n) noexcept
{
it_ -= n;
@ -155,16 +161,19 @@ class subsequence_iterable
return *this;
}
HEDLEY_NO_THROW
const_iterator operator-(std::size_t n) const noexcept
{
return const_iterator(out_it_ - outputs_per_leaf*n, it_ - n);
}
HEDLEY_NO_THROW
difference_type operator-(const_iterator rhs) const noexcept
{
return it_ - rhs.it_;
}
HEDLEY_NO_THROW
reference operator[](std::size_t i) const noexcept
{
return out_it_[i*outputs_per_leaf + mod(it_[i], lg_outputs_per_leaf)];
@ -279,17 +288,21 @@ class recipe_subsequence_iterable
using subsequence_iterator_type = typename std::vector<std::size_t>::const_iterator;
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr const_iterator(output_iterator out_it, subsequence_iterator_type it) noexcept
: out_it_{out_it}, it_{it}
{ }
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr const_iterator(const_iterator &&) noexcept = default;
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr const_iterator(const const_iterator &) noexcept = default;
const_iterator & operator=(const const_iterator &) = default;
HEDLEY_NO_THROW
const_iterator & operator=(const_iterator &&) noexcept = default;
~const_iterator() = default;
@ -339,27 +352,32 @@ class recipe_subsequence_iterable
return *this;
}
HEDLEY_NO_THROW
const_iterator operator+(std::size_t n) const noexcept
{
return const_iterator(out_it_, it_ + n);
}
HEDLEY_NO_THROW
const_iterator & operator-=(std::size_t n) noexcept
{
it_ -= n;
return *this;
}
HEDLEY_NO_THROW
const_iterator operator-(std::size_t n) const noexcept
{
return const_iterator(out_it_, it_ - n);
}
HEDLEY_NO_THROW
difference_type operator-(const_iterator rhs) const noexcept
{
return it_ - rhs.it_;
}
HEDLEY_NO_THROW
reference operator[](std::size_t i) const noexcept
{
return out_it_[it_[i]];

View file

@ -152,6 +152,7 @@ template <>
struct make_from_integral_value<dpf::twobit>
{
using integral_type = std::uint8_t;
HEDLEY_NO_THROW
constexpr dpf::twobit operator()(integral_type val) const noexcept
{
return dpf::to_twobit(val);
@ -184,8 +185,11 @@ class numeric_limits<dpf::twobit> : public numeric_limits<std::uint8_t>
public:
static constexpr int digits = 2;
static constexpr int digits10 = 0;
HEDLEY_NO_THROW
static constexpr dpf::twobit min() noexcept { return dpf::twobit::zero; }
HEDLEY_NO_THROW
static constexpr dpf::twobit max() noexcept { return dpf::twobit::three; }
HEDLEY_NO_THROW
static constexpr dpf::twobit lowest() noexcept { return min(); }
};

View file

@ -1,3 +1,8 @@
/// @file dpf/uint256_t.hpp
/// @brief Leaf arithmetic for `uint256_t` and the 128-bit SIMD lanes.
/// @copyright Copyright (c) 2019-2026 Ryan Henry and [others](@ref authors)
/// @license Released under a GNU General Public v2.0 (GPLv2) license.
#ifndef LIBDPF_INCLUDE_DPF_UINT256_T_HPP__
#define LIBDPF_INCLUDE_DPF_UINT256_T_HPP__
@ -205,6 +210,7 @@ struct msb_of<uint256_t>
template <>
struct mod_pow_2<uint128_t>
{
HEDLEY_NO_THROW
std::size_t operator()(uint128_t val, std::size_t n) const noexcept
{
return mod_pow_2<uint64_t>{}(static_cast<uint64_t>(val.lower()), n);
@ -214,6 +220,7 @@ struct mod_pow_2<uint128_t>
template <>
struct mod_pow_2<uint256_t>
{
HEDLEY_NO_THROW
std::size_t operator()(uint256_t val, std::size_t n) const noexcept
{
return mod_pow_2<uint128_t>{}(val.lower(), n);
@ -227,6 +234,7 @@ struct to_integral_type<uint128_t>
using parent = to_integral_type_base<uint128_t>;
using typename parent::integral_type;
HEDLEY_NO_THROW
constexpr integral_type operator()(uint128_t val) const noexcept
{
return (simde_uint128(val.upper()) << 64) | simde_uint128(val.lower());
@ -240,6 +248,7 @@ struct to_integral_type<uint256_t>
using parent = to_integral_type_base<uint256_t>;
using typename parent::integral_type;
HEDLEY_NO_THROW
constexpr integral_type operator()(uint256_t val) const noexcept
{
return val;

View file

@ -69,14 +69,23 @@ class numeric_limits<::uint128_t>
static constexpr bool traps = false;
static constexpr bool tinyness_before = false;
HEDLEY_NO_THROW
static constexpr uint128_t min() noexcept { return uint128_t{0ul, 0ul}; }
HEDLEY_NO_THROW
static constexpr uint128_t lowest() noexcept { return uint128_t{0ul, 0ul}; }
HEDLEY_NO_THROW
static constexpr uint128_t max() noexcept { return uint128_t{-1ul, -1ul}; }
HEDLEY_NO_THROW
static constexpr uint128_t epsilon() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr uint128_t round_error() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr uint128_t infinity() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr uint128_t quiet_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr uint128_t signaling_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr uint128_t denorm_min() noexcept { return 0; }
};
@ -127,14 +136,23 @@ class numeric_limits<::uint256_t>
static constexpr bool traps = false;
static constexpr bool tinyness_before = false;
HEDLEY_NO_THROW
static constexpr uint256_t min() noexcept { return uint256_t{uint128_t{0ul, 0ul}, uint128_t{0ul, 0ul}}; }
HEDLEY_NO_THROW
static constexpr uint256_t lowest() noexcept { return uint256_t{uint128_t{0ul, 0ul}, uint128_t{0ul, 0ul}}; }
HEDLEY_NO_THROW
static constexpr uint256_t max() noexcept { return uint256_t{uint128_t{-1ul, -1ul}, uint128_t{-1ul, -1ul}}; }
HEDLEY_NO_THROW
static constexpr uint256_t epsilon() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr uint256_t round_error() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr uint256_t infinity() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr uint256_t quiet_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr uint256_t signaling_NaN() noexcept { return 0; }
HEDLEY_NO_THROW
static constexpr uint256_t denorm_min() noexcept { return 0; }
};
@ -266,6 +284,7 @@ auto make_bitset(Bools ...bs)
}
template <typename NodeT>
HEDLEY_NO_THROW
static NodeT single_bit_mask(std::size_t i) noexcept;
template <>
@ -449,6 +468,7 @@ struct make_from_integral_value
// for `unsigned __int128`.
using integral_type = typename make_signed_if<S_integral_type,
std::is_signed_v<T> && sizeof(S_integral_type) <= 8>::type;
HEDLEY_NO_THROW
constexpr T operator()(integral_type val) const noexcept
{
return static_cast<T>(val);
@ -459,6 +479,7 @@ struct make_from_integral_value
/// `static_cast<T>(x0 ^ x1)`: for `keyword`, `operator^` yields the parent
/// `modint`, which cannot convert back through the private keyword ctor.
template <typename T>
HEDLEY_NO_THROW
constexpr T xor_input_shares(T x0, T x1) noexcept
{
constexpr auto to_int = to_integral_type<T>{};
@ -490,6 +511,7 @@ static constexpr IntegralT get_node_mask(InputT mask, std::size_t level_index)
/// width is undefined for the native unsigned types).
template <typename IntegralT>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr IntegralT shift_right(IntegralT value, std::size_t offset) noexcept
{
if (offset >= bitlength_of_v<IntegralT>)
@ -500,6 +522,7 @@ constexpr IntegralT shift_right(IntegralT value, std::size_t offset) noexcept
/// Floor of `from_inclusive / 2^lg_opl`. `lg_opl` is `log2(outputs_per_leaf)`.
template <typename IntegralT>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr IntegralT leaf_node_floor(IntegralT from_inclusive, std::size_t lg_opl) noexcept
{
if (lg_opl == 0)
@ -514,6 +537,7 @@ constexpr IntegralT leaf_node_floor(IntegralT from_inclusive, std::size_t lg_opl
/// end (`[from, 2^width)`), which `split_leaf_nodes` interprets.
template <typename IntegralT>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr IntegralT leaf_node_ceil_exclusive(IntegralT to_inclusive, std::size_t lg_opl) noexcept
{
constexpr std::size_t width = bitlength_of_v<IntegralT>;
@ -585,25 +609,87 @@ struct node_segments
std::size_t total = 0;
};
/// True when the inclusive walk `[from, to]` wraps the low `bits` of the
/// domain. Comparison is on the post-MSB-flip bit pattern. Leaf ids alone
/// cannot carry this: packing can put a wrapping pair into `from_node <= to_node`.
template <typename IntegralT>
inline bool interval_wraps(IntegralT from, IntegralT to, std::size_t bits)
{
if (bits == 0)
return false;
if (bits < bitlength_of_v<IntegralT>)
{
const IntegralT mask = static_cast<IntegralT>(
(IntegralT{1} << bits) - IntegralT{1});
return (from & mask) > (to & mask);
}
return from > to;
}
/// Split an inclusive output interval, already reduced to leaf ids, into one
/// or two half-open walks. A linearized `from_node > to_node` wraps the node
/// id space `[0, 2^depth)`. A saturated `to_node == 0` means the exclusive end
/// is `2^{bitwidth(IntegralT)}`, which is the whole id space when `depth` is
/// that width.
///
/// `input_wraps` is the order of the original inputs, before leaf coarsening.
/// The buffer is still two runs, `[from_node, 2^depth)` then `[0, to_node)`,
/// even when packing makes `from_node <= to_node`. In that case the runs
/// overlap on the shared leaf: the iterable's preclip consumes the start of
/// the first copy and its length stops inside the second. Collapsing the
/// overlap into one forward segment writes the wrong leaves.
template <typename IntegralT>
inline node_segments<IntegralT> split_leaf_nodes(IntegralT from_node,
IntegralT to_node, std::size_t depth)
IntegralT to_node, std::size_t depth, bool input_wraps = false)
{
node_segments<IntegralT> out;
constexpr std::size_t width = bitlength_of_v<IntegralT>;
constexpr std::size_t size_digits = bitlength_of_v<std::size_t>;
auto push = [&](IntegralT lo, IntegralT hi)
{
const std::size_t count = static_cast<std::size_t>(hi - lo);
std::size_t count = 0;
if (hi == IntegralT{0} && lo != IntegralT{0})
{
// Exclusive end is 2^width. The count fits in size_t only when
// that power is one past size_t's maximum and lo is nonzero.
if (width > size_digits)
throw std::length_error("DPF leaf domain does not fit in size_t");
count = static_cast<std::size_t>(0) - static_cast<std::size_t>(lo);
}
else
{
const auto wide = hi - lo;
if (wide > IntegralT(std::numeric_limits<std::size_t>::max()))
throw std::length_error("DPF leaf domain does not fit in size_t");
count = static_cast<std::size_t>(wide);
}
if (count == 0)
return;
if (out.total > std::numeric_limits<std::size_t>::max() - count)
throw std::length_error("DPF leaf domain does not fit in size_t");
out.seg[out.n++] = node_segment<IntegralT>{lo, hi, count};
out.total += count;
};
if (input_wraps)
{
if (depth >= width)
{
if (from_node == IntegralT{0})
throw std::length_error("DPF leaf domain does not fit in size_t");
push(from_node, IntegralT{0});
if (to_node != IntegralT{0})
push(IntegralT{0}, to_node);
return out;
}
const IntegralT domain_end = static_cast<IntegralT>(IntegralT{1} << depth);
if (from_node < domain_end)
push(from_node, domain_end);
if (to_node != IntegralT{0})
push(IntegralT{0}, to_node);
return out;
}
if (to_node != IntegralT{0} && from_node < to_node)
{
push(from_node, to_node);
@ -612,7 +698,6 @@ inline node_segments<IntegralT> split_leaf_nodes(IntegralT from_node,
if (to_node != IntegralT{0} && from_node == to_node)
return out;
constexpr std::size_t width = bitlength_of_v<IntegralT>;
if (from_node == IntegralT{0} && to_node == IntegralT{0})
throw std::length_error("DPF leaf domain does not fit in size_t");
@ -638,14 +723,26 @@ static constexpr std::size_t get_leafnodes_in_node_interval(IntegralT from_node,
return static_cast<std::size_t>(to_node - from_node);
}
template <typename T>
inline void flip_msb_if_signed_integral(T & x);
template <typename DpfKey,
typename InputT = typename DpfKey::input_type,
typename IntegralT = typename DpfKey::integral_type>
static std::size_t get_leafnodes_in_output_interval(InputT from, InputT to)
{
return split_leaf_nodes(get_from_node<DpfKey, InputT, IntegralT>(from),
get_to_node<DpfKey, InputT, IntegralT>(to),
static_cast<std::size_t>(DpfKey::depth)).total;
// Match eval: the walk order is the bit pattern after the sign flip.
InputT flipped_from = from;
InputT flipped_to = to;
flip_msb_if_signed_integral(flipped_from);
flip_msb_if_signed_integral(flipped_to);
constexpr auto to_int = to_integral_type<InputT>{};
const auto from_i = static_cast<IntegralT>(to_int(flipped_from));
const auto to_i = static_cast<IntegralT>(to_int(flipped_to));
const bool wraps = interval_wraps(from_i, to_i, bitlength_of_v<InputT>);
return split_leaf_nodes(get_from_node<DpfKey, InputT, IntegralT>(flipped_from),
get_to_node<DpfKey, InputT, IntegralT>(flipped_to),
static_cast<std::size_t>(DpfKey::depth), wraps).total;
}
/// Historical name used by the test suite.
@ -660,6 +757,7 @@ static std::size_t get_nodes_in_interval(InputT from, InputT to)
template <typename T>
struct mod_pow_2
{
HEDLEY_NO_THROW
std::size_t operator()(T val, std::size_t n) const noexcept
{
if (n == 0)
@ -696,6 +794,7 @@ static constexpr auto msb_of_v = msb_of<T>::value;
template <typename T>
struct countl_zero
{
HEDLEY_NO_THROW
HEDLEY_CONST
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(T val) const noexcept
@ -724,6 +823,7 @@ struct countl_zero
template <typename T>
struct countr_zero
{
HEDLEY_NO_THROW
HEDLEY_CONST
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(T val) const noexcept
@ -756,6 +856,7 @@ struct countr_zero
template <typename T>
struct countl_zero_symmetric_difference
{
HEDLEY_NO_THROW
HEDLEY_CONST
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(T lhs, T rhs) const noexcept
@ -773,6 +874,7 @@ struct countl_zero<simde_int128>
{
using T = simde_int128;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(const T & val) const noexcept
@ -795,6 +897,7 @@ struct countl_zero<simde_uint128>
{
using T = simde_uint128;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(const T & val) const noexcept
@ -812,6 +915,7 @@ struct countl_zero<uint128_t>
{
using T = uint128_t;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(const T & val) const noexcept
@ -828,6 +932,7 @@ struct countl_zero<uint256_t>
{
using T = uint256_t;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(const T & val) const noexcept
@ -844,6 +949,7 @@ struct countl_zero<simde__m128i>
{
using T = simde__m128i;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
std::size_t operator()(const T & val) const noexcept
@ -860,6 +966,7 @@ template <>
struct countl_zero<simde__m256i>
{
using T = simde__m256i;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(const T & val) const noexcept
@ -882,6 +989,7 @@ struct countr_zero<simde_int128>
{
using T = simde_int128;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(const T & val) const noexcept
@ -898,6 +1006,7 @@ struct countr_zero<simde_uint128>
{
using T = simde_uint128;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(const T & val) const noexcept
@ -914,6 +1023,7 @@ struct countr_zero<uint128_t>
{
using T = uint128_t;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(const T & val) const noexcept
@ -930,6 +1040,7 @@ struct countr_zero<uint256_t>
{
using T = uint256_t;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(const T & val) const noexcept
@ -946,6 +1057,7 @@ struct countr_zero<simde__m128i>
{
using T = simde__m128i;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
std::size_t operator()(const T & val) const noexcept
@ -963,6 +1075,7 @@ struct countr_zero<simde__m256i>
{
using T = simde__m256i;
HEDLEY_NO_THROW
HEDLEY_PURE
HEDLEY_ALWAYS_INLINE
constexpr std::size_t operator()(const T & val) const noexcept
@ -1026,13 +1139,19 @@ static constexpr std::size_t packed_lane_bits_v
= packed_lane_bits<std::remove_cv_t<T>>::value;
template <typename T>
auto data(T & bar) // NOLINT(runtime/references)
HEDLEY_PURE
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr auto data(T & bar) noexcept // NOLINT(runtime/references)
{
return std::data(bar);
}
/// Pointer overload. Constness of `bar` is the constness of `T`.
template <typename T>
auto data(T * bar)
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr T * data(T * bar) noexcept
{
return bar;
}
@ -1063,7 +1182,10 @@ template <typename T>
static constexpr bool is_tuple_v = is_tuple<T>::value;
template <std::size_t I, typename T>
auto & get(T & t) // NOLINT(runtime/references)
HEDLEY_PURE
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr auto & get(T & t) noexcept // NOLINT(runtime/references)
{
if constexpr(I == 0 && is_tuple_v<T> == false)
{
@ -1085,7 +1207,10 @@ template <typename T>
static constexpr bool is_bit_array_v = is_bit_array<T>::value;
template <typename T>
auto size(const T & t)
HEDLEY_PURE
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
constexpr auto size(const T & t) noexcept
{
if constexpr(is_bit_array_v<T> == false)
{

View file

@ -44,8 +44,11 @@ struct wildcard_value
static_assert(std::numeric_limits<T>::is_iec559 ||
!(std::is_same_v<T, float> || std::is_same_v<T, double>),
"floating point types only supported for iec559");
HEDLEY_NO_THROW
inline constexpr wildcard_value() noexcept : val{std::nullopt} { }
HEDLEY_NO_THROW
inline constexpr wildcard_value(const T & t) noexcept : val{t} { }
HEDLEY_NO_THROW
inline constexpr wildcard_value(T && t) noexcept : val{std::move(t)} { }
HEDLEY_ALWAYS_INLINE
@ -93,6 +96,7 @@ template <typename T> using concrete_type_t = typename concrete_type<T>::type;
template <typename T> struct concrete_value
{
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
constexpr std::optional<T> operator()(T y) const noexcept
@ -102,6 +106,7 @@ template <typename T> struct concrete_value
};
template <typename T> struct concrete_value<wildcard_value<T>>
{
HEDLEY_NO_THROW
HEDLEY_ALWAYS_INLINE
HEDLEY_CONST
constexpr std::optional<T> operator()(wildcard_value<T>) const noexcept

View file

@ -49,13 +49,16 @@ struct xor_wrapper
constexpr xor_wrapper() = default;
/// @brief Copy c'tor
HEDLEY_NO_THROW
constexpr xor_wrapper(const xor_wrapper &) noexcept = default;
/// @brief Move c'tor
HEDLEY_NO_THROW
constexpr xor_wrapper(xor_wrapper &&) noexcept = default;
/// @brief Value c'tor
// cppcheck-suppress noExplicitConstructor
HEDLEY_NO_THROW
constexpr xor_wrapper(value_type v) noexcept : value{v} { } // NOLINT(runtime/explicit)
/// @}
@ -163,7 +166,7 @@ struct xor_wrapper
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr xor_wrapper & operator<<=(std::size_t amount)
constexpr xor_wrapper & operator<<=(std::size_t amount) noexcept
{
this->value <<= amount;
return *this;
@ -171,7 +174,7 @@ struct xor_wrapper
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
constexpr xor_wrapper & operator>>=(std::size_t amount)
constexpr xor_wrapper & operator>>=(std::size_t amount) noexcept
{
this->value >>= amount;
return *this;
@ -274,14 +277,14 @@ struct xor_wrapper
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
friend constexpr xor_wrapper operator<<(const xor_wrapper & val, std::size_t amount)
friend constexpr xor_wrapper operator<<(const xor_wrapper & val, std::size_t amount) noexcept
{
return xor_wrapper(val.value << amount);
}
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
friend constexpr xor_wrapper operator>>(const xor_wrapper & val, std::size_t amount)
friend constexpr xor_wrapper operator>>(const xor_wrapper & val, std::size_t amount) noexcept
{
return xor_wrapper(val.value >> amount);
}
@ -670,217 +673,217 @@ constexpr static auto operator "" _x61(unsigned long long int x) { return dpf::x
constexpr static auto operator "" _x62(unsigned long long int x) { return dpf::xints::xint62_t{static_cast<psnip_uint64_t>(x)}; }
constexpr static auto operator "" _x63(unsigned long long int x) { return dpf::xints::xint63_t{static_cast<psnip_uint64_t>(x)}; }
constexpr static auto operator "" _x64(unsigned long long int x) { return dpf::xints::xint64_t{static_cast<psnip_uint64_t>(x)}; }
template <char ...digits> constexpr static auto operator "" _x65() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint65_t{x}; }
template <char ...digits> constexpr static auto operator "" _x66() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint66_t{x}; }
template <char ...digits> constexpr static auto operator "" _x67() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint67_t{x}; }
template <char ...digits> constexpr static auto operator "" _x68() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint68_t{x}; }
template <char ...digits> constexpr static auto operator "" _x69() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint69_t{x}; }
template <char ...digits> constexpr static auto operator "" _x65() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint65_t{x}; }
template <char ...digits> constexpr static auto operator "" _x66() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint66_t{x}; }
template <char ...digits> constexpr static auto operator "" _x67() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint67_t{x}; }
template <char ...digits> constexpr static auto operator "" _x68() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint68_t{x}; }
template <char ...digits> constexpr static auto operator "" _x69() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint69_t{x}; }
// 70--79
template <char ...digits> constexpr static auto operator "" _x70() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint70_t{x}; }
template <char ...digits> constexpr static auto operator "" _x71() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint71_t{x}; }
template <char ...digits> constexpr static auto operator "" _x72() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint72_t{x}; }
template <char ...digits> constexpr static auto operator "" _x73() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint73_t{x}; }
template <char ...digits> constexpr static auto operator "" _x74() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint74_t{x}; }
template <char ...digits> constexpr static auto operator "" _x75() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint75_t{x}; }
template <char ...digits> constexpr static auto operator "" _x76() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint76_t{x}; }
template <char ...digits> constexpr static auto operator "" _x77() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint77_t{x}; }
template <char ...digits> constexpr static auto operator "" _x78() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint78_t{x}; }
template <char ...digits> constexpr static auto operator "" _x79() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint79_t{x}; }
template <char ...digits> constexpr static auto operator "" _x70() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint70_t{x}; }
template <char ...digits> constexpr static auto operator "" _x71() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint71_t{x}; }
template <char ...digits> constexpr static auto operator "" _x72() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint72_t{x}; }
template <char ...digits> constexpr static auto operator "" _x73() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint73_t{x}; }
template <char ...digits> constexpr static auto operator "" _x74() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint74_t{x}; }
template <char ...digits> constexpr static auto operator "" _x75() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint75_t{x}; }
template <char ...digits> constexpr static auto operator "" _x76() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint76_t{x}; }
template <char ...digits> constexpr static auto operator "" _x77() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint77_t{x}; }
template <char ...digits> constexpr static auto operator "" _x78() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint78_t{x}; }
template <char ...digits> constexpr static auto operator "" _x79() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint79_t{x}; }
// 80--89
template <char ...digits> constexpr static auto operator "" _x80() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint80_t{x}; }
template <char ...digits> constexpr static auto operator "" _x81() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint81_t{x}; }
template <char ...digits> constexpr static auto operator "" _x82() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint82_t{x}; }
template <char ...digits> constexpr static auto operator "" _x83() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint83_t{x}; }
template <char ...digits> constexpr static auto operator "" _x84() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint84_t{x}; }
template <char ...digits> constexpr static auto operator "" _x85() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint85_t{x}; }
template <char ...digits> constexpr static auto operator "" _x86() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint86_t{x}; }
template <char ...digits> constexpr static auto operator "" _x87() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint87_t{x}; }
template <char ...digits> constexpr static auto operator "" _x88() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint88_t{x}; }
template <char ...digits> constexpr static auto operator "" _x89() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint89_t{x}; }
template <char ...digits> constexpr static auto operator "" _x80() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint80_t{x}; }
template <char ...digits> constexpr static auto operator "" _x81() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint81_t{x}; }
template <char ...digits> constexpr static auto operator "" _x82() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint82_t{x}; }
template <char ...digits> constexpr static auto operator "" _x83() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint83_t{x}; }
template <char ...digits> constexpr static auto operator "" _x84() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint84_t{x}; }
template <char ...digits> constexpr static auto operator "" _x85() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint85_t{x}; }
template <char ...digits> constexpr static auto operator "" _x86() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint86_t{x}; }
template <char ...digits> constexpr static auto operator "" _x87() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint87_t{x}; }
template <char ...digits> constexpr static auto operator "" _x88() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint88_t{x}; }
template <char ...digits> constexpr static auto operator "" _x89() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint89_t{x}; }
// 90--99
template <char ...digits> constexpr static auto operator "" _x90() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint90_t{x}; }
template <char ...digits> constexpr static auto operator "" _x91() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint91_t{x}; }
template <char ...digits> constexpr static auto operator "" _x92() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint92_t{x}; }
template <char ...digits> constexpr static auto operator "" _x93() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint93_t{x}; }
template <char ...digits> constexpr static auto operator "" _x94() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint94_t{x}; }
template <char ...digits> constexpr static auto operator "" _x95() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint95_t{x}; }
template <char ...digits> constexpr static auto operator "" _x96() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint96_t{x}; }
template <char ...digits> constexpr static auto operator "" _x97() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint97_t{x}; }
template <char ...digits> constexpr static auto operator "" _x98() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint98_t{x}; }
template <char ...digits> constexpr static auto operator "" _x99() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint99_t{x}; }
template <char ...digits> constexpr static auto operator "" _x90() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint90_t{x}; }
template <char ...digits> constexpr static auto operator "" _x91() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint91_t{x}; }
template <char ...digits> constexpr static auto operator "" _x92() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint92_t{x}; }
template <char ...digits> constexpr static auto operator "" _x93() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint93_t{x}; }
template <char ...digits> constexpr static auto operator "" _x94() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint94_t{x}; }
template <char ...digits> constexpr static auto operator "" _x95() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint95_t{x}; }
template <char ...digits> constexpr static auto operator "" _x96() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint96_t{x}; }
template <char ...digits> constexpr static auto operator "" _x97() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint97_t{x}; }
template <char ...digits> constexpr static auto operator "" _x98() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint98_t{x}; }
template <char ...digits> constexpr static auto operator "" _x99() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint99_t{x}; }
// 100--109
template <char ...digits> constexpr static auto operator "" _x100() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint100_t{x}; }
template <char ...digits> constexpr static auto operator "" _x101() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint101_t{x}; }
template <char ...digits> constexpr static auto operator "" _x102() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint102_t{x}; }
template <char ...digits> constexpr static auto operator "" _x103() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint103_t{x}; }
template <char ...digits> constexpr static auto operator "" _x104() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint104_t{x}; }
template <char ...digits> constexpr static auto operator "" _x105() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint105_t{x}; }
template <char ...digits> constexpr static auto operator "" _x106() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint106_t{x}; }
template <char ...digits> constexpr static auto operator "" _x107() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint107_t{x}; }
template <char ...digits> constexpr static auto operator "" _x108() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint108_t{x}; }
template <char ...digits> constexpr static auto operator "" _x109() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint109_t{x}; }
template <char ...digits> constexpr static auto operator "" _x100() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint100_t{x}; }
template <char ...digits> constexpr static auto operator "" _x101() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint101_t{x}; }
template <char ...digits> constexpr static auto operator "" _x102() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint102_t{x}; }
template <char ...digits> constexpr static auto operator "" _x103() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint103_t{x}; }
template <char ...digits> constexpr static auto operator "" _x104() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint104_t{x}; }
template <char ...digits> constexpr static auto operator "" _x105() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint105_t{x}; }
template <char ...digits> constexpr static auto operator "" _x106() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint106_t{x}; }
template <char ...digits> constexpr static auto operator "" _x107() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint107_t{x}; }
template <char ...digits> constexpr static auto operator "" _x108() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint108_t{x}; }
template <char ...digits> constexpr static auto operator "" _x109() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint109_t{x}; }
// 110--119
template <char ...digits> constexpr static auto operator "" _x110() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint110_t{x}; }
template <char ...digits> constexpr static auto operator "" _x111() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint111_t{x}; }
template <char ...digits> constexpr static auto operator "" _x112() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint112_t{x}; }
template <char ...digits> constexpr static auto operator "" _x113() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint113_t{x}; }
template <char ...digits> constexpr static auto operator "" _x114() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint114_t{x}; }
template <char ...digits> constexpr static auto operator "" _x115() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint115_t{x}; }
template <char ...digits> constexpr static auto operator "" _x116() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint116_t{x}; }
template <char ...digits> constexpr static auto operator "" _x117() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint117_t{x}; }
template <char ...digits> constexpr static auto operator "" _x118() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint118_t{x}; }
template <char ...digits> constexpr static auto operator "" _x119() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint119_t{x}; }
template <char ...digits> constexpr static auto operator "" _x110() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint110_t{x}; }
template <char ...digits> constexpr static auto operator "" _x111() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint111_t{x}; }
template <char ...digits> constexpr static auto operator "" _x112() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint112_t{x}; }
template <char ...digits> constexpr static auto operator "" _x113() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint113_t{x}; }
template <char ...digits> constexpr static auto operator "" _x114() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint114_t{x}; }
template <char ...digits> constexpr static auto operator "" _x115() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint115_t{x}; }
template <char ...digits> constexpr static auto operator "" _x116() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint116_t{x}; }
template <char ...digits> constexpr static auto operator "" _x117() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint117_t{x}; }
template <char ...digits> constexpr static auto operator "" _x118() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint118_t{x}; }
template <char ...digits> constexpr static auto operator "" _x119() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint119_t{x}; }
// 120--129
template <char ...digits> constexpr static auto operator "" _x120() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint120_t{x}; }
template <char ...digits> constexpr static auto operator "" _x121() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint121_t{x}; }
template <char ...digits> constexpr static auto operator "" _x122() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint122_t{x}; }
template <char ...digits> constexpr static auto operator "" _x123() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint123_t{x}; }
template <char ...digits> constexpr static auto operator "" _x124() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint124_t{x}; }
template <char ...digits> constexpr static auto operator "" _x125() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint125_t{x}; }
template <char ...digits> constexpr static auto operator "" _x126() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint126_t{x}; }
template <char ...digits> constexpr static auto operator "" _x127() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint127_t{x}; }
template <char ...digits> constexpr static auto operator "" _x128() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint128_t{x}; }
template <char ...digits> constexpr static auto operator "" _x129() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint129_t{x}; }
template <char ...digits> constexpr static auto operator "" _x120() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint120_t{x}; }
template <char ...digits> constexpr static auto operator "" _x121() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint121_t{x}; }
template <char ...digits> constexpr static auto operator "" _x122() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint122_t{x}; }
template <char ...digits> constexpr static auto operator "" _x123() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint123_t{x}; }
template <char ...digits> constexpr static auto operator "" _x124() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint124_t{x}; }
template <char ...digits> constexpr static auto operator "" _x125() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint125_t{x}; }
template <char ...digits> constexpr static auto operator "" _x126() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint126_t{x}; }
template <char ...digits> constexpr static auto operator "" _x127() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint127_t{x}; }
template <char ...digits> constexpr static auto operator "" _x128() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); simde_uint128 x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint128_t{x}; }
template <char ...digits> constexpr static auto operator "" _x129() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint129_t{x}; }
// 130--139
template <char ...digits> constexpr static auto operator "" _x130() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint130_t{x}; }
template <char ...digits> constexpr static auto operator "" _x131() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint131_t{x}; }
template <char ...digits> constexpr static auto operator "" _x132() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint132_t{x}; }
template <char ...digits> constexpr static auto operator "" _x133() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint133_t{x}; }
template <char ...digits> constexpr static auto operator "" _x134() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint134_t{x}; }
template <char ...digits> constexpr static auto operator "" _x135() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint135_t{x}; }
template <char ...digits> constexpr static auto operator "" _x136() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint136_t{x}; }
template <char ...digits> constexpr static auto operator "" _x137() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint137_t{x}; }
template <char ...digits> constexpr static auto operator "" _x138() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint138_t{x}; }
template <char ...digits> constexpr static auto operator "" _x139() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint139_t{x}; }
template <char ...digits> constexpr static auto operator "" _x130() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint130_t{x}; }
template <char ...digits> constexpr static auto operator "" _x131() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint131_t{x}; }
template <char ...digits> constexpr static auto operator "" _x132() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint132_t{x}; }
template <char ...digits> constexpr static auto operator "" _x133() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint133_t{x}; }
template <char ...digits> constexpr static auto operator "" _x134() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint134_t{x}; }
template <char ...digits> constexpr static auto operator "" _x135() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint135_t{x}; }
template <char ...digits> constexpr static auto operator "" _x136() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint136_t{x}; }
template <char ...digits> constexpr static auto operator "" _x137() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint137_t{x}; }
template <char ...digits> constexpr static auto operator "" _x138() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint138_t{x}; }
template <char ...digits> constexpr static auto operator "" _x139() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint139_t{x}; }
// 140--149
template <char ...digits> constexpr static auto operator "" _x140() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint140_t{x}; }
template <char ...digits> constexpr static auto operator "" _x141() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint141_t{x}; }
template <char ...digits> constexpr static auto operator "" _x142() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint142_t{x}; }
template <char ...digits> constexpr static auto operator "" _x143() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint143_t{x}; }
template <char ...digits> constexpr static auto operator "" _x144() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint144_t{x}; }
template <char ...digits> constexpr static auto operator "" _x145() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint145_t{x}; }
template <char ...digits> constexpr static auto operator "" _x146() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint146_t{x}; }
template <char ...digits> constexpr static auto operator "" _x147() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint147_t{x}; }
template <char ...digits> constexpr static auto operator "" _x148() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint148_t{x}; }
template <char ...digits> constexpr static auto operator "" _x149() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint149_t{x}; }
template <char ...digits> constexpr static auto operator "" _x140() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint140_t{x}; }
template <char ...digits> constexpr static auto operator "" _x141() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint141_t{x}; }
template <char ...digits> constexpr static auto operator "" _x142() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint142_t{x}; }
template <char ...digits> constexpr static auto operator "" _x143() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint143_t{x}; }
template <char ...digits> constexpr static auto operator "" _x144() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint144_t{x}; }
template <char ...digits> constexpr static auto operator "" _x145() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint145_t{x}; }
template <char ...digits> constexpr static auto operator "" _x146() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint146_t{x}; }
template <char ...digits> constexpr static auto operator "" _x147() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint147_t{x}; }
template <char ...digits> constexpr static auto operator "" _x148() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint148_t{x}; }
template <char ...digits> constexpr static auto operator "" _x149() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint149_t{x}; }
// 150--159
template <char ...digits> constexpr static auto operator "" _x150() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint150_t{x}; }
template <char ...digits> constexpr static auto operator "" _x151() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint151_t{x}; }
template <char ...digits> constexpr static auto operator "" _x152() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint152_t{x}; }
template <char ...digits> constexpr static auto operator "" _x153() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint153_t{x}; }
template <char ...digits> constexpr static auto operator "" _x154() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint154_t{x}; }
template <char ...digits> constexpr static auto operator "" _x155() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint155_t{x}; }
template <char ...digits> constexpr static auto operator "" _x156() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint156_t{x}; }
template <char ...digits> constexpr static auto operator "" _x157() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint157_t{x}; }
template <char ...digits> constexpr static auto operator "" _x158() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint158_t{x}; }
template <char ...digits> constexpr static auto operator "" _x159() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint159_t{x}; }
template <char ...digits> constexpr static auto operator "" _x150() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint150_t{x}; }
template <char ...digits> constexpr static auto operator "" _x151() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint151_t{x}; }
template <char ...digits> constexpr static auto operator "" _x152() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint152_t{x}; }
template <char ...digits> constexpr static auto operator "" _x153() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint153_t{x}; }
template <char ...digits> constexpr static auto operator "" _x154() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint154_t{x}; }
template <char ...digits> constexpr static auto operator "" _x155() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint155_t{x}; }
template <char ...digits> constexpr static auto operator "" _x156() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint156_t{x}; }
template <char ...digits> constexpr static auto operator "" _x157() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint157_t{x}; }
template <char ...digits> constexpr static auto operator "" _x158() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint158_t{x}; }
template <char ...digits> constexpr static auto operator "" _x159() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint159_t{x}; }
// 160--169
template <char ...digits> constexpr static auto operator "" _x160() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint160_t{x}; }
template <char ...digits> constexpr static auto operator "" _x161() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint161_t{x}; }
template <char ...digits> constexpr static auto operator "" _x162() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint162_t{x}; }
template <char ...digits> constexpr static auto operator "" _x163() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint163_t{x}; }
template <char ...digits> constexpr static auto operator "" _x164() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint164_t{x}; }
template <char ...digits> constexpr static auto operator "" _x165() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint165_t{x}; }
template <char ...digits> constexpr static auto operator "" _x166() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint166_t{x}; }
template <char ...digits> constexpr static auto operator "" _x167() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint167_t{x}; }
template <char ...digits> constexpr static auto operator "" _x168() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint168_t{x}; }
template <char ...digits> constexpr static auto operator "" _x169() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint169_t{x}; }
template <char ...digits> constexpr static auto operator "" _x160() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint160_t{x}; }
template <char ...digits> constexpr static auto operator "" _x161() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint161_t{x}; }
template <char ...digits> constexpr static auto operator "" _x162() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint162_t{x}; }
template <char ...digits> constexpr static auto operator "" _x163() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint163_t{x}; }
template <char ...digits> constexpr static auto operator "" _x164() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint164_t{x}; }
template <char ...digits> constexpr static auto operator "" _x165() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint165_t{x}; }
template <char ...digits> constexpr static auto operator "" _x166() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint166_t{x}; }
template <char ...digits> constexpr static auto operator "" _x167() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint167_t{x}; }
template <char ...digits> constexpr static auto operator "" _x168() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint168_t{x}; }
template <char ...digits> constexpr static auto operator "" _x169() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint169_t{x}; }
// 170--179
template <char ...digits> constexpr static auto operator "" _x170() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint170_t{x}; }
template <char ...digits> constexpr static auto operator "" _x171() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint171_t{x}; }
template <char ...digits> constexpr static auto operator "" _x172() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint172_t{x}; }
template <char ...digits> constexpr static auto operator "" _x173() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint173_t{x}; }
template <char ...digits> constexpr static auto operator "" _x174() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint174_t{x}; }
template <char ...digits> constexpr static auto operator "" _x175() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint175_t{x}; }
template <char ...digits> constexpr static auto operator "" _x176() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint176_t{x}; }
template <char ...digits> constexpr static auto operator "" _x177() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint177_t{x}; }
template <char ...digits> constexpr static auto operator "" _x178() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint178_t{x}; }
template <char ...digits> constexpr static auto operator "" _x179() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint179_t{x}; }
template <char ...digits> constexpr static auto operator "" _x170() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint170_t{x}; }
template <char ...digits> constexpr static auto operator "" _x171() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint171_t{x}; }
template <char ...digits> constexpr static auto operator "" _x172() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint172_t{x}; }
template <char ...digits> constexpr static auto operator "" _x173() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint173_t{x}; }
template <char ...digits> constexpr static auto operator "" _x174() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint174_t{x}; }
template <char ...digits> constexpr static auto operator "" _x175() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint175_t{x}; }
template <char ...digits> constexpr static auto operator "" _x176() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint176_t{x}; }
template <char ...digits> constexpr static auto operator "" _x177() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint177_t{x}; }
template <char ...digits> constexpr static auto operator "" _x178() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint178_t{x}; }
template <char ...digits> constexpr static auto operator "" _x179() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint179_t{x}; }
// 180--189
template <char ...digits> constexpr static auto operator "" _x180() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint180_t{x}; }
template <char ...digits> constexpr static auto operator "" _x181() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint181_t{x}; }
template <char ...digits> constexpr static auto operator "" _x182() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint182_t{x}; }
template <char ...digits> constexpr static auto operator "" _x183() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint183_t{x}; }
template <char ...digits> constexpr static auto operator "" _x184() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint184_t{x}; }
template <char ...digits> constexpr static auto operator "" _x185() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint185_t{x}; }
template <char ...digits> constexpr static auto operator "" _x186() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint186_t{x}; }
template <char ...digits> constexpr static auto operator "" _x187() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint187_t{x}; }
template <char ...digits> constexpr static auto operator "" _x188() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint188_t{x}; }
template <char ...digits> constexpr static auto operator "" _x189() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint189_t{x}; }
template <char ...digits> constexpr static auto operator "" _x180() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint180_t{x}; }
template <char ...digits> constexpr static auto operator "" _x181() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint181_t{x}; }
template <char ...digits> constexpr static auto operator "" _x182() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint182_t{x}; }
template <char ...digits> constexpr static auto operator "" _x183() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint183_t{x}; }
template <char ...digits> constexpr static auto operator "" _x184() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint184_t{x}; }
template <char ...digits> constexpr static auto operator "" _x185() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint185_t{x}; }
template <char ...digits> constexpr static auto operator "" _x186() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint186_t{x}; }
template <char ...digits> constexpr static auto operator "" _x187() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint187_t{x}; }
template <char ...digits> constexpr static auto operator "" _x188() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint188_t{x}; }
template <char ...digits> constexpr static auto operator "" _x189() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint189_t{x}; }
// 190--199
template <char ...digits> constexpr static auto operator "" _x190() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint190_t{x}; }
template <char ...digits> constexpr static auto operator "" _x191() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint191_t{x}; }
template <char ...digits> constexpr static auto operator "" _x192() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint192_t{x}; }
template <char ...digits> constexpr static auto operator "" _x193() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint193_t{x}; }
template <char ...digits> constexpr static auto operator "" _x194() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint194_t{x}; }
template <char ...digits> constexpr static auto operator "" _x195() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint195_t{x}; }
template <char ...digits> constexpr static auto operator "" _x196() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint196_t{x}; }
template <char ...digits> constexpr static auto operator "" _x197() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint197_t{x}; }
template <char ...digits> constexpr static auto operator "" _x198() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint198_t{x}; }
template <char ...digits> constexpr static auto operator "" _x199() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint199_t{x}; }
template <char ...digits> constexpr static auto operator "" _x190() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint190_t{x}; }
template <char ...digits> constexpr static auto operator "" _x191() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint191_t{x}; }
template <char ...digits> constexpr static auto operator "" _x192() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint192_t{x}; }
template <char ...digits> constexpr static auto operator "" _x193() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint193_t{x}; }
template <char ...digits> constexpr static auto operator "" _x194() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint194_t{x}; }
template <char ...digits> constexpr static auto operator "" _x195() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint195_t{x}; }
template <char ...digits> constexpr static auto operator "" _x196() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint196_t{x}; }
template <char ...digits> constexpr static auto operator "" _x197() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint197_t{x}; }
template <char ...digits> constexpr static auto operator "" _x198() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint198_t{x}; }
template <char ...digits> constexpr static auto operator "" _x199() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint199_t{x}; }
// 200--209
template <char ...digits> constexpr static auto operator "" _x200() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint200_t{x}; }
template <char ...digits> constexpr static auto operator "" _x201() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint201_t{x}; }
template <char ...digits> constexpr static auto operator "" _x202() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint202_t{x}; }
template <char ...digits> constexpr static auto operator "" _x203() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint203_t{x}; }
template <char ...digits> constexpr static auto operator "" _x204() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint204_t{x}; }
template <char ...digits> constexpr static auto operator "" _x205() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint205_t{x}; }
template <char ...digits> constexpr static auto operator "" _x206() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint206_t{x}; }
template <char ...digits> constexpr static auto operator "" _x207() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint207_t{x}; }
template <char ...digits> constexpr static auto operator "" _x208() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint208_t{x}; }
template <char ...digits> constexpr static auto operator "" _x209() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint209_t{x}; }
template <char ...digits> constexpr static auto operator "" _x200() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint200_t{x}; }
template <char ...digits> constexpr static auto operator "" _x201() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint201_t{x}; }
template <char ...digits> constexpr static auto operator "" _x202() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint202_t{x}; }
template <char ...digits> constexpr static auto operator "" _x203() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint203_t{x}; }
template <char ...digits> constexpr static auto operator "" _x204() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint204_t{x}; }
template <char ...digits> constexpr static auto operator "" _x205() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint205_t{x}; }
template <char ...digits> constexpr static auto operator "" _x206() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint206_t{x}; }
template <char ...digits> constexpr static auto operator "" _x207() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint207_t{x}; }
template <char ...digits> constexpr static auto operator "" _x208() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint208_t{x}; }
template <char ...digits> constexpr static auto operator "" _x209() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint209_t{x}; }
// 210--219
template <char ...digits> constexpr static auto operator "" _x210() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint210_t{x}; }
template <char ...digits> constexpr static auto operator "" _x211() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint211_t{x}; }
template <char ...digits> constexpr static auto operator "" _x212() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint212_t{x}; }
template <char ...digits> constexpr static auto operator "" _x213() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint213_t{x}; }
template <char ...digits> constexpr static auto operator "" _x214() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint214_t{x}; }
template <char ...digits> constexpr static auto operator "" _x215() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint215_t{x}; }
template <char ...digits> constexpr static auto operator "" _x216() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint216_t{x}; }
template <char ...digits> constexpr static auto operator "" _x217() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint217_t{x}; }
template <char ...digits> constexpr static auto operator "" _x218() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint218_t{x}; }
template <char ...digits> constexpr static auto operator "" _x219() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint219_t{x}; }
template <char ...digits> constexpr static auto operator "" _x210() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint210_t{x}; }
template <char ...digits> constexpr static auto operator "" _x211() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint211_t{x}; }
template <char ...digits> constexpr static auto operator "" _x212() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint212_t{x}; }
template <char ...digits> constexpr static auto operator "" _x213() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint213_t{x}; }
template <char ...digits> constexpr static auto operator "" _x214() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint214_t{x}; }
template <char ...digits> constexpr static auto operator "" _x215() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint215_t{x}; }
template <char ...digits> constexpr static auto operator "" _x216() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint216_t{x}; }
template <char ...digits> constexpr static auto operator "" _x217() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint217_t{x}; }
template <char ...digits> constexpr static auto operator "" _x218() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint218_t{x}; }
template <char ...digits> constexpr static auto operator "" _x219() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint219_t{x}; }
// 220--229
template <char ...digits> constexpr static auto operator "" _x220() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint220_t{x}; }
template <char ...digits> constexpr static auto operator "" _x221() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint221_t{x}; }
template <char ...digits> constexpr static auto operator "" _x222() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint222_t{x}; }
template <char ...digits> constexpr static auto operator "" _x223() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint223_t{x}; }
template <char ...digits> constexpr static auto operator "" _x224() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint224_t{x}; }
template <char ...digits> constexpr static auto operator "" _x225() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint225_t{x}; }
template <char ...digits> constexpr static auto operator "" _x226() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint226_t{x}; }
template <char ...digits> constexpr static auto operator "" _x227() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint227_t{x}; }
template <char ...digits> constexpr static auto operator "" _x228() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint228_t{x}; }
template <char ...digits> constexpr static auto operator "" _x229() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint229_t{x}; }
template <char ...digits> constexpr static auto operator "" _x220() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint220_t{x}; }
template <char ...digits> constexpr static auto operator "" _x221() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint221_t{x}; }
template <char ...digits> constexpr static auto operator "" _x222() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint222_t{x}; }
template <char ...digits> constexpr static auto operator "" _x223() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint223_t{x}; }
template <char ...digits> constexpr static auto operator "" _x224() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint224_t{x}; }
template <char ...digits> constexpr static auto operator "" _x225() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint225_t{x}; }
template <char ...digits> constexpr static auto operator "" _x226() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint226_t{x}; }
template <char ...digits> constexpr static auto operator "" _x227() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint227_t{x}; }
template <char ...digits> constexpr static auto operator "" _x228() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint228_t{x}; }
template <char ...digits> constexpr static auto operator "" _x229() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint229_t{x}; }
// 230--239
template <char ...digits> constexpr static auto operator "" _x230() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint230_t{x}; }
template <char ...digits> constexpr static auto operator "" _x231() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint231_t{x}; }
template <char ...digits> constexpr static auto operator "" _x232() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint232_t{x}; }
template <char ...digits> constexpr static auto operator "" _x233() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint233_t{x}; }
template <char ...digits> constexpr static auto operator "" _x234() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint234_t{x}; }
template <char ...digits> constexpr static auto operator "" _x235() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint235_t{x}; }
template <char ...digits> constexpr static auto operator "" _x236() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint236_t{x}; }
template <char ...digits> constexpr static auto operator "" _x237() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint237_t{x}; }
template <char ...digits> constexpr static auto operator "" _x238() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint238_t{x}; }
template <char ...digits> constexpr static auto operator "" _x239() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint239_t{x}; }
template <char ...digits> constexpr static auto operator "" _x230() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint230_t{x}; }
template <char ...digits> constexpr static auto operator "" _x231() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint231_t{x}; }
template <char ...digits> constexpr static auto operator "" _x232() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint232_t{x}; }
template <char ...digits> constexpr static auto operator "" _x233() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint233_t{x}; }
template <char ...digits> constexpr static auto operator "" _x234() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint234_t{x}; }
template <char ...digits> constexpr static auto operator "" _x235() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint235_t{x}; }
template <char ...digits> constexpr static auto operator "" _x236() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint236_t{x}; }
template <char ...digits> constexpr static auto operator "" _x237() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint237_t{x}; }
template <char ...digits> constexpr static auto operator "" _x238() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint238_t{x}; }
template <char ...digits> constexpr static auto operator "" _x239() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint239_t{x}; }
// 240--249
template <char ...digits> constexpr static auto operator "" _x240() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint240_t{x}; }
template <char ...digits> constexpr static auto operator "" _x241() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint241_t{x}; }
template <char ...digits> constexpr static auto operator "" _x242() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint242_t{x}; }
template <char ...digits> constexpr static auto operator "" _x243() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint243_t{x}; }
template <char ...digits> constexpr static auto operator "" _x244() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint244_t{x}; }
template <char ...digits> constexpr static auto operator "" _x245() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint245_t{x}; }
template <char ...digits> constexpr static auto operator "" _x246() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint246_t{x}; }
template <char ...digits> constexpr static auto operator "" _x247() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint247_t{x}; }
template <char ...digits> constexpr static auto operator "" _x248() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint248_t{x}; }
template <char ...digits> constexpr static auto operator "" _x249() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint249_t{x}; }
template <char ...digits> constexpr static auto operator "" _x240() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint240_t{x}; }
template <char ...digits> constexpr static auto operator "" _x241() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint241_t{x}; }
template <char ...digits> constexpr static auto operator "" _x242() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint242_t{x}; }
template <char ...digits> constexpr static auto operator "" _x243() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint243_t{x}; }
template <char ...digits> constexpr static auto operator "" _x244() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint244_t{x}; }
template <char ...digits> constexpr static auto operator "" _x245() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint245_t{x}; }
template <char ...digits> constexpr static auto operator "" _x246() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint246_t{x}; }
template <char ...digits> constexpr static auto operator "" _x247() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint247_t{x}; }
template <char ...digits> constexpr static auto operator "" _x248() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint248_t{x}; }
template <char ...digits> constexpr static auto operator "" _x249() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint249_t{x}; }
// 250--259
template <char ...digits> constexpr static auto operator "" _x250() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint250_t{x}; }
template <char ...digits> constexpr static auto operator "" _x251() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint251_t{x}; }
template <char ...digits> constexpr static auto operator "" _x252() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint252_t{x}; }
template <char ...digits> constexpr static auto operator "" _x253() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint253_t{x}; }
template <char ...digits> constexpr static auto operator "" _x254() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint254_t{x}; }
template <char ...digits> constexpr static auto operator "" _x255() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint255_t{x}; }
template <char ...digits> constexpr static auto operator "" _x256() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; (~((x *= 10) | (x += digits - '0')), ...); return dpf::xints::xint256_t{x}; }
template <char ...digits> constexpr static auto operator "" _x250() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint250_t{x}; }
template <char ...digits> constexpr static auto operator "" _x251() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint251_t{x}; }
template <char ...digits> constexpr static auto operator "" _x252() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint252_t{x}; }
template <char ...digits> constexpr static auto operator "" _x253() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint253_t{x}; }
template <char ...digits> constexpr static auto operator "" _x254() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint254_t{x}; }
template <char ...digits> constexpr static auto operator "" _x255() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint255_t{x}; }
template <char ...digits> constexpr static auto operator "" _x256() { utils::constexpr_maybe_throw<std::runtime_error>(!(std::isdigit(digits) && ...), "invalid char"); uint256_t x{0}; ((x = x * 10 + (digits - '0')), ...); return dpf::xints::xint256_t{x}; }
} // namespace xints
@ -929,6 +932,7 @@ template <typename T>
struct mod_pow_2<xor_wrapper<T>>
{
static constexpr auto mod = mod_pow_2<T>{};
HEDLEY_NO_THROW
std::size_t operator()(xor_wrapper<T> val, std::size_t n) const noexcept
{
return mod(val.value, n);

View file

@ -100,6 +100,7 @@ struct zip_iterator
template <typename ...Iterables>
HEDLEY_ALWAYS_INLINE
HEDLEY_NO_THROW
zip_iterable<Iterables...> tuple_as_zip(std::tuple<Iterables...> & tup) noexcept
{
return zip_iterable<Iterables...>(

View file

@ -10,6 +10,8 @@
#include "grotto/fixedpoint.hpp"
#include "grotto/nmod.hpp"
#include "grotto/gadget_hints.hpp"
#include "grotto/gadgets.hpp"
@ -24,6 +26,8 @@
#include "grotto/principal_lut.hpp"
#include "grotto/range_lut.hpp"
#include "grotto/window_lut.hpp"
#include "grotto/prefix_parity.hpp"

View file

@ -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)

View 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__

View file

@ -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;

View file

@ -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(); }
};

View file

@ -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

View file

@ -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
View 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__

View file

@ -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>

View file

@ -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} { }

View file

@ -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; });

View file

@ -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);

View file

@ -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)

View 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__