use alloc::vec::Vec;
use bytemuck::checked::CheckedCastError;
use core::{cmp, fmt::Debug, ops};
pub mod baby_bear;
pub trait Field {
type Elem: Elem + RootsOfUnity;
type ExtElem: ExtElem<SubElem = Self::Elem>;
}
pub trait Elem: 'static
+ Clone
+ Copy
+ Send
+ Sync
+ Debug
+ Sized
+ ops::Neg<Output = Self>
+ ops::SubAssign
+ cmp::PartialEq
+ cmp::Eq
+ core::clone::Clone
+ core::marker::Copy
+ bytemuck::NoUninit
+ bytemuck::CheckedBitPattern
+ core::default::Default
+ ops::Add<Self, Output = Self>
+ ops::Sub<Self, Output = Self>
+ ops::Mul<Self, Output = Self>
+ ops::AddAssign<Self>
+ ops::SubAssign<Self>
+ ops::MulAssign<Self>
{
const INVALID: Self;
const ZERO: Self;
const ONE: Self;
const WORDS: usize;
fn inv(self) -> Self;
fn pow(self, exp: usize) -> Self {
debug_assert!(self.is_valid());
let mut n = exp;
let mut tot = Self::ONE;
let mut x = self;
while n != 0 {
if n % 2 == 1 {
tot *= x;
}
n /= 2;
x *= x;
}
tot
}
fn random(rng: &mut impl rand_core::RngCore) -> Self;
fn from_u64(val: u64) -> Self;
fn to_u32_words(&self) -> Vec<u32>;
fn from_u32_words(val: &[u32]) -> Self;
fn is_valid(&self) -> bool;
fn is_reduced(&self) -> bool;
fn valid_or_zero(&self) -> Self {
if self.is_valid() {
*self
} else {
Self::ZERO
}
}
fn ensure_valid(&self) -> &Self {
debug_assert!(self.is_valid());
self
}
fn ensure_reduced(&self) -> &Self {
assert!(self.is_reduced());
self
}
fn as_u32_slice(elems: &[Self]) -> &[u32] {
if cfg!(debug_assertions) {
for elem in elems {
elem.ensure_valid();
}
}
Self::as_u32_slice_unchecked(elems)
}
fn as_u32_slice_unchecked(elems: &[Self]) -> &[u32] {
bytemuck::cast_slice(elems)
}
fn from_u32_slice(u32s: &[u32]) -> &[Self] {
bytemuck::checked::cast_slice(u32s)
}
fn try_from_u32_slice(u32s: &[u32]) -> Result<&[Self], CheckedCastError> {
bytemuck::checked::try_cast_slice(u32s)
}
}
pub trait ExtElem : Elem
+ From<Self::SubElem>
+ ops::Neg<Output = Self>
+ cmp::PartialEq
+ cmp::Eq
+ ops::Add<Self::SubElem, Output = Self>
+ ops::Sub<Self::SubElem, Output = Self>
+ ops::Mul<Self::SubElem, Output = Self>
+ ops::AddAssign<Self::SubElem>
+ ops::SubAssign<Self::SubElem>
+ ops::MulAssign<Self::SubElem>
{
type SubElem: Elem
+ ops::Add<Self, Output = Self>
+ ops::Sub<Self, Output = Self>
+ ops::Mul<Self, Output = Self>;
const EXT_SIZE: usize;
fn from_subfield(elem: &Self::SubElem) -> Self;
fn from_subelems(elems: impl IntoIterator<Item = Self::SubElem>) -> Self;
fn subelems(&self) -> &[Self::SubElem];
}
pub trait RootsOfUnity: Sized + 'static {
const MAX_ROU_PO2: usize;
const ROU_FWD: &'static [Self];
const ROU_REV: &'static [Self];
}
pub fn map_pow<E: super::field::Elem>(base: E, exponents: &[usize]) -> Vec<E> {
let mut result = Vec::with_capacity(exponents.len());
let mut prev_exp: usize;
match exponents.first() {
None => return result,
Some(&exp) => {
result.push(base.pow(exp));
prev_exp = exp;
}
}
for exp in exponents.iter().skip(1).copied() {
assert!(
prev_exp < exp,
"Expecting exponents to be strictly increasing but {prev_exp} is not less than {exp}"
);
if exp == prev_exp + 1 {
result.push(*result.last().unwrap() * base);
} else {
result.push(*result.last().unwrap() * base.pow(exp - prev_exp));
}
prev_exp = exp;
}
result
}
#[cfg(test)]
mod tests {
use core::fmt::Debug;
use rand::Rng;
use super::{Elem, ExtElem, RootsOfUnity};
pub fn test_roots_of_unity<F: Elem + RootsOfUnity + Debug>() {
let mut cur: Option<F> = None;
for &rou in F::ROU_FWD.iter().rev() {
if let Some(ref mut curval) = &mut cur {
*curval *= *curval;
assert_eq!(*curval, rou);
} else {
cur = Some(rou);
}
}
assert_eq!(cur, Some(F::ONE));
for (&fwd, &rev) in F::ROU_FWD.iter().zip(F::ROU_REV.iter()) {
assert_eq!(fwd * rev, F::ONE);
}
}
fn non_zero_rand<F: Elem>(r: &mut impl Rng) -> F {
loop {
let val = F::random(r);
if val != F::ZERO {
return val;
}
}
}
pub fn test_field_ops<F>(p_u64: u64)
where
F: Elem + Into<u64> + From<u64> + Debug,
{
let p: u128 = p_u64 as _;
assert_eq!(F::from(0), F::ZERO);
assert_eq!(F::from(p_u64), F::ZERO);
assert_eq!(F::from(1), F::ONE);
assert_eq!(F::from(p_u64 - 1) + F::from(1), F::ZERO);
assert_eq!(F::ZERO.inv(), F::ZERO);
assert_eq!(F::ONE.inv(), F::ONE);
let mut rng = rand::rng();
for _ in 0..1000 {
let x: F = non_zero_rand(&mut rng);
let y: F = non_zero_rand(&mut rng);
let xi: u128 = x.into() as _;
let yi: u128 = y.into() as _;
assert_eq!((x + y).into() as u128, (xi + yi) % p);
assert_eq!((x * y).into() as u128, (xi * yi) % p);
assert_eq!((x - y).into() as u128, (xi + p - yi) % p);
let xinv = x.inv();
if x != F::ONE {
assert!(xinv != x);
}
assert_eq!(xinv * x, F::ONE);
}
let base: F = non_zero_rand(&mut rng);
let map_pow_cases: &[&[usize]] = &[&[], &[0], &[0, 1, 2, 3], &[1, 18, 19, 1234, 5678]];
for exps in map_pow_cases.iter() {
let expected: alloc::vec::Vec<_> = exps.iter().map(|&exp| base.pow(exp)).collect();
let actual = super::map_pow(base, exps);
assert_eq!(expected, actual);
}
}
pub fn test_ext_field_ops<E: ExtElem>() {
let mut r = rand::rng();
let x = E::random(&mut r);
let y = E::random(&mut r);
let mut e = x;
let promote = |e| E::from(e);
e += y;
assert_eq!(e, x + y);
assert_eq!(e, y + x);
assert_eq!(x, e - y);
assert_eq!(-x, y - e);
e -= y;
assert_eq!(e, x);
e *= y;
assert_eq!(e, x * y);
assert_eq!(e, y * x);
assert_eq!(x, e * y.inv());
assert_eq!(x.inv(), y * e.inv());
e *= y.inv();
assert_eq!(e, x);
let b = E::SubElem::random(&mut r);
e += b;
assert_eq!(e, x + b);
assert_eq!(e, b + x);
assert_eq!(e, x + promote(b));
assert_eq!(x, e - promote(b));
assert_eq!(x, e - b);
assert_eq!(-x, b - e);
assert_eq!(-x, promote(b) - e);
e -= b;
assert_eq!(e, x);
e *= b;
assert_eq!(e, x * b);
assert_eq!(e, b * x);
assert_eq!(e, x * promote(b));
assert_eq!(x, e * b.inv());
assert_eq!(x, b.inv() * e);
assert_eq!(x, e * promote(b.inv()));
assert_eq!(x, e * promote(b).inv());
assert_eq!(x.inv(), b * e.inv());
e *= b.inv();
assert_eq!(e, x);
}
}