use alloc::format;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::array;
use core::fmt::{self, Debug, Display, Formatter};
use core::iter::{Product, Sum};
use core::marker::PhantomData;
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use itertools::Itertools;
use num_bigint::BigUint;
use p3_util::{as_base_slice, as_base_slice_mut, flatten_to_base, reconstitute_from_base};
use rand::distr::StandardUniform;
use rand::prelude::Distribution;
use serde::{Deserialize, Serialize};
use super::{HasFrobenius, HasTwoAdicBinomialExtension, PackedBinomialExtensionField};
use crate::extension::{BinomiallyExtendable, BinomiallyExtendableAlgebra};
use crate::field::Field;
use crate::{
Algebra, BasedVectorSpace, Dup, ExtensionField, Packable, PrimeCharacteristicRing,
RawDataSerializable, TwoAdicField, field_to_array,
};
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize, PartialOrd, Ord)]
#[repr(transparent)] #[must_use]
pub struct BinomialExtensionField<F, const D: usize, A = F> {
#[serde(
with = "p3_util::array_serialization",
bound(serialize = "A: Serialize", deserialize = "A: Deserialize<'de>")
)]
pub(crate) value: [A; D],
_phantom: PhantomData<F>,
}
impl<F, A, const D: usize> BinomialExtensionField<F, D, A> {
#[inline]
pub const fn new(value: [A; D]) -> Self {
Self {
value,
_phantom: PhantomData,
}
}
}
impl<F: Copy, const D: usize> BinomialExtensionField<F, D, F> {
#[inline]
pub const fn new_array<const N: usize>(input: [[F; D]; N]) -> [Self; N] {
const { assert!(N > 0) }
let mut output = [Self::new(input[0]); N];
let mut i = 1;
while i < N {
output[i] = Self::new(input[i]);
i += 1;
}
output
}
}
impl<F: Field, A: Algebra<F>, const D: usize> Default for BinomialExtensionField<F, D, A> {
fn default() -> Self {
Self::new(array::from_fn(|_| A::ZERO))
}
}
impl<F: Field, A: Algebra<F>, const D: usize> From<A> for BinomialExtensionField<F, D, A> {
fn from(x: A) -> Self {
Self::new(field_to_array(x))
}
}
impl<F, A, const D: usize> From<[A; D]> for BinomialExtensionField<F, D, A> {
#[inline]
fn from(x: [A; D]) -> Self {
Self {
value: x,
_phantom: PhantomData,
}
}
}
impl<F: BinomiallyExtendable<D>, const D: usize> Packable for BinomialExtensionField<F, D> {}
impl<F: BinomiallyExtendable<D>, A: Algebra<F>, const D: usize> BasedVectorSpace<A>
for BinomialExtensionField<F, D, A>
{
const DIMENSION: usize = D;
#[inline]
fn as_basis_coefficients_slice(&self) -> &[A] {
&self.value
}
#[inline]
fn from_basis_coefficients_fn<Fn: FnMut(usize) -> A>(f: Fn) -> Self {
Self::new(array::from_fn(f))
}
#[inline]
fn from_basis_coefficients_iter<I: ExactSizeIterator<Item = A>>(mut iter: I) -> Option<Self> {
(iter.len() == D).then(|| Self::new(array::from_fn(|_| iter.next().unwrap()))) }
#[inline]
fn flatten_to_base(vec: Vec<Self>) -> Vec<A> {
unsafe {
flatten_to_base::<A, Self>(vec)
}
}
#[inline]
fn reconstitute_from_base(vec: Vec<A>) -> Vec<Self> {
unsafe {
reconstitute_from_base::<A, Self>(vec)
}
}
}
impl<F: BinomiallyExtendable<D>, const D: usize> ExtensionField<F>
for BinomialExtensionField<F, D>
{
type ExtensionPacking = PackedBinomialExtensionField<F, F::Packing, D>;
#[inline]
fn is_in_basefield(&self) -> bool {
self.value[1..].iter().all(F::is_zero)
}
#[inline]
fn as_base(&self) -> Option<F> {
<Self as ExtensionField<F>>::is_in_basefield(self).then(|| self.value[0])
}
}
impl<F: BinomiallyExtendable<D>, const D: usize> HasFrobenius<F> for BinomialExtensionField<F, D> {
#[inline]
fn frobenius(&self) -> Self {
let mut res = Self::ZERO;
for (i, z) in F::DTH_ROOT.powers().take(D).enumerate() {
res.value[i] = self.value[i] * z;
}
res
}
#[inline]
fn repeated_frobenius(&self, count: usize) -> Self {
if count == 0 {
return *self;
} else if count >= D {
return self.repeated_frobenius(count % D);
}
let z0 = F::DTH_ROOT.exp_u64(count as u64);
let mut res = Self::ZERO;
for (i, z) in z0.powers().take(D).enumerate() {
res.value[i] = self.value[i] * z;
}
res
}
#[inline]
fn pseudo_inv(&self) -> Self {
let mut prod_conj = self.frobenius();
for _ in 2..D {
prod_conj = (prod_conj * *self).frobenius();
}
let a = self.value;
let b = prod_conj.value;
let mut w_coeff = F::ZERO;
for i in 1..D {
w_coeff += a[i] * b[D - i];
}
let norm = F::dot_product(&[a[0], F::W], &[b[0], w_coeff]);
debug_assert_eq!(Self::from(norm), *self * prod_conj);
prod_conj * norm.inverse()
}
}
impl<F, A, const D: usize> PrimeCharacteristicRing for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: BinomiallyExtendableAlgebra<F, D> + Copy,
{
type PrimeSubfield = <A as PrimeCharacteristicRing>::PrimeSubfield;
const ZERO: Self = Self::new([A::ZERO; D]);
const ONE: Self = Self::new(field_to_array(A::ONE));
const TWO: Self = Self::new(field_to_array(A::TWO));
const NEG_ONE: Self = Self::new(field_to_array(A::NEG_ONE));
#[inline]
fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
<A as PrimeCharacteristicRing>::from_prime_subfield(f).into()
}
#[inline]
fn halve(&self) -> Self {
Self::new(array::from_fn(|i| self.value[i].halve()))
}
#[inline(always)]
fn square(&self) -> Self {
let mut res = Self::default();
let w = F::W;
binomial_square(&self.value, &mut res.value, w);
res
}
#[inline]
fn mul_2exp_u64(&self, exp: u64) -> Self {
Self::new(array::from_fn(|i| self.value[i].mul_2exp_u64(exp)))
}
#[inline]
fn div_2exp_u64(&self, exp: u64) -> Self {
Self::new(array::from_fn(|i| self.value[i].div_2exp_u64(exp)))
}
#[inline]
fn zero_vec(len: usize) -> Vec<Self> {
unsafe { reconstitute_from_base(F::zero_vec(len * D)) }
}
}
impl<F: BinomiallyExtendable<D>, const D: usize> Algebra<F> for BinomialExtensionField<F, D> {}
impl<F: BinomiallyExtendable<D>, const D: usize> RawDataSerializable
for BinomialExtensionField<F, D>
{
const NUM_BYTES: usize = F::NUM_BYTES * D;
#[inline]
fn into_bytes(self) -> impl IntoIterator<Item = u8> {
self.value.into_iter().flat_map(|x| x.into_bytes())
}
#[inline]
fn into_byte_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u8> {
F::into_byte_stream(input.into_iter().flat_map(|x| x.value))
}
#[inline]
fn into_u32_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u32> {
F::into_u32_stream(input.into_iter().flat_map(|x| x.value))
}
#[inline]
fn into_u64_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u64> {
F::into_u64_stream(input.into_iter().flat_map(|x| x.value))
}
#[inline]
fn into_parallel_byte_streams<const N: usize>(
input: impl IntoIterator<Item = [Self; N]>,
) -> impl IntoIterator<Item = [u8; N]> {
F::into_parallel_byte_streams(
input
.into_iter()
.flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
)
}
#[inline]
fn into_parallel_u32_streams<const N: usize>(
input: impl IntoIterator<Item = [Self; N]>,
) -> impl IntoIterator<Item = [u32; N]> {
F::into_parallel_u32_streams(
input
.into_iter()
.flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
)
}
#[inline]
fn into_parallel_u64_streams<const N: usize>(
input: impl IntoIterator<Item = [Self; N]>,
) -> impl IntoIterator<Item = [u64; N]> {
F::into_parallel_u64_streams(
input
.into_iter()
.flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
)
}
}
impl<F: BinomiallyExtendable<D>, const D: usize> Field for BinomialExtensionField<F, D> {
type Packing = Self;
const GENERATOR: Self = Self::new(F::EXT_GENERATOR);
fn try_inverse(&self) -> Option<Self> {
if self.is_zero() {
return None;
}
let mut res = Self::default();
match D {
2 => quadratic_inv(&self.value, &mut res.value, F::W),
3 => cubic_inv(&self.value, &mut res.value, F::W),
4 => quartic_inv(&self.value, &mut res.value, F::W),
5 => res = quintic_inv(self),
8 => octic_inv(&self.value, &mut res.value, F::W),
_ => res = self.pseudo_inv(),
}
Some(res)
}
#[inline]
fn add_slices(slice_1: &mut [Self], slice_2: &[Self]) {
unsafe {
let base_slice_1 = as_base_slice_mut(slice_1);
let base_slice_2 = as_base_slice(slice_2);
F::add_slices(base_slice_1, base_slice_2);
}
}
#[inline]
fn order() -> BigUint {
F::order().pow(D as u32)
}
}
impl<F, const D: usize> Display for BinomialExtensionField<F, D>
where
F: BinomiallyExtendable<D>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
if self.is_zero() {
write!(f, "0")
} else {
let str = self
.value
.iter()
.enumerate()
.filter(|(_, x)| !x.is_zero())
.map(|(i, x)| match (i, x.is_one()) {
(0, _) => format!("{x}"),
(1, true) => "X".to_string(),
(1, false) => format!("{x} X"),
(_, true) => format!("X^{i}"),
(_, false) => format!("{x} X^{i}"),
})
.join(" + ");
write!(f, "{str}")
}
}
}
impl<F, A, const D: usize> Neg for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: Algebra<F>,
{
type Output = Self;
#[inline]
fn neg(self) -> Self {
Self::new(self.value.map(A::neg))
}
}
impl<F, A, const D: usize> Add for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: BinomiallyExtendableAlgebra<F, D>,
{
type Output = Self;
#[inline]
fn add(self, rhs: Self) -> Self {
let value = A::binomial_add(&self.value, &rhs.value);
Self::new(value)
}
}
impl<F, A, const D: usize> Add<A> for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: Algebra<F>,
{
type Output = Self;
#[inline]
fn add(mut self, rhs: A) -> Self {
self.value[0] += rhs;
self
}
}
impl<F, A, const D: usize> AddAssign for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: BinomiallyExtendableAlgebra<F, D>,
{
#[inline]
fn add_assign(&mut self, rhs: Self) {
self.value = A::binomial_add(&self.value, &rhs.value);
}
}
impl<F, A, const D: usize> AddAssign<A> for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: Algebra<F>,
{
#[inline]
fn add_assign(&mut self, rhs: A) {
self.value[0] += rhs;
}
}
impl<F, A, const D: usize> Sum for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: BinomiallyExtendableAlgebra<F, D> + Copy,
{
#[inline]
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO)
}
}
impl<F, A, const D: usize> Sub for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: BinomiallyExtendableAlgebra<F, D>,
{
type Output = Self;
#[inline]
fn sub(self, rhs: Self) -> Self {
let value = A::binomial_sub(&self.value, &rhs.value);
Self::new(value)
}
}
impl<F, A, const D: usize> Sub<A> for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: Algebra<F>,
{
type Output = Self;
#[inline]
fn sub(self, rhs: A) -> Self {
let mut res = self.value;
res[0] -= rhs;
Self::new(res)
}
}
impl<F, A, const D: usize> SubAssign for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: BinomiallyExtendableAlgebra<F, D>,
{
#[inline]
fn sub_assign(&mut self, rhs: Self) {
self.value = A::binomial_sub(&self.value, &rhs.value);
}
}
impl<F, A, const D: usize> SubAssign<A> for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: Algebra<F>,
{
#[inline]
fn sub_assign(&mut self, rhs: A) {
self.value[0] -= rhs;
}
}
impl<F, A, const D: usize> Mul for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: BinomiallyExtendableAlgebra<F, D>,
{
type Output = Self;
#[inline]
fn mul(self, rhs: Self) -> Self {
let a = self.value;
let b = rhs.value;
let mut res = Self::default();
let w = F::W;
A::binomial_mul(&a, &b, &mut res.value, w);
res
}
}
impl<F, A, const D: usize> Mul<A> for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: BinomiallyExtendableAlgebra<F, D>,
{
type Output = Self;
#[inline]
fn mul(self, rhs: A) -> Self {
Self::new(A::binomial_base_mul(self.value, rhs))
}
}
impl<F, A, const D: usize> MulAssign for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: BinomiallyExtendableAlgebra<F, D>,
{
#[inline]
fn mul_assign(&mut self, rhs: Self) {
*self = self.clone() * rhs;
}
}
impl<F, A, const D: usize> MulAssign<A> for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: BinomiallyExtendableAlgebra<F, D>,
{
#[inline]
fn mul_assign(&mut self, rhs: A) {
*self = self.clone() * rhs;
}
}
impl<F, A, const D: usize> Product for BinomialExtensionField<F, D, A>
where
F: BinomiallyExtendable<D>,
A: BinomiallyExtendableAlgebra<F, D> + Copy,
{
#[inline]
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(|acc, x| acc * x).unwrap_or(Self::ONE)
}
}
impl<F, const D: usize> Div for BinomialExtensionField<F, D>
where
F: BinomiallyExtendable<D>,
{
type Output = Self;
#[allow(clippy::suspicious_arithmetic_impl)]
#[inline]
fn div(self, rhs: Self) -> Self::Output {
self * rhs.inverse()
}
}
impl<F, const D: usize> DivAssign for BinomialExtensionField<F, D>
where
F: BinomiallyExtendable<D>,
{
#[inline]
fn div_assign(&mut self, rhs: Self) {
*self = *self / rhs;
}
}
impl<F: BinomiallyExtendable<D>, const D: usize> Distribution<BinomialExtensionField<F, D>>
for StandardUniform
where
Self: Distribution<F>,
{
#[inline]
fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> BinomialExtensionField<F, D> {
BinomialExtensionField::new(array::from_fn(|_| self.sample(rng)))
}
}
impl<F: Field + HasTwoAdicBinomialExtension<D>, const D: usize> TwoAdicField
for BinomialExtensionField<F, D>
{
const TWO_ADICITY: usize = F::EXT_TWO_ADICITY;
#[inline]
fn two_adic_generator(bits: usize) -> Self {
Self::new(F::ext_two_adic_generator(bits))
}
}
#[inline]
pub fn vector_add<R: PrimeCharacteristicRing + Add<R2, Output = R>, R2: Dup, const D: usize>(
a: &[R; D],
b: &[R2; D],
) -> [R; D] {
array::from_fn(|i| a[i].dup() + b[i].dup())
}
#[inline]
pub(crate) fn vector_sub<
R: PrimeCharacteristicRing + Sub<R2, Output = R>,
R2: Dup,
const D: usize,
>(
a: &[R; D],
b: &[R2; D],
) -> [R; D] {
array::from_fn(|i| a[i].dup() - b[i].dup())
}
#[inline]
pub(super) fn binomial_mul<
F: Field,
R: Algebra<F> + Algebra<R2>,
R2: Algebra<F>,
const D: usize,
>(
a: &[R; D],
b: &[R2; D],
res: &mut [R; D],
w: F,
) {
match D {
2 => quadratic_mul(a, b, res, w),
3 => cubic_mul(a, b, res, w),
4 => quartic_mul(a, b, res, w),
5 => quintic_mul(a, b, res, w),
8 => octic_mul(a, b, res, w),
_ =>
{
#[allow(clippy::needless_range_loop)]
for i in 0..D {
for j in 0..D {
if i + j >= D {
res[i + j - D] += a[i].dup() * w * b[j].dup();
} else {
res[i + j] += a[i].dup() * b[j].dup();
}
}
}
}
}
}
#[inline]
pub(super) fn binomial_square<F: Field, R: Algebra<F>, const D: usize>(
a: &[R; D],
res: &mut [R; D],
w: F,
) {
match D {
2 => {
let a1_w = a[1].dup() * w;
res[0] = R::dot_product(a[..].try_into().unwrap(), &[a[0].dup(), a1_w]);
res[1] = a[0].dup() * a[1].double();
}
3 => cubic_square(a, res, w),
4 => quartic_square(a, res, w),
5 => quintic_square(a, res, w),
8 => octic_square(a, res, w),
_ => binomial_mul::<F, R, R, D>(a, a, res, w),
}
}
#[inline]
fn quadratic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
where
F: Field,
R: Algebra<F> + Algebra<R2>,
R2: Algebra<F>,
{
let b1_w = b[1].dup() * w;
res[0] = R::dot_product(a[..].try_into().unwrap(), &[b[0].dup().into(), b1_w.into()]);
res[1] = R::dot_product(
&[a[0].dup(), a[1].dup()],
&[b[1].dup().into(), b[0].dup().into()],
);
}
#[inline]
fn quadratic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
assert_eq!(D, 2);
let neg_a1 = -a[1];
let scalar = F::dot_product(&[a[0], neg_a1], &[a[0], w * a[1]]).inverse();
res[0] = a[0] * scalar;
res[1] = neg_a1 * scalar;
}
#[inline]
fn cubic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
assert_eq!(D, 3);
let a0_square = a[0].square();
let a1_square = a[1].square();
let a2_w = w * a[2];
let a0_a1 = a[0] * a[1];
let scalar = (a0_square * a[0] + w * a[1] * a1_square + a2_w.square() * a[2]
- (F::ONE + F::TWO) * a2_w * a0_a1)
.inverse();
res[0] = scalar * (a0_square - a[1] * a2_w);
res[1] = scalar * (a2_w * a[2] - a0_a1);
res[2] = scalar * (a1_square - a[0] * a[2]);
}
#[inline]
fn cubic_mul<F: Field, R: Algebra<F> + Algebra<R2>, R2: Algebra<F>, const D: usize>(
a: &[R; D],
b: &[R2; D],
res: &mut [R; D],
w: F,
) {
assert_eq!(D, 3);
let a0_b0 = a[0].dup() * b[0].dup();
let a1_b1 = a[1].dup() * b[1].dup();
let a2_b2 = a[2].dup() * b[2].dup();
res[0] = a0_b0.dup()
+ ((a[1].dup() + a[2].dup()) * (b[1].dup() + b[2].dup()) - a1_b1.dup() - a2_b2.dup()) * w;
res[1] = (a[0].dup() + a[1].dup()) * (b[0].dup() + b[1].dup()) - a0_b0.dup() - a1_b1.dup()
+ a2_b2.dup() * w;
res[2] = (a[0].dup() + a[2].dup()) * (b[0].dup() + b[2].dup()) - a0_b0 - a2_b2 + a1_b1;
}
#[inline]
fn cubic_square<F: Field, R: Algebra<F>, const D: usize>(a: &[R; D], res: &mut [R; D], w: F) {
assert_eq!(D, 3);
let w_a2 = a[2].dup() * w;
res[0] = a[0].square() + (a[1].dup() * w_a2.dup()).double();
res[1] = w_a2 * a[2].dup() + (a[0].dup() * a[1].dup()).double();
res[2] = a[1].square() + (a[0].dup() * a[2].dup()).double();
}
#[inline]
pub fn quartic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
where
F: Field,
R: Algebra<F> + Algebra<R2>,
R2: Algebra<F>,
{
assert_eq!(D, 4);
let b_r_rev: [R; 5] = [
b[3].dup().into(),
b[2].dup().into(),
b[1].dup().into(),
b[0].dup().into(),
w.into(),
];
let w_coeff_0 =
R::dot_product::<3>(a[1..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[3..].try_into().unwrap());
let w_coeff_1 =
R::dot_product::<2>(a[2..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
res[1] = R::dot_product(
&[a[0].dup(), a[1].dup(), w_coeff_1],
b_r_rev[2..].try_into().unwrap(),
);
let b3_w = b[3].dup() * w;
res[2] = R::dot_product::<4>(
a[..4].try_into().unwrap(),
&[
b_r_rev[1].dup(),
b_r_rev[2].dup(),
b_r_rev[3].dup(),
b3_w.into(),
],
);
res[3] = R::dot_product::<4>(a[..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
}
#[inline]
fn quartic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
assert_eq!(D, 4);
let neg_a1 = -a[1];
let a3_w = a[3] * w;
let norm_0 = F::dot_product(&[a[0], a[2], neg_a1.double()], &[a[0], a[2] * w, a3_w]);
let norm_1 = F::dot_product(&[a[0], a[1], -a[3]], &[a[2].double(), neg_a1, a3_w]);
let mut inv = [F::ZERO; 2];
quadratic_inv(&[norm_0, norm_1], &mut inv, w);
let mut out_evn = [F::ZERO; 2];
let mut out_odd = [F::ZERO; 2];
quadratic_mul(&[a[0], a[2]], &inv, &mut out_evn, w);
quadratic_mul(&[a[1], a[3]], &inv, &mut out_odd, w);
res[0] = out_evn[0];
res[1] = -out_odd[0];
res[2] = out_evn[1];
res[3] = -out_odd[1];
}
#[inline]
fn quartic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
where
F: Field,
R: Algebra<F>,
{
assert_eq!(D, 4);
let two_a0 = a[0].double();
let two_a1 = a[1].double();
let two_a2 = a[2].double();
let a2_w = a[2].dup() * w;
let a3_w = a[3].dup() * w;
res[0] = R::dot_product(
&[a[0].dup(), a2_w, two_a1],
&[a[0].dup(), a[2].dup(), a3_w.dup()],
);
res[1] = R::dot_product(&[two_a0.dup(), two_a2.dup()], &[a[1].dup(), a3_w.dup()]);
res[2] = R::dot_product(
&[a[1].dup(), a3_w, two_a0.dup()],
&[a[1].dup(), a[3].dup(), a[2].dup()],
);
res[3] = R::dot_product(&[two_a0, two_a2], &[a[3].dup(), a[1].dup()]);
}
pub fn quintic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
where
F: Field,
R: Algebra<F> + Algebra<R2>,
R2: Algebra<F>,
{
assert_eq!(D, 5);
let b_r_rev: [R; 6] = [
b[4].dup().into(),
b[3].dup().into(),
b[2].dup().into(),
b[1].dup().into(),
b[0].dup().into(),
w.into(),
];
let w_coeff_0 =
R::dot_product::<4>(a[1..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[4..].try_into().unwrap());
let w_coeff_1 =
R::dot_product::<3>(a[2..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
res[1] = R::dot_product(
&[a[0].dup(), a[1].dup(), w_coeff_1],
b_r_rev[3..].try_into().unwrap(),
);
let w_coeff_2 =
R::dot_product::<2>(a[3..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
res[2] = R::dot_product(
&[a[0].dup(), a[1].dup(), a[2].dup(), w_coeff_2],
b_r_rev[2..].try_into().unwrap(),
);
let b4_w = b[4].dup() * w;
res[3] = R::dot_product::<5>(
a[..5].try_into().unwrap(),
&[
b_r_rev[1].dup(),
b_r_rev[2].dup(),
b_r_rev[3].dup(),
b_r_rev[4].dup(),
b4_w.into(),
],
);
res[4] = R::dot_product::<5>(a[..].try_into().unwrap(), b_r_rev[..5].try_into().unwrap());
}
#[inline]
fn quintic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
where
F: Field,
R: Algebra<F>,
{
assert_eq!(D, 5);
let two_a0 = a[0].double();
let two_a1 = a[1].double();
let two_a2 = a[2].double();
let two_a3 = a[3].double();
let w_a3 = a[3].dup() * w;
let w_a4 = a[4].dup() * w;
res[0] = R::dot_product(
&[a[0].dup(), w_a4.dup(), w_a3.dup()],
&[a[0].dup(), two_a1.dup(), two_a2.dup()],
);
res[1] = R::dot_product(
&[w_a3, two_a0.dup(), w_a4.dup()],
&[a[3].dup(), a[1].dup(), two_a2],
);
res[2] = R::dot_product(
&[a[1].dup(), two_a0.dup(), w_a4.dup()],
&[a[1].dup(), a[2].dup(), two_a3],
);
res[3] = R::dot_product(
&[w_a4, two_a0.dup(), two_a1.dup()],
&[a[4].dup(), a[3].dup(), a[2].dup()],
);
res[4] = R::dot_product(
&[a[2].dup(), two_a0, two_a1],
&[a[2].dup(), a[4].dup(), a[3].dup()],
);
}
#[inline]
fn octic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
where
F: Field,
R: Algebra<F>,
{
assert_eq!(D, 8);
let a0_2 = a[0].double();
let a1_2 = a[1].double();
let a2_2 = a[2].double();
let a3_2 = a[3].double();
let w_a4 = a[4].dup() * w;
let w_a5 = a[5].dup() * w;
let w_a6 = a[6].dup() * w;
let w_a7 = a[7].dup() * w;
let w_a5_2 = w_a5.double();
let w_a6_2 = w_a6.double();
let w_a7_2 = w_a7.double();
res[0] = R::dot_product(
&[a[0].dup(), a[1].dup(), a[2].dup(), a[3].dup(), a[4].dup()],
&[a[0].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2.dup(), w_a4],
);
res[1] = R::dot_product(
&[a0_2.dup(), a[2].dup(), a[3].dup(), a[4].dup()],
&[a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2],
);
res[2] = R::dot_product(
&[a0_2.dup(), a[1].dup(), a[3].dup(), a[4].dup(), a[5].dup()],
&[a[2].dup(), a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5],
);
res[3] = R::dot_product(
&[a0_2.dup(), a1_2.dup(), a[4].dup(), a[5].dup()],
&[a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6_2],
);
res[4] = R::dot_product(
&[a0_2.dup(), a1_2.dup(), a[2].dup(), a[5].dup(), a[6].dup()],
&[a[4].dup(), a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6],
);
res[5] = R::dot_product(
&[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[6].dup()],
&[a[5].dup(), a[4].dup(), a[3].dup(), w_a7_2],
);
res[6] = R::dot_product(
&[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[3].dup(), a[7].dup()],
&[a[6].dup(), a[5].dup(), a[4].dup(), a[3].dup(), w_a7],
);
res[7] = R::dot_product(
&[a0_2, a1_2, a2_2, a3_2],
&[a[7].dup(), a[6].dup(), a[5].dup(), a[4].dup()],
);
}
#[inline]
fn quintic_inv<F: BinomiallyExtendable<D>, const D: usize>(
a: &BinomialExtensionField<F, D>,
) -> BinomialExtensionField<F, D> {
let a_exp_q = a.frobenius();
let a_exp_q_plus_q_sq = (*a * a_exp_q).frobenius();
let prod_conj = a_exp_q_plus_q_sq * a_exp_q_plus_q_sq.repeated_frobenius(2);
let a_vals = a.value;
let mut b = prod_conj.value;
b.reverse();
let w_coeff = F::dot_product::<4>(a.value[1..].try_into().unwrap(), b[..4].try_into().unwrap());
let norm = F::dot_product::<2>(&[a_vals[0], F::W], &[b[4], w_coeff]);
debug_assert_eq!(BinomialExtensionField::<F, D>::from(norm), *a * prod_conj);
prod_conj * norm.inverse()
}
#[inline]
fn compute_coefficient<
F,
R,
const D: usize,
const D_PLUS_1: usize,
const N: usize,
const D_PLUS_1_MIN_N: usize,
>(
a: &[R; D],
b_rev: &[R; D_PLUS_1],
) -> R
where
F: Field,
R: Algebra<F>,
{
let w_coeff = R::dot_product::<N>(
a[(D - N)..].try_into().unwrap(),
b_rev[..N].try_into().unwrap(),
);
let mut scratch: [R; D_PLUS_1_MIN_N] = array::from_fn(|i| a[i].dup());
scratch[D_PLUS_1_MIN_N - 1] = w_coeff;
R::dot_product(&scratch, b_rev[N..].try_into().unwrap())
}
#[inline]
pub fn octic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
where
F: Field,
R: Algebra<F> + Algebra<R2>,
R2: Algebra<F>,
{
assert_eq!(D, 8);
let a: &[R; 8] = a[..].try_into().unwrap();
let mut b_r_rev: [R; 9] = [
b[7].dup().into(),
b[6].dup().into(),
b[5].dup().into(),
b[4].dup().into(),
b[3].dup().into(),
b[2].dup().into(),
b[1].dup().into(),
b[0].dup().into(),
w.into(),
];
res[0] = compute_coefficient::<F, R, 8, 9, 7, 2>(a, &b_r_rev);
res[1] = compute_coefficient::<F, R, 8, 9, 6, 3>(a, &b_r_rev);
res[2] = compute_coefficient::<F, R, 8, 9, 5, 4>(a, &b_r_rev);
res[3] = compute_coefficient::<F, R, 8, 9, 4, 5>(a, &b_r_rev);
res[4] = compute_coefficient::<F, R, 8, 9, 3, 6>(a, &b_r_rev);
res[5] = compute_coefficient::<F, R, 8, 9, 2, 7>(a, &b_r_rev);
b_r_rev[8] *= b[7].dup();
res[6] = R::dot_product::<8>(a, b_r_rev[1..].try_into().unwrap());
res[7] = R::dot_product::<8>(a, b_r_rev[..8].try_into().unwrap());
}
#[inline]
fn octic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
assert_eq!(D, 8);
let evns = [a[0], a[2], a[4], a[6]];
let odds = [a[1], a[3], a[5], a[7]];
let mut evns_sq = [F::ZERO; 4];
let mut odds_sq = [F::ZERO; 4];
quartic_square(&evns, &mut evns_sq, w);
quartic_square(&odds, &mut odds_sq, w);
let norm = [
evns_sq[0] - w * odds_sq[3],
evns_sq[1] - odds_sq[0],
evns_sq[2] - odds_sq[1],
evns_sq[3] - odds_sq[2],
];
let mut norm_inv = [F::ZERO; 4];
quartic_inv(&norm, &mut norm_inv, w);
let mut out_evn = [F::ZERO; 4];
let mut out_odd = [F::ZERO; 4];
quartic_mul(&evns, &norm_inv, &mut out_evn, w);
quartic_mul(&odds, &norm_inv, &mut out_odd, w);
res[0] = out_evn[0];
res[1] = -out_odd[0];
res[2] = out_evn[1];
res[3] = -out_odd[1];
res[4] = out_evn[2];
res[5] = -out_odd[2];
res[6] = out_evn[3];
res[7] = -out_odd[3];
}