use super::{Elem, Field, FieldBuffer, HasGenerator, PrimeCharacteristic};
pub const MODULUS: u64 = 0xFFFF_FFFF_0000_0001;
const EPSILON: u64 = 0xFFFF_FFFF;
#[inline]
#[must_use]
pub const fn reduce(x: u64) -> u64 {
if x >= MODULUS { x - MODULUS } else { x }
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::cast_lossless)]
pub const fn reduce_wide(x: u128) -> u64 {
let x_lo = x as u64;
let x_hi = (x >> 64) as u64;
let x_hi_hi = x_hi >> 32;
let x_hi_lo = x_hi & EPSILON;
let (t0, borrow) = x_lo.overflowing_sub(x_hi_hi);
let t0 = if borrow { t0.wrapping_sub(EPSILON) } else { t0 };
let t1 = x_hi_lo * EPSILON;
let (res, carry) = t0.overflowing_add(t1);
let t2 = res.wrapping_add(if carry { EPSILON } else { 0 });
reduce(t2)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, PartialOrd, Ord)]
pub struct Goldilocks;
const GROUP_FACTORS: [u64; 6] = [2, 3, 5, 17, 257, 65537];
const fn is_primitive(candidate: u64) -> bool {
let order = MODULUS - 1;
if super::powmod(candidate, order, MODULUS) != 1 {
return false;
}
let mut i = 0;
while i < GROUP_FACTORS.len() {
if super::powmod(candidate, order / GROUP_FACTORS[i], MODULUS) == 1 {
return false;
}
i += 1;
}
true
}
impl Field for Goldilocks {
type Raw = u64;
type Characteristic = PrimeCharacteristic<MODULUS>;
const NAME: &'static str = "GF(2^64 - 2^32 + 1)";
const DEGREE: u32 = 1;
const ORDER: u128 = MODULUS as u128;
const ZERO_RAW: u64 = 0;
const ONE_RAW: u64 = 1;
const VALID: () = ();
#[inline]
fn canonical_raw(raw: u64) -> u64 {
reduce(raw)
}
#[inline]
#[allow(clippy::cast_possible_truncation, clippy::cast_lossless)]
fn add_raw(left: u64, right: u64) -> u64 {
let sum = reduce(left) as u128 + reduce(right) as u128;
let modulus = MODULUS as u128;
if sum >= modulus {
(sum - modulus) as u64
} else {
sum as u64
}
}
#[inline]
#[allow(clippy::cast_possible_truncation, clippy::cast_lossless)]
fn sub_raw(left: u64, right: u64) -> u64 {
let sum = reduce(left) as u128 + MODULUS as u128 - reduce(right) as u128;
let modulus = MODULUS as u128;
if sum >= modulus {
(sum - modulus) as u64
} else {
sum as u64
}
}
#[inline]
fn neg_raw(value: u64) -> u64 {
let reduced = reduce(value);
if reduced == 0 { 0 } else { MODULUS - reduced }
}
#[inline]
fn mul_raw(left: u64, right: u64) -> u64 {
reduce_wide(u128::from(reduce(left)) * u128::from(reduce(right)))
}
#[inline]
#[allow(clippy::cast_possible_truncation, clippy::cast_lossless)]
fn inv_raw(value: u64) -> u64 {
if reduce(value) == 0 {
return 0;
}
super::powmod(reduce(value), MODULUS - 2, MODULUS)
}
}
impl HasGenerator for Goldilocks {
const GENERATOR_RAW: u64 = {
assert!(
is_primitive(7),
"Goldilocks generator does not have full order"
);
7
};
}
impl FieldBuffer for Goldilocks {
const BYTES: usize = 8;
const STORAGE_BITS: u32 = 64;
#[inline]
fn decode(bytes: &[u8]) -> Elem<Self> {
let bytes: [u8; 8] = bytes
.try_into()
.expect("GF(2^64 - 2^32 + 1) element has the wrong byte width");
Elem::<Self>::from_raw(u64::from_le_bytes(bytes))
}
#[inline]
fn encode(bytes: &mut [u8], value: Elem<Self>) {
assert_eq!(
bytes.len(),
8,
"GF(2^64 - 2^32 + 1) element has the wrong byte width"
);
bytes.copy_from_slice(&value.to_raw().to_le_bytes());
}
}
impl Elem<Goldilocks> {
#[inline]
#[must_use]
pub const fn from_bytes(bytes: [u8; 8]) -> Self {
Self::from_raw(u64::from_le_bytes(bytes))
}
#[inline]
#[must_use]
pub const fn to_bytes(self) -> [u8; 8] {
self.raw.to_le_bytes()
}
#[inline]
#[must_use]
pub const fn from_raw(value: u64) -> Self {
let () = super::Validate::<Goldilocks>::OK;
Elem { raw: reduce(value) }
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::cast_lossless)]
pub const fn add(self, rhs: Self) -> Self {
let sum = reduce(self.raw) as u128 + reduce(rhs.raw) as u128;
let modulus = MODULUS as u128;
Elem {
raw: if sum >= modulus {
(sum - modulus) as u64
} else {
sum as u64
},
}
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation, clippy::cast_lossless)]
pub const fn sub(self, rhs: Self) -> Self {
let sum = reduce(self.raw) as u128 + MODULUS as u128 - reduce(rhs.raw) as u128;
let modulus = MODULUS as u128;
Elem {
raw: if sum >= modulus {
(sum - modulus) as u64
} else {
sum as u64
},
}
}
#[inline]
#[must_use]
pub const fn neg(self) -> Self {
let reduced = reduce(self.raw);
Elem {
raw: if reduced == 0 { 0 } else { MODULUS - reduced },
}
}
#[inline]
#[must_use]
#[allow(clippy::cast_lossless)]
pub const fn mul(self, rhs: Self) -> Self {
Elem {
raw: reduce_wide(self.raw as u128 * rhs.raw as u128),
}
}
#[inline]
#[must_use]
pub const fn square(self) -> Self {
self.mul(self)
}
#[inline]
#[must_use]
pub const fn inv(self) -> Self {
self.pow((MODULUS - 2) as u128)
}
#[inline]
#[must_use]
pub const fn div(self, rhs: Self) -> Self {
if reduce(rhs.raw) == 0 {
return Self::ZERO;
}
self.mul(rhs.inv())
}
#[inline]
#[must_use]
pub const fn pow(self, mut exponent: u128) -> Self {
let mut base = self;
let mut result = Self::ONE;
while exponent != 0 {
if exponent & 1 != 0 {
result = result.mul(base);
}
base = base.square();
exponent >>= 1;
}
result
}
}
impl core::fmt::Display for Elem<Goldilocks> {
fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(formatter, "{}", self.raw)
}
}
#[cfg(test)]
mod tests {
use super::{Elem, Goldilocks, HasGenerator, MODULUS, is_primitive};
const fn gcd(mut a: u64, mut b: u64) -> u64 {
while b != 0 {
(a, b) = (b, a % b);
}
a
}
#[test]
fn primitivity_follows_generator_power_coprimality() {
let generator =
Elem::<Goldilocks>::from_raw(core::hint::black_box(Goldilocks::GENERATOR_RAW));
for k in 1..=96u64 {
let candidate = generator.pow(u128::from(k)).to_raw();
assert_eq!(
is_primitive(core::hint::black_box(candidate)),
gcd(k, MODULUS - 1) == 1,
"7^{k}"
);
}
}
#[test]
fn primitivity_rejects_degenerate_orders() {
for candidate in [0, 1, MODULUS - 1] {
assert!(
!is_primitive(core::hint::black_box(candidate)),
"{candidate}"
);
}
}
}