use crate::field::{Goldilocks, P};
use strata_compute::{Bits, Spectral};
use strata_core::{Codec, Field, Ring, Semiring};
use strata_ext::Batch;
use strata_proof::{Dot, Reduce};
extern crate alloc;
use alloc::vec::Vec;
impl Codec for Goldilocks {
fn byte_len() -> usize {
8
}
fn encode(&self, buf: &mut [u8]) {
buf[..8].copy_from_slice(&self.as_u64().to_le_bytes());
}
fn decode(bytes: &[u8]) -> Option<Self> {
if bytes.len() < 8 {
return None;
}
let mut buf = [0u8; 8];
buf.copy_from_slice(&bytes[..8]);
let val = u64::from_le_bytes(buf);
if val >= P {
return None;
}
Some(Goldilocks::new(val))
}
}
impl Semiring for Goldilocks {
const ZERO: Self = Goldilocks::ZERO;
const ONE: Self = Goldilocks::ONE;
}
impl Ring for Goldilocks {}
impl Field for Goldilocks {
#[inline]
fn inv(self) -> Self {
Goldilocks::inv(self)
}
#[inline]
fn square(self) -> Self {
Goldilocks::square(self)
}
fn sqrt(self) -> Option<Self> {
crate::sqrt::sqrt(self)
}
}
impl Reduce for Goldilocks {
fn reduce(bytes: &[u8]) -> Self {
assert!(bytes.len() >= 8, "need at least 8 bytes for Goldilocks");
let mut buf = [0u8; 8];
buf.copy_from_slice(&bytes[..8]);
Goldilocks::new(u64::from_le_bytes(buf)).canonicalize()
}
}
impl Dot for Goldilocks {
fn dot(a: &[Self], b: &[Self]) -> Self {
assert_eq!(a.len(), b.len());
let mut acc_lo: u128 = 0;
let mut acc_hi: u128 = 0;
for (i, (&ai, &bi)) in a.iter().zip(b.iter()).enumerate() {
let prod = (ai.as_u64() as u128) * (bi.as_u64() as u128);
acc_lo += prod;
if (i & 0xFFFF) == 0xFFFF {
let reduced = reduce128(acc_lo);
acc_hi += reduced as u128;
acc_lo = 0;
}
}
let final_lo = reduce128(acc_lo);
let total = acc_hi + final_lo as u128;
Goldilocks::new(reduce128(total)).canonicalize()
}
}
#[inline]
fn reduce128(val: u128) -> u64 {
let lo = val as u64;
let hi = (val >> 64) as u64;
let (sum, carry) = lo.overflowing_add(hi.wrapping_mul(P.wrapping_neg()));
if carry || sum >= P {
sum.wrapping_sub(P)
} else {
sum
}
}
const G: u64 = 7;
impl Spectral for Goldilocks {
const TWO_ADICITY: u32 = 32;
const ROOT_OF_UNITY: Self = Goldilocks::new(0x185629DCDA58878C);
const ROOT_OF_UNITY_INV: Self = Goldilocks::new(0x76B6B635B6FC8719);
const GENERATOR: Self = Goldilocks::new(G);
}
impl Bits for Goldilocks {
const NUM_BITS: u32 = 64;
fn to_bits_le(&self) -> Vec<bool> {
let v = self.as_u64();
(0..64).map(|i| (v >> i) & 1 == 1).collect()
}
fn from_bits_le(bits: &[bool]) -> Self {
let mut v = 0u64;
for (i, &bit) in bits.iter().enumerate().take(64) {
if bit {
v |= 1 << i;
}
}
Goldilocks::new(v).canonicalize()
}
}
impl Batch for Goldilocks {
fn batch_inv(elements: &mut [Self]) {
let n = elements.len();
if n == 0 {
return;
}
let mut prefix = Vec::with_capacity(n);
let mut acc = Self::ONE;
for &e in elements.iter() {
if e == Self::ZERO {
prefix.push(acc);
} else {
acc *= e;
prefix.push(acc);
}
}
let mut inv_acc = acc.inv();
for i in (1..n).rev() {
if elements[i] == Self::ZERO {
continue;
}
let inv_i = inv_acc * prefix[i - 1];
inv_acc *= elements[i];
elements[i] = inv_i;
}
if elements[0] != Self::ZERO {
elements[0] = inv_acc;
}
}
}
strata_core::test_field_axioms!(Goldilocks, goldilocks_axioms, |v: u64| Goldilocks::new(v)
.canonicalize());
#[cfg(test)]
mod spectral_tests {
use super::*;
use strata_compute::Spectral;
use strata_core::Field;
#[test]
fn root_of_unity_order() {
let mut r = Goldilocks::ROOT_OF_UNITY;
for _ in 0..32 {
r = r.square();
}
assert_eq!(r, Goldilocks::ONE, "root^(2^32) should be 1");
}
#[test]
fn root_of_unity_primitive() {
let mut r = Goldilocks::ROOT_OF_UNITY;
for _ in 0..31 {
r = r.square();
}
assert_ne!(
r,
Goldilocks::ONE,
"root^(2^31) should NOT be 1 (primitive)"
);
}
#[test]
fn root_inv_product() {
assert_eq!(
Goldilocks::ROOT_OF_UNITY * Goldilocks::ROOT_OF_UNITY_INV,
Goldilocks::ONE,
"root * root_inv should be 1"
);
}
#[test]
fn generator_is_primitive_root() {
let half_order = (P - 1) / 2;
let result = Goldilocks::GENERATOR.pow(half_order);
assert_eq!(
result,
Goldilocks::NEG_ONE,
"generator^((p-1)/2) should be -1"
);
}
#[test]
fn root_of_unity_at_various_sizes() {
for log_n in 1..=20 {
let n = 1usize << log_n;
let omega = Goldilocks::root_of_unity(n);
assert_eq!(
omega.pow(n as u64),
Goldilocks::ONE,
"omega^{n} should be 1"
);
if n > 1 {
assert_ne!(
omega.pow((n / 2) as u64),
Goldilocks::ONE,
"omega^({}/2) should NOT be 1",
n
);
}
}
}
}