#![forbid(unsafe_code)]
use fgf::{Elem, Field, FieldBuffer, FiniteField, Goldilocks, backend_for, ops};
type WidePrime = FiniteField<0xFFFF_FFFF_FFFF_FFC5, 1>;
type GldResidue = FiniteField<0xFFFF_FFFF_0000_0001, 1>;
const LANES: usize = 6;
const LANE_BYTES: usize = 8;
fn main() {
lanes();
residue_and_fold();
}
fn lanes() {
assert_eq!(
Elem::<WidePrime>::from_raw(0xFFFF_FFFF_FFFF_FFC5).to_raw(),
0
);
assert_eq!(Elem::<WidePrime>::from_raw(u64::MAX).to_raw(), 58);
assert!(Elem::<WidePrime>::from_residue(u64::MAX).is_err());
let x = Elem::<WidePrime>::from_residue(12_345_678).unwrap();
assert_eq!(x.to_residue(), Some(12_345_678));
assert_eq!(<WidePrime as FieldBuffer>::BYTES, LANE_BYTES);
let mut lane = [0u8; LANE_BYTES];
<WidePrime as FieldBuffer>::encode(&mut lane, x);
assert_eq!(lane, 12_345_678u64.to_le_bytes());
assert_eq!(<WidePrime as FieldBuffer>::decode(&lane), x);
let xs: [Elem<WidePrime>; LANES] = std::array::from_fn(|i| {
Elem::<WidePrime>::from_raw((i as u64 + 1) * 0x0123_4567_89AB_CDEF)
});
let k = Elem::<WidePrime>::from_residue(0x0123_4567_89AB_CDEF).unwrap();
let prepared = ops::Coeff::<WidePrime>::new(k);
const WIDTH: usize = LANES * LANE_BYTES;
let mut source = [0u8; WIDTH];
ops::pack::<WidePrime>(&mut source, &xs);
let mut scaled = [0u8; WIDTH];
ops::mul_into_with::<WidePrime>(&mut scaled, &prepared, &source);
let mut acc = [0u8; WIDTH];
ops::mul_add_with::<WidePrime>(&mut acc, &prepared, &source);
assert_eq!(scaled, acc);
let mut lanes = [Elem::<WidePrime>::ZERO; LANES];
ops::unpack::<WidePrime>(&mut lanes, &acc);
for (value, &x) in lanes.iter().zip(&xs) {
assert_eq!(*value, k * x, "prepared coefficient agrees per lane");
}
let mut product = [0u8; WIDTH];
ops::mul_elementwise::<WidePrime>(&mut product, &source, &source);
ops::unpack::<WidePrime>(&mut lanes, &product);
for (value, &x) in lanes.iter().zip(&xs) {
assert_eq!(*value, x.square(), "elementwise product agrees per lane");
}
assert_eq!(Elem::<WidePrime>::ZERO.inv(), Elem::<WidePrime>::ZERO);
assert_eq!(x.div(Elem::<WidePrime>::ZERO), Elem::<WidePrime>::ZERO);
println!(
"{} ({:?}): eight-byte lanes, prepared coefficient reused, no allocation",
WidePrime::NAME,
backend_for::<WidePrime>()
);
}
fn residue_and_fold() {
let residue = Elem::<GldResidue>::from_raw(u64::MAX);
let folded = Elem::<Goldilocks>::from_raw(u64::MAX);
assert_eq!(residue.to_raw(), folded.to_raw());
assert_eq!(folded.to_raw(), (1u64 << 32) - 2);
let moved = Elem::<Goldilocks>::from_residue(residue.to_raw()).unwrap();
assert_eq!(moved, folded);
let (c, d) = (
Elem::<Goldilocks>::from_raw(0xFFFF_FFFF),
Elem::<Goldilocks>::from_raw(0x1_0000_0000),
);
let rc = Elem::<GldResidue>::from_residue(c.to_raw()).unwrap();
let rd = Elem::<GldResidue>::from_residue(d.to_raw()).unwrap();
assert_eq!((rc * rd).to_raw(), (c * d).to_raw());
let eps = Elem::<Goldilocks>::from_residue((1u64 << 32) - 1).unwrap();
assert_eq!(eps.square().to_raw(), 0xFFFF_FFFE_0000_0001);
println!(
"{} and {} agree by residue at one modulus",
GldResidue::NAME,
Goldilocks::NAME
);
}