/// @file dpf/fp61.hpp /// @brief Prime field \(\mathbb{F}_{2^{61}-1}\) additive output type. /// @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_FP61_HPP__ #define LIBDPF_INCLUDE_DPF_FP61_HPP__ #include #include #include #include #include #include "hedley/hedley.h" #include "dpf/utils.hpp" #include "dpf/leaf_arithmetic.hpp" namespace dpf { /// @brief Modulus \(p = 2^{61}-1\). `p` itself reduces to 0. inline constexpr std::uint64_t fp61_mod = (std::uint64_t{1} << 61) - 1; /// @brief Additive element of \(\mathbb{F}_{2^{61}-1}\). class fp61 { public: /// @brief Underlying unsigned word. Values are stored already reduced. using integral_type = std::uint64_t; static constexpr std::size_t num_bits = 61; static constexpr bool dpf_modint = true; static constexpr bool dpf_fp61 = true; /// @brief Reduce `v` into the field. /// @param v the integer to reduce. Defaults to 0 HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr fp61(integral_type v = 0) noexcept : val{reduce(v)} { } /// @brief Copy constructor. HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr fp61(const fp61 &) noexcept = default; /// @brief Move constructor. HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr fp61(fp61 &&) noexcept = default; /// @brief Copy assignment. /// @return `*this` HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr fp61 & operator=(const fp61 &) noexcept = default; /// @brief Move assignment. /// @return `*this` HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE constexpr fp61 & operator=(fp61 &&) noexcept = default; /// @brief The reduced representative in `[0, p)`. /// @return the stored field element HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_PURE constexpr integral_type raw() const noexcept { return val; } /// @brief Same value as `raw()`. /// @return the stored field element HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_PURE explicit constexpr operator integral_type() const noexcept { return val; } /// @brief Mersenne reduction of a 64-bit word. /// @param x the integer to reduce /// @return `x` modulo `2^61-1`, with `p` itself represented as 0 HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST static constexpr integral_type reduce(integral_type x) noexcept { x = (x & fp61_mod) + (x >> 61); if (x >= fp61_mod) x -= fp61_mod; return x; } /// @brief Field addition. /// @param a left addend /// @param b right addend /// @return `a + b` in the field HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST friend constexpr fp61 operator+(fp61 a, fp61 b) noexcept { return fp61{a.val + b.val}; } /// @brief Field subtraction. /// @param a minuend /// @param b subtrahend /// @return `a - b` in the field HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST friend constexpr fp61 operator-(fp61 a, fp61 b) noexcept { return fp61{a.val + fp61_mod - b.val}; } /// @brief Field negation. /// @param a the element to negate /// @return `-a`, with `-0 = 0` HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST friend constexpr fp61 operator-(fp61 a) noexcept { return fp61{a.val == 0 ? 0 : fp61_mod - a.val}; } /// @brief Field multiplication. /// @param a left factor /// @param b right factor /// @return `a * b` in the field HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST friend constexpr fp61 operator*(fp61 a, fp61 b) noexcept { using u128 = unsigned __int128; const u128 p = static_cast(a.val) * static_cast(b.val); const auto lo = static_cast(p) & fp61_mod; const auto mid = static_cast(p >> 61) & fp61_mod; const auto hi = static_cast(p >> 122); return fp61{lo + mid + hi}; } /// @brief Field equality. /// @param a left element /// @param b right element /// @return `true` when the reduced values match HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST friend constexpr bool operator==(fp61 a, fp61 b) noexcept { return a.val == b.val; } /// @brief Field inequality. /// @param a left element /// @param b right element /// @return `true` when the reduced values differ HEDLEY_NO_THROW HEDLEY_ALWAYS_INLINE HEDLEY_CONST friend constexpr bool operator!=(fp61 a, fp61 b) noexcept { return a.val != b.val; } /// @brief Write the reduced representative in decimal. /// @param os the output stream /// @param a the element to write /// @return `os` friend std::ostream & operator<<(std::ostream & os, fp61 a) { return os << a.val; } private: integral_type val; }; namespace utils { template <> struct bitlength_of : std::integral_constant { }; template <> struct has_characteristic_two : std::false_type { }; } // namespace utils namespace leaf_arithmetic { HEDLEY_PRAGMA(GCC diagnostic push) HEDLEY_PRAGMA(GCC diagnostic ignored "-Wignored-attributes") template <> struct add_t { auto operator()(const simde__m128i & a, const simde__m128i & b) const { return add_t{}(a, b); } }; template <> struct subtract_t { auto operator()(const simde__m128i & a, const simde__m128i & b) const { return subtract_t{}(a, b); } }; template <> struct add_t { auto operator()(const simde__m256i & a, const simde__m256i & b) const { return add_t{}(a, b); } }; template <> struct subtract_t { auto operator()(const simde__m256i & a, const simde__m256i & b) const { return subtract_t{}(a, b); } }; HEDLEY_PRAGMA(GCC diagnostic pop) } // namespace leaf_arithmetic } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_FP61_HPP__