use alloc::vec::Vec;
use core::fmt::{self, Debug, Display, Formatter};
use core::iter::{Product, Sum};
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use crate::extension::OEF;
#[derive(Copy, Clone)]
pub struct ExtensionAlgebra<F: OEF<D>, const D: usize>(pub [F; D]);
impl<F: OEF<D>, const D: usize> ExtensionAlgebra<F, D> {
pub const ZERO: Self = Self([F::ZERO; D]);
pub fn one() -> Self {
F::ONE.into()
}
pub fn from_basefield_array(arr: [F; D]) -> Self {
Self(arr)
}
pub fn to_basefield_array(self) -> [F; D] {
self.0
}
pub fn scalar_mul(&self, scalar: F) -> Self {
let mut res = self.0;
res.iter_mut().for_each(|x| {
*x *= scalar;
});
Self(res)
}
}
impl<F: OEF<D>, const D: usize> From<F> for ExtensionAlgebra<F, D> {
fn from(x: F) -> Self {
let mut arr = [F::ZERO; D];
arr[0] = x;
Self(arr)
}
}
impl<F: OEF<D>, const D: usize> Display for ExtensionAlgebra<F, D> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "({})", self.0[0])?;
for i in 1..D {
write!(f, " + ({})*b^{i}", self.0[i])?;
}
Ok(())
}
}
impl<F: OEF<D>, const D: usize> Debug for ExtensionAlgebra<F, D> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<F: OEF<D>, const D: usize> Neg for ExtensionAlgebra<F, D> {
type Output = Self;
#[inline]
fn neg(self) -> Self {
let mut arr = self.0;
arr.iter_mut().for_each(|x| *x = -*x);
Self(arr)
}
}
impl<F: OEF<D>, const D: usize> Add for ExtensionAlgebra<F, D> {
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
let mut arr = self.0;
arr.iter_mut().zip(&rhs.0).for_each(|(x, &y)| *x += y);
Self(arr)
}
}
impl<F: OEF<D>, const D: usize> AddAssign for ExtensionAlgebra<F, D> {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs;
}
}
impl<F: OEF<D>, const D: usize> Sum for ExtensionAlgebra<F, D> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::ZERO, |acc, x| acc + x)
}
}
impl<F: OEF<D>, const D: usize> Sub for ExtensionAlgebra<F, D> {
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
let mut arr = self.0;
arr.iter_mut().zip(&rhs.0).for_each(|(x, &y)| *x -= y);
Self(arr)
}
}
impl<F: OEF<D>, const D: usize> SubAssign for ExtensionAlgebra<F, D> {
#[inline]
fn sub_assign(&mut self, rhs: Self) {
*self = *self - rhs;
}
}
impl<F: OEF<D>, const D: usize> Mul for ExtensionAlgebra<F, D> {
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
let mut res = [F::ZERO; D];
let w = F::from_basefield(F::W);
for i in 0..D {
for j in 0..D {
res[(i + j) % D] += if i + j < D {
self.0[i] * rhs.0[j]
} else {
w * self.0[i] * rhs.0[j]
}
}
}
Self(res)
}
}
impl<F: OEF<D>, const D: usize> MulAssign for ExtensionAlgebra<F, D> {
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = *self * rhs;
}
}
impl<F: OEF<D>, const D: usize> Product for ExtensionAlgebra<F, D> {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.fold(Self::one(), |acc, x| acc * x)
}
}
#[derive(Clone, Debug)]
pub struct PolynomialCoeffsAlgebra<F: OEF<D>, const D: usize> {
pub(crate) coeffs: Vec<ExtensionAlgebra<F, D>>,
}
impl<F: OEF<D>, const D: usize> PolynomialCoeffsAlgebra<F, D> {
pub fn new(coeffs: Vec<ExtensionAlgebra<F, D>>) -> Self {
PolynomialCoeffsAlgebra { coeffs }
}
pub fn eval(&self, x: ExtensionAlgebra<F, D>) -> ExtensionAlgebra<F, D> {
self.coeffs
.iter()
.rev()
.fold(ExtensionAlgebra::ZERO, |acc, &c| acc * x + c)
}
pub fn eval_with_powers(&self, powers: &[ExtensionAlgebra<F, D>]) -> ExtensionAlgebra<F, D> {
debug_assert_eq!(self.coeffs.len(), powers.len() + 1);
let acc = self.coeffs[0];
self.coeffs[1..]
.iter()
.zip(powers)
.fold(acc, |acc, (&x, &c)| acc + c * x)
}
pub fn eval_base(&self, x: F) -> ExtensionAlgebra<F, D> {
self.coeffs
.iter()
.rev()
.fold(ExtensionAlgebra::ZERO, |acc, &c| acc.scalar_mul(x) + c)
}
pub fn eval_base_with_powers(&self, powers: &[F]) -> ExtensionAlgebra<F, D> {
debug_assert_eq!(self.coeffs.len(), powers.len() + 1);
let acc = self.coeffs[0];
self.coeffs[1..]
.iter()
.zip(powers)
.fold(acc, |acc, (&x, &c)| acc + x.scalar_mul(c))
}
}
#[cfg(test)]
mod tests {
use alloc::vec::Vec;
use itertools::Itertools;
use crate::extension::algebra::ExtensionAlgebra;
use crate::extension::{Extendable, FieldExtension};
use crate::goldilocks_field::GoldilocksField;
use crate::types::{Field, Sample};
fn test_extension_algebra<F: Extendable<D>, const D: usize>() {
#[derive(Copy, Clone, Debug)]
enum ZeroOne {
Zero,
One,
}
let to_field = |zo: &ZeroOne| match zo {
ZeroOne::Zero => F::ZERO,
ZeroOne::One => F::ONE,
};
let to_fields = |x: &[ZeroOne], y: &[ZeroOne]| -> (F::Extension, F::Extension) {
let mut arr0 = [F::ZERO; D];
let mut arr1 = [F::ZERO; D];
arr0.copy_from_slice(&x.iter().map(to_field).collect::<Vec<_>>());
arr1.copy_from_slice(&y.iter().map(to_field).collect::<Vec<_>>());
(
<F as Extendable<D>>::Extension::from_basefield_array(arr0),
<F as Extendable<D>>::Extension::from_basefield_array(arr1),
)
};
let selector = |xs: Vec<ZeroOne>, ts: &[F::Extension]| -> F::Extension {
(0..2 * D)
.map(|i| match xs[i] {
ZeroOne::Zero => F::Extension::ONE - ts[i],
ZeroOne::One => ts[i],
})
.product()
};
let mul_mle = |ts: Vec<F::Extension>| -> [F::Extension; D] {
let mut ans = [F::Extension::ZERO; D];
for xs in (0..2 * D)
.map(|_| vec![ZeroOne::Zero, ZeroOne::One])
.multi_cartesian_product()
{
let (a, b) = to_fields(&xs[..D], &xs[D..]);
let c = a * b;
let res = selector(xs, &ts);
for i in 0..D {
ans[i] += res.scalar_mul(c.to_basefield_array()[i]);
}
}
ans
};
let ts = F::Extension::rand_vec(2 * D);
let mut arr0 = [F::Extension::ZERO; D];
let mut arr1 = [F::Extension::ZERO; D];
arr0.copy_from_slice(&ts[..D]);
arr1.copy_from_slice(&ts[D..]);
let x = ExtensionAlgebra::from_basefield_array(arr0);
let y = ExtensionAlgebra::from_basefield_array(arr1);
let z = x * y;
assert_eq!(z.0, mul_mle(ts));
}
mod base {
use super::*;
#[test]
fn test_algebra() {
test_extension_algebra::<GoldilocksField, 1>();
}
}
mod quadratic {
use super::*;
#[test]
fn test_algebra() {
test_extension_algebra::<GoldilocksField, 2>();
}
}
mod quartic {
use super::*;
#[test]
fn test_algebra() {
test_extension_algebra::<GoldilocksField, 4>();
}
}
}