/// @file dpf/gf2.hpp /// @brief Characteristic-2 field elements as DPF output types. /// @details `dpf::gf2`, `gf22`, `gf24`, `gf28`, `gf216`, `gf232`, and `gf264` /// are GF(2^k) for k = 1, 2, 4, 8, 16, 32, 64. Addition and /// subtraction are XOR. Multiplication is the polynomial product /// in the standard basis (low bit is the coefficient of 1). /// /// The moduli match laneint on mocha2. Widths 1, 2, 4, and 16 are /// the sparse irreducibles in `laneint/gf2_poly_basis.hpp` and the /// epi multipliers. GF(2^8) is the AES polynomial that /// `gf256_mul_u8` uses (`0x11B`), not the sparse `0x11D` alternative. /// GF(2^32) and GF(2^64) use the irreducible basis polynomials. /// The epi “default” reducers `x^32+x^7+1` and `x^64+x^4+1` factor, /// so they are not the field. Leaf scaling of `gf28` and `gf216` /// is the FAST'13 nibble `pshufb` (Plank, Greenan, Miller): one /// constant builds 16-byte tables, and every lane is a shuffle. /// `detail::shamir_field` inverts a /// nonzero element by Fermat, `a^{2^k - 2}`. Shamir points are the /// integers `1 .. N` as bit patterns, so `N` must be less than `2^k` /// or two parties land on the same element. /// @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_GF2_HPP__ #define LIBDPF_INCLUDE_DPF_GF2_HPP__ #include #include #include #include #include #include #include "hedley/hedley.h" #include "simde/simde/x86/avx2.h" #include "dpf/leaf_arithmetic.hpp" #include "dpf/utils.hpp" namespace dpf { namespace gf2_detail { template struct field; template <> struct field<1> { using word = std::uint8_t; using wide = std::uint16_t; static constexpr unsigned bits = 1; /// `x + 1` static constexpr wide modulus = 0x3u; }; template <> struct field<2> { using word = std::uint8_t; using wide = std::uint16_t; static constexpr unsigned bits = 2; /// `x^2 + x + 1` static constexpr wide modulus = 0x7u; }; template <> struct field<4> { using word = std::uint8_t; using wide = std::uint16_t; static constexpr unsigned bits = 4; /// `x^4 + x + 1` static constexpr wide modulus = 0x13u; }; template <> struct field<8> { using word = std::uint8_t; using wide = std::uint16_t; static constexpr unsigned bits = 8; /// AES: `x^8 + x^4 + x^3 + x + 1` static constexpr wide modulus = 0x11bu; }; template <> struct field<16> { using word = std::uint16_t; using wide = std::uint32_t; static constexpr unsigned bits = 16; /// `x^16 + x^5 + x^3 + x^2 + 1` static constexpr wide modulus = 0x1002du; }; template <> struct field<32> { using word = std::uint32_t; using wide = std::uint64_t; static constexpr unsigned bits = 32; /// `x^32 + x^31 + x^28 + x^21 + 1` static constexpr wide modulus = 0x190200001ull; }; template <> struct field<64> { using word = std::uint64_t; using wide = unsigned __int128; static constexpr unsigned bits = 64; /// `x^64 + x^63 + x^62 + x^53 + 1` static constexpr wide modulus = (wide{1} << 64) | (wide{1} << 63) | (wide{1} << 62) | (wide{1} << 53) | wide{1}; }; template HEDLEY_ALWAYS_INLINE HEDLEY_CONST constexpr typename field::word mask() noexcept { using word = typename field::word; if constexpr (Bits >= sizeof(word) * 8u) return static_cast(~word{0}); else return static_cast((word{1} << Bits) - word{1}); } template HEDLEY_ALWAYS_INLINE HEDLEY_CONST constexpr typename field::word reduce(typename field::wide p) noexcept { using wide = typename field::wide; constexpr wide mod = field::modulus; for (int bit = static_cast(2u * Bits) - 2; bit >= static_cast(Bits); --bit) { if (((p >> bit) & wide{1}) != 0) p ^= mod << (bit - static_cast(Bits)); } return static_cast::word>(p); } template HEDLEY_ALWAYS_INLINE HEDLEY_CONST constexpr typename field::word mul(typename field::word a, typename field::word b) noexcept { using wide = typename field::wide; a = static_cast::word>(a & mask()); b = static_cast::word>(b & mask()); wide p = 0; for (unsigned i = 0; i < Bits; ++i) { if (((b >> i) & 1u) != 0u) p ^= static_cast(a) << i; } return reduce(p); } /// @brief Multiplicative inverse, `a^{2^Bits - 2}`. /// @param a a nonzero field element /// @return `a^{-1}` /// @throws std::invalid_argument if `a` is zero template typename field::word inv(typename field::word a) { using word = typename field::word; a = static_cast(a & mask()); if (a == 0) throw std::invalid_argument("shamir: inverse of zero"); // Exponent 2^Bits - 2 is Bits-1 ones followed by a zero. word result{1}; for (int bit = static_cast(Bits) - 1; bit >= 0; --bit) { result = mul(result, result); if (bit != 0) result = mul(result, a); } return result; } /// @brief Multiply by `x` in AES GF(2^8), modulus `0x11B`. HEDLEY_ALWAYS_INLINE constexpr std::uint8_t gf28_mul_x(std::uint8_t a) noexcept { const std::uint8_t hi = static_cast(a & 0x80u); a = static_cast(static_cast(a << 1) ^ (hi != 0 ? 0x1bu : 0)); return a; } /// @brief Multiply by `x` in GF(2^16), modulus `x^16+x^5+x^3+x^2+1`. HEDLEY_ALWAYS_INLINE constexpr std::uint16_t gf216_mul_x(std::uint16_t a) noexcept { const std::uint16_t hi = static_cast(a & 0x8000u); a = static_cast(static_cast(a << 1) ^ (hi != 0 ? 0x2du : 0)); return a; } /// @brief 16 products of `base` with a nibble, built from four multiplies by `x`. HEDLEY_ALWAYS_INLINE void nibble_products_u8(std::uint8_t base, std::uint8_t out[16]) noexcept { const std::uint8_t b0 = base; const std::uint8_t b1 = gf28_mul_x(b0); const std::uint8_t b2 = gf28_mul_x(b1); const std::uint8_t b3 = gf28_mul_x(b2); out[0] = 0; for (unsigned n = 1; n < 16u; ++n) { std::uint8_t r = 0; if ((n & 1u) != 0) r = static_cast(r ^ b0); if ((n & 2u) != 0) r = static_cast(r ^ b1); if ((n & 4u) != 0) r = static_cast(r ^ b2); if ((n & 8u) != 0) r = static_cast(r ^ b3); out[n] = r; } } HEDLEY_ALWAYS_INLINE void nibble_products_u16(std::uint16_t base, std::uint8_t lo[16], std::uint8_t hi[16]) noexcept { const std::uint16_t b0 = base; const std::uint16_t b1 = gf216_mul_x(b0); const std::uint16_t b2 = gf216_mul_x(b1); const std::uint16_t b3 = gf216_mul_x(b2); lo[0] = 0; hi[0] = 0; for (unsigned n = 1; n < 16u; ++n) { std::uint16_t r = 0; if ((n & 1u) != 0) r = static_cast(r ^ b0); if ((n & 2u) != 0) r = static_cast(r ^ b1); if ((n & 4u) != 0) r = static_cast(r ^ b2); if ((n & 8u) != 0) r = static_cast(r ^ b3); lo[n] = static_cast(r); hi[n] = static_cast(r >> 8); } } /// @brief FAST'13 nibble tables for one GF(2^8) constant. /// @details Plank, Greenan, Miller, "Screaming Fast Galois Field Arithmetic /// Using Intel SIMD Instructions". `y * a = (y * a_lo) XOR (y * (a_hi << 4))`, /// each half a 16-entry `pshufb`. struct gf28_shufb { simde__m128i lo; simde__m128i hi; }; inline const gf28_shufb & gf28_shufb_for(std::uint8_t y) noexcept { struct tables { gf28_shufb t[256]; tables() noexcept { for (unsigned yy = 0; yy < 256u; ++yy) { alignas(16) std::uint8_t lo[16]; alignas(16) std::uint8_t hi[16]; nibble_products_u8(static_cast(yy), lo); const std::uint8_t x4 = gf28_mul_x(gf28_mul_x(gf28_mul_x(gf28_mul_x( static_cast(yy))))); nibble_products_u8(x4, hi); t[yy].lo = simde_mm_load_si128(reinterpret_cast(lo)); t[yy].hi = simde_mm_load_si128(reinterpret_cast(hi)); } } }; static const tables all; return all.t[y]; } HEDLEY_ALWAYS_INLINE simde__m128i gf28_scale_m128(simde__m128i lut_lo, simde__m128i lut_hi, simde__m128i a) noexcept { const simde__m128i m0f = simde_mm_set1_epi8(0x0f); const simde__m128i lo = simde_mm_and_si128(a, m0f); const simde__m128i hi = simde_mm_and_si128(simde_mm_srli_epi16(a, 4), m0f); return simde_mm_xor_si128(simde_mm_shuffle_epi8(lut_lo, lo), simde_mm_shuffle_epi8(lut_hi, hi)); } /// @brief Four nibble positions of one GF(2^16) constant. Each position is a /// low-byte table and a high-byte table. struct gf216_shufb { simde__m128i lo[4]; simde__m128i hi[4]; }; HEDLEY_ALWAYS_INLINE gf216_shufb gf216_shufb_prepare(std::uint16_t k) noexcept { gf216_shufb L{}; std::uint16_t base = k; for (unsigned nib = 0; nib < 4u; ++nib) { alignas(16) std::uint8_t lo[16]; alignas(16) std::uint8_t hi[16]; nibble_products_u16(base, lo, hi); L.lo[nib] = simde_mm_load_si128(reinterpret_cast(lo)); L.hi[nib] = simde_mm_load_si128(reinterpret_cast(hi)); base = gf216_mul_x(gf216_mul_x(gf216_mul_x(gf216_mul_x(base)))); } return L; } HEDLEY_ALWAYS_INLINE simde__m128i gf216_scale_m128(const gf216_shufb & L, simde__m128i x) noexcept { const simde__m128i m4 = simde_mm_set1_epi16(0x000f); const simde__m128i pick_ev = simde_mm_setr_epi8( 0, static_cast(0x80), 2, static_cast(0x80), 4, static_cast(0x80), 6, static_cast(0x80), 8, static_cast(0x80), 10, static_cast(0x80), 12, static_cast(0x80), 14, static_cast(0x80)); const simde__m128i ix0 = simde_mm_shuffle_epi8(simde_mm_and_si128(x, m4), pick_ev); const simde__m128i ix1 = simde_mm_shuffle_epi8( simde_mm_and_si128(simde_mm_srli_epi16(x, 4), m4), pick_ev); const simde__m128i ix2 = simde_mm_shuffle_epi8( simde_mm_and_si128(simde_mm_srli_epi16(x, 8), m4), pick_ev); const simde__m128i ix3 = simde_mm_shuffle_epi8( simde_mm_and_si128(simde_mm_srli_epi16(x, 12), m4), pick_ev); auto part = [](simde__m128i tl, simde__m128i th, simde__m128i ix) noexcept { const simde__m128i pl = simde_mm_shuffle_epi8(tl, ix); const simde__m128i ph = simde_mm_shuffle_epi8(th, ix); return simde_mm_or_si128(pl, simde_mm_slli_epi16(ph, 8)); }; return simde_mm_xor_si128( simde_mm_xor_si128(part(L.lo[0], L.hi[0], ix0), part(L.lo[1], L.hi[1], ix1)), simde_mm_xor_si128(part(L.lo[2], L.hi[2], ix2), part(L.lo[3], L.hi[3], ix3))); } HEDLEY_ALWAYS_INLINE void scale_gf28(unsigned char * dst, const unsigned char * src, std::size_t n, std::uint8_t k) noexcept { if (k == 0) { std::memset(dst, 0, n); return; } if (k == 1) { if (dst != src) std::memcpy(dst, src, n); return; } const gf28_shufb & lut = gf28_shufb_for(k); const simde__m256i lo256 = simde_mm256_broadcastsi128_si256(lut.lo); const simde__m256i hi256 = simde_mm256_broadcastsi128_si256(lut.hi); const simde__m256i m0f = simde_mm256_set1_epi8(0x0f); std::size_t i = 0; for (; i + 32 <= n; i += 32) { const simde__m256i a = simde_mm256_loadu_si256(src + i); const simde__m256i lo = simde_mm256_and_si256(a, m0f); const simde__m256i hi = simde_mm256_and_si256(simde_mm256_srli_epi16(a, 4), m0f); const simde__m256i r = simde_mm256_xor_si256( simde_mm256_shuffle_epi8(lo256, lo), simde_mm256_shuffle_epi8(hi256, hi)); simde_mm256_storeu_si256(dst + i, r); } for (; i + 16 <= n; i += 16) { const simde__m128i a = simde_mm_loadu_si128(src + i); simde_mm_storeu_si128(dst + i, gf28_scale_m128(lut.lo, lut.hi, a)); } for (; i < n; ++i) dst[i] = mul<8>(src[i], k); } HEDLEY_ALWAYS_INLINE void scale_gf216(unsigned char * dst, const unsigned char * src, std::size_t n, std::uint16_t k) noexcept { if (k == 0) { std::memset(dst, 0, n); return; } if (k == 1) { if (dst != src) std::memcpy(dst, src, n); return; } const gf216_shufb L = gf216_shufb_prepare(k); std::size_t i = 0; for (; i + 16 <= n; i += 16) { const simde__m128i a = simde_mm_loadu_si128(src + i); simde_mm_storeu_si128(dst + i, gf216_scale_m128(L, a)); } for (; i + sizeof(std::uint16_t) <= n; i += sizeof(std::uint16_t)) { std::uint16_t a{}; std::memcpy(&a, src + i, sizeof(a)); const std::uint16_t c = mul<16>(a, k); std::memcpy(dst + i, &c, sizeof(c)); } for (; i < n; ++i) dst[i] = 0; } HEDLEY_ALWAYS_INLINE void xor_bytes(unsigned char * dst, const unsigned char * a, const unsigned char * b, std::size_t n) noexcept { for (std::size_t i = 0; i < n; ++i) dst[i] = static_cast(a[i] ^ b[i]); } template HEDLEY_ALWAYS_INLINE void scale_bytes(unsigned char * dst, const unsigned char * src, std::size_t n, typename field::word k) noexcept { using word = typename field::word; k = static_cast(k & mask()); if constexpr (Bits == 1) { const unsigned char fill = (k & 1u) ? static_cast(0xffu) : 0; for (std::size_t i = 0; i < n; ++i) dst[i] = static_cast(src[i] & fill); } else if constexpr (Bits < 8) { constexpr unsigned per_byte = 8u / Bits; constexpr unsigned lane_mask = (1u << Bits) - 1u; for (std::size_t i = 0; i < n; ++i) { unsigned out = 0; const unsigned in = src[i]; for (unsigned lane = 0; lane < per_byte; ++lane) { const auto a = static_cast((in >> (lane * Bits)) & lane_mask); out |= static_cast(mul(a, k)) << (lane * Bits); } dst[i] = static_cast(out); } } else if constexpr (Bits == 8) { scale_gf28(dst, src, n, static_cast(k)); } else if constexpr (Bits == 16) { scale_gf216(dst, src, n, static_cast(k)); } else { std::size_t i = 0; for (; i + sizeof(word) <= n; i += sizeof(word)) { word a{}; std::memcpy(&a, src + i, sizeof(word)); const word c = mul(a, k); std::memcpy(dst + i, &c, sizeof(word)); } for (; i < n; ++i) dst[i] = 0; } } template HEDLEY_ALWAYS_INLINE NodeT xor_node(const NodeT & a, const NodeT & b) noexcept { unsigned char aa[sizeof(NodeT)]; unsigned char bb[sizeof(NodeT)]; unsigned char cc[sizeof(NodeT)]; std::memcpy(aa, std::addressof(a), sizeof(NodeT)); std::memcpy(bb, std::addressof(b), sizeof(NodeT)); xor_bytes(cc, aa, bb, sizeof(NodeT)); NodeT out; std::memcpy(std::addressof(out), cc, sizeof(NodeT)); return out; } template HEDLEY_ALWAYS_INLINE NodeT scale_node(const NodeT & a, typename field::word k) noexcept { unsigned char src[sizeof(NodeT)]; unsigned char dst[sizeof(NodeT)]; std::memcpy(src, std::addressof(a), sizeof(NodeT)); scale_bytes(dst, src, sizeof(NodeT), k); NodeT out; std::memcpy(std::addressof(out), dst, sizeof(NodeT)); return out; } } // namespace gf2_detail /// @brief Element of GF(2^Bits) in the standard polynomial basis. /// @tparam Bits field degree. One of 1, 2, 4, 8, 16, 32, 64. template class gf2n { public: using integral_type = typename gf2_detail::field::word; static constexpr unsigned bits = Bits; static constexpr bool dpf_gf2n = true; static constexpr bool dpf_point_group = true; /// @brief The zero element. HEDLEY_ALWAYS_INLINE constexpr gf2n() noexcept = default; /// @brief Bit embedding of an integer. A negative value is the field /// negation of its magnitude, which is the same element: every /// element is its own additive inverse. /// @tparam T integral type /// @param v the integer to embed template >> HEDLEY_ALWAYS_INLINE constexpr gf2n(T v) noexcept { if constexpr (std::is_signed_v) { if (v < 0) { using unsigned_type = std::make_unsigned_t; const auto mag = static_cast(0) - static_cast(v); val = static_cast(mag) & gf2_detail::mask(); } else { val = static_cast(v) & gf2_detail::mask(); } } else { val = static_cast(v) & gf2_detail::mask(); } } /// @brief Low `Bits` of a PRG block. Every bit string is a field element. /// @param bytes the PRG output /// @param n the number of bytes available /// @return the field element HEDLEY_ALWAYS_INLINE static gf2n from_seed(const void * bytes, std::size_t n) noexcept { integral_type w{}; if (bytes != nullptr && n != 0) { const std::size_t take = n < sizeof(w) ? n : sizeof(w); std::memcpy(&w, bytes, take); } return gf2n{w}; } /// @brief Reduced representative in `0 .. 2^Bits - 1`. /// @return the stored field element HEDLEY_ALWAYS_INLINE HEDLEY_PURE constexpr integral_type raw() const noexcept { return static_cast(val & gf2_detail::mask()); } /// @brief Same value as `raw()`. /// @return the stored field element HEDLEY_ALWAYS_INLINE HEDLEY_PURE explicit constexpr operator integral_type() const noexcept { return raw(); } private: integral_type val{}; }; /// @brief GF(2). using gf2 = gf2n<1>; /// @brief GF(4) = GF(2^2), modulus `x^2 + x + 1`. using gf22 = gf2n<2>; /// @brief GF(16) = GF(2^4), modulus `x^4 + x + 1`. using gf24 = gf2n<4>; /// @brief GF(256) = GF(2^8), AES modulus `x^8 + x^4 + x^3 + x + 1`. using gf28 = gf2n<8>; /// @brief GF(2^16), modulus `x^16 + x^5 + x^3 + x^2 + 1`. using gf216 = gf2n<16>; /// @brief GF(2^32), modulus `x^32 + x^31 + x^28 + x^21 + 1`. using gf232 = gf2n<32>; /// @brief GF(2^64), modulus `x^64 + x^63 + x^62 + x^53 + 1`. using gf264 = gf2n<64>; /// @brief Field addition. XOR of the reduced representatives. template HEDLEY_ALWAYS_INLINE constexpr gf2n operator+(gf2n a, gf2n b) noexcept { using word = typename gf2n::integral_type; return gf2n{static_cast(a.raw() ^ b.raw())}; } /// @brief Field subtraction. Identical to addition. template HEDLEY_ALWAYS_INLINE constexpr gf2n operator-(gf2n a, gf2n b) noexcept { return a + b; } /// @brief Additive inverse. Identical to `a` in characteristic 2. template HEDLEY_ALWAYS_INLINE constexpr gf2n operator-(gf2n a) noexcept { return a; } /// @brief Field XOR. Identical to addition. template HEDLEY_ALWAYS_INLINE constexpr gf2n operator^(gf2n a, gf2n b) noexcept { return a + b; } /// @brief Field multiplication. template HEDLEY_ALWAYS_INLINE constexpr gf2n operator*(gf2n a, gf2n b) noexcept { return gf2n{gf2_detail::mul(a.raw(), b.raw())}; } /// @brief Equality of the reduced representatives. template HEDLEY_ALWAYS_INLINE constexpr bool operator==(gf2n a, gf2n b) noexcept { return a.raw() == b.raw(); } /// @brief Inequality of the reduced representatives. template HEDLEY_ALWAYS_INLINE constexpr bool operator!=(gf2n a, gf2n b) noexcept { return a.raw() != b.raw(); } /// @brief Write the reduced representative in decimal. template std::ostream & operator<<(std::ostream & os, gf2n a) { return os << static_cast(a.raw()); } /// @brief Non-template operators so a packed-lane proxy converts to the field /// and finds `+` / `-` the same way `nyble` and `twobit` do. #define DPF_GF_LANE_OPS(Type) \ HEDLEY_ALWAYS_INLINE constexpr Type operator+(Type a, Type b) noexcept \ { \ using word = typename Type::integral_type; \ return Type{static_cast(a.raw() ^ b.raw())}; \ } \ HEDLEY_ALWAYS_INLINE constexpr Type operator-(Type a, Type b) noexcept \ { \ return a + b; \ } \ HEDLEY_ALWAYS_INLINE constexpr Type operator-(Type a) noexcept { return a; } \ HEDLEY_ALWAYS_INLINE constexpr Type operator^(Type a, Type b) noexcept \ { \ return a + b; \ } \ HEDLEY_ALWAYS_INLINE constexpr Type operator*(Type a, Type b) noexcept \ { \ return Type{gf2_detail::mul(a.raw(), b.raw())}; \ } \ HEDLEY_ALWAYS_INLINE constexpr bool operator==(Type a, Type b) noexcept \ { \ return a.raw() == b.raw(); \ } \ HEDLEY_ALWAYS_INLINE constexpr bool operator!=(Type a, Type b) noexcept \ { \ return !(a == b); \ } \ inline std::ostream & operator<<(std::ostream & os, Type a) \ { \ return os << static_cast(a.raw()); \ } DPF_GF_LANE_OPS(gf2) DPF_GF_LANE_OPS(gf22) DPF_GF_LANE_OPS(gf24) DPF_GF_LANE_OPS(gf28) DPF_GF_LANE_OPS(gf216) DPF_GF_LANE_OPS(gf232) DPF_GF_LANE_OPS(gf264) #undef DPF_GF_LANE_OPS namespace utils { template struct bitlength_of> : std::integral_constant { }; template struct bitlength_of_output, NodeT> : std::integral_constant { }; template struct is_packed_subbyte> : std::bool_constant<(Bits < 8)> {}; template struct packed_lane_bits> : std::integral_constant {}; template struct has_characteristic_two> : std::true_type {}; } // namespace utils namespace leaf_arithmetic { template struct add_t, NodeT, std::enable_if_t>> { HEDLEY_ALWAYS_INLINE auto operator()(const NodeT & a, const NodeT & b) const noexcept { return gf2_detail::xor_node(a, b); } }; template struct subtract_t, NodeT, std::enable_if_t>> { HEDLEY_ALWAYS_INLINE auto operator()(const NodeT & a, const NodeT & b) const noexcept { return gf2_detail::xor_node(a, b); } }; template struct multiply_t, NodeT, std::enable_if_t>> { HEDLEY_ALWAYS_INLINE auto operator()(const NodeT & a, gf2n b) const noexcept { return gf2_detail::scale_node(a, b.raw()); } }; } // namespace leaf_arithmetic } // namespace dpf #include "dpf/output_buffer.hpp" namespace dpf { template <> class output_buffer : public dynamic_packed_array { using base = dynamic_packed_array; public: using size_type = typename base::size_type; explicit output_buffer(size_type size) : base(size) { } output_buffer(output_buffer &&) noexcept = default; output_buffer(const output_buffer &) = delete; output_buffer & operator=(output_buffer &&) noexcept = default; output_buffer & operator=(const output_buffer &) = delete; ~output_buffer() noexcept = default; }; template <> class output_buffer : public dynamic_packed_array { using base = dynamic_packed_array; public: using size_type = typename base::size_type; explicit output_buffer(size_type size) : base(size) { } output_buffer(output_buffer &&) noexcept = default; output_buffer(const output_buffer &) = delete; output_buffer & operator=(output_buffer &&) noexcept = default; output_buffer & operator=(const output_buffer &) = delete; ~output_buffer() noexcept = default; }; template <> class output_buffer : public dynamic_packed_array { using base = dynamic_packed_array; public: using size_type = typename base::size_type; explicit output_buffer(size_type size) : base(size) { } output_buffer(output_buffer &&) noexcept = default; output_buffer(const output_buffer &) = delete; output_buffer & operator=(output_buffer &&) noexcept = default; output_buffer & operator=(const output_buffer &) = delete; ~output_buffer() noexcept = default; }; template <> class output_buffer> : public packed_share_output { using base = packed_share_output; public: using size_type = typename base::size_type; explicit output_buffer(size_type size) : base(size) { } output_buffer(output_buffer &&) noexcept = default; output_buffer(const output_buffer &) = delete; output_buffer & operator=(output_buffer &&) noexcept = default; output_buffer & operator=(const output_buffer &) = delete; ~output_buffer() noexcept = default; }; template <> class output_buffer> : public packed_share_output { using base = packed_share_output; public: using size_type = typename base::size_type; explicit output_buffer(size_type size) : base(size) { } output_buffer(output_buffer &&) noexcept = default; output_buffer(const output_buffer &) = delete; output_buffer & operator=(output_buffer &&) noexcept = default; output_buffer & operator=(const output_buffer &) = delete; ~output_buffer() noexcept = default; }; template <> class output_buffer> : public packed_share_output { using base = packed_share_output; public: using size_type = typename base::size_type; explicit output_buffer(size_type size) : base(size) { } output_buffer(output_buffer &&) noexcept = default; output_buffer(const output_buffer &) = delete; output_buffer & operator=(output_buffer &&) noexcept = default; output_buffer & operator=(const output_buffer &) = delete; ~output_buffer() noexcept = default; }; template <> class output_buffer> : public packed_share_output { using base = packed_share_output; public: using size_type = typename base::size_type; explicit output_buffer(size_type size) : base(size) { } output_buffer(output_buffer &&) noexcept = default; output_buffer(const output_buffer &) = delete; output_buffer & operator=(output_buffer &&) noexcept = default; output_buffer & operator=(const output_buffer &) = delete; ~output_buffer() noexcept = default; }; template <> class output_buffer> : public packed_share_output { using base = packed_share_output; public: using size_type = typename base::size_type; explicit output_buffer(size_type size) : base(size) { } output_buffer(output_buffer &&) noexcept = default; output_buffer(const output_buffer &) = delete; output_buffer & operator=(output_buffer &&) noexcept = default; output_buffer & operator=(const output_buffer &) = delete; ~output_buffer() noexcept = default; }; template <> class output_buffer> : public packed_share_output { using base = packed_share_output; public: using size_type = typename base::size_type; explicit output_buffer(size_type size) : base(size) { } output_buffer(output_buffer &&) noexcept = default; output_buffer(const output_buffer &) = delete; output_buffer & operator=(output_buffer &&) noexcept = default; output_buffer & operator=(const output_buffer &) = delete; ~output_buffer() noexcept = default; }; } // namespace dpf #include "dpf/shamir.hpp" namespace dpf { namespace detail { /// @brief Shamir inverse for `gf2n`. Points `1 .. N` must stay distinct, so /// `N < 2^Bits`. template struct shamir_field> : std::true_type { /// @brief `a^{-1}` by Fermat, `a^{2^Bits - 2}`. /// @param a a nonzero field element /// @return `a^{-1}` /// @throws std::invalid_argument if `a` is zero static gf2n inv(gf2n a) { return gf2n{gf2_detail::inv(a.raw())}; } }; } // namespace detail } // namespace dpf #endif // LIBDPF_INCLUDE_DPF_GF2_HPP__