#pragma once
#include <cassert>
#include <cstdint>
#include "ff/baby_bear.hpp"
class Fp : public bb31_t {
public:
static constexpr uint32_t P = 15 * (uint32_t(1) << 27) + 1; static constexpr uint32_t M = 0x88000001; static constexpr uint32_t R2 = 1172168163; static constexpr uint32_t MONTY_BITS = 32;
static constexpr uint32_t MONTY_MASK = 0xffffffff; static constexpr uint32_t HALF_P_PLUS_1 = (P + 1) >> 1;
static constexpr uint32_t TWO_ADICITY = 27;
static constexpr uint32_t INV_2EXP_K = 975175684u;
private:
__device__ uint32_t val() const {
return static_cast<uint32_t>(*this);
}
public:
__device__ constexpr Fp() : bb31_t(0) {}
__device__ explicit constexpr Fp(const bb31_t& b) : bb31_t(b) {}
__device__ Fp(const bb31_base& b) : bb31_t(b) {}
__host__ __device__ constexpr Fp(uint32_t v) : bb31_t(static_cast<int>(v)) {}
template <class I, std::enable_if_t<std::is_integral_v<I>, int> = 0>
__host__ __device__ constexpr Fp(I v) : bb31_t(static_cast<int>(v)) {}
__device__ static constexpr Fp fromRaw(uint32_t val) {
return Fp(bb31_t(val));
}
__device__ static constexpr Fp zero() { return Fp(0); }
__device__ static constexpr Fp one() { return Fp(1); }
__device__ static constexpr Fp neg_one() { return maxVal(); }
__device__ uint32_t asUInt32() const { return val(); }
__device__ uint32_t asRaw() const { return bb31_t::operator*(); }
__device__ static constexpr Fp maxVal() { return P - 1; }
__device__ static constexpr Fp invalid() { return Fp::fromRaw(0xfffffffful); }
__device__ uint32_t get() const { return asRaw(); }
__device__ void set(uint32_t input) { bb31_t::operator=(input); }
friend __device__ inline bool operator==(const Fp& a, const Fp& b) {
return a.asRaw() == b.asRaw();
}
friend __device__ inline bool operator!=(const Fp& a, const Fp& b) {
return a.asRaw() != b.asRaw();
}
friend __device__ inline bool operator==(const Fp& a, int b) {
return a.asRaw() == Fp(b).asRaw();
}
friend __device__ inline bool operator==(int a, const Fp& b) {
return Fp(a).asRaw() == b.asRaw();
}
__device__ bool operator<(Fp rhs) const { return val() < rhs.val(); }
__device__ bool operator<=(Fp rhs) const { return val() <= rhs.val(); }
__device__ bool operator>(Fp rhs) const { return val() > rhs.val(); }
__device__ bool operator>=(Fp rhs) const { return val() >= rhs.val(); }
__device__ Fp operator++(int) {
Fp r = *this;
*this += Fp::one();
return r;
}
__device__ Fp operator--(int) {
Fp r = *this;
*this -= Fp::one();
return r;
}
__device__ Fp operator++() {
*this += Fp::one();
return *this;
}
__device__ Fp operator--() {
*this -= Fp::one();
return *this;
}
static __device__ inline uint32_t halve_u32(uint32_t input) {
uint32_t shr = input >> 1;
uint32_t lo_bit = input & 1;
return shr + (lo_bit * HALF_P_PLUS_1); }
static __device__ uint32_t monty_reduce(uint64_t x) {
uint64_t t = (x * uint64_t(Fp::M)) & uint64_t(Fp::MONTY_MASK);
uint64_t u = t * uint64_t(Fp::P);
uint64_t x_sub_u = x - u;
bool overflow = x < u;
uint32_t x_sub_u_hi = uint32_t(x_sub_u >> Fp::MONTY_BITS);
uint32_t corr = overflow ? (Fp::P) : 0;
return x_sub_u_hi + corr;
}
public:
__device__ Fp doubled() const { return *this + *this; }
__device__ Fp halve() const { return Fp::fromRaw(halve_u32(asRaw())); }
__device__ Fp mul_2exp_neg_n(uint32_t n) const {
assert(n < 33 && "n must be less than 33");
uint64_t value_mul_2exp_neg_n = static_cast<uint64_t>(asRaw()) << (32 - n);
return Fp::fromRaw(monty_reduce(value_mul_2exp_neg_n));
}
};
__device__ inline Fp pow(Fp x, uint32_t n) {
return Fp(static_cast<bb31_t>(x) ^ n);
}
template <class I, std::enable_if_t<std::is_integral_v<I>, int> = 0>
__device__ inline Fp pow(Fp x, I n) {
return pow(x, static_cast<uint32_t>(n));
}
static __device__ inline int64_t gcd_inversion_prime_field_32(uint32_t a, uint32_t b) {
int64_t u = 1;
int64_t v = 0;
for (uint32_t i = 0; i < 60; i++) {
if ((a & 1u) != 0u) {
if (a < b) {
uint32_t tmp_ab = a;
a = b;
b = tmp_ab;
int64_t tmp_uv = u;
u = v;
v = tmp_uv;
}
a -= b;
u -= v;
}
a >>= 1;
v <<= 1;
}
return v;
}
__device__ inline Fp inv_gcd(Fp x) {
if (x.asRaw() == 0u) {
return Fp::zero();
}
uint32_t a = x.asUInt32();
int64_t v = gcd_inversion_prime_field_32(a, Fp::P);
int64_t v_mod = v % static_cast<int64_t>(Fp::P);
if (v_mod < 0) {
v_mod += static_cast<int64_t>(Fp::P);
}
return Fp(static_cast<uint32_t>(v_mod)) * Fp(Fp::INV_2EXP_K);
}
__device__ inline Fp inv_fermat(Fp x) {
if (x.asRaw() == 0u) {
return Fp::zero();
}
return Fp(static_cast<bb31_t>(x).reciprocal());
}
__device__ inline Fp inv(Fp x) {
return inv_fermat(x);
}
constexpr __device__ Fp TWO_ADIC_GENERATORS[Fp::TWO_ADICITY + 1] = {
Fp(0x1), Fp(0x78000000), Fp(0x67055c21), Fp(0x5ee99486), Fp(0xbb4c4e4), Fp(0x2d4cc4da), Fp(0x669d6090), Fp(0x17b56c64), Fp(0x67456167), Fp(0x688442f9), Fp(0x145e952d), Fp(0x4fe61226), Fp(0x4c734715), Fp(0x11c33e2a), Fp(0x62c3d2b1), Fp(0x77cad399), Fp(0x54c131f4), Fp(0x4cabd6a6), Fp(0x5cf5713f), Fp(0x3e9430e8), Fp(0xba067a3), Fp(0x18adc27d), Fp(0x21fd55bc), Fp(0x4b859b3d), Fp(0x3bd57996), Fp(0x4483d85a), Fp(0x3a26eef8), Fp(0x1a427a41) };
static_assert(std::is_trivially_copyable<Fp>::value, "Fp must be POD-ish");
static_assert(sizeof(Fp) == 4, "Fp must be 4 bytes");
static_assert(alignof(Fp) == 4, "Fp must be 4-byte aligned");
static_assert(sizeof(Fp) == sizeof(bb31_t), "Fp and bb31_t sizes must match");
static_assert(alignof(Fp) == alignof(bb31_t), "Fp and bb31_t align must match");