1use alloc::format;
2use alloc::string::ToString;
3use alloc::vec::Vec;
4use core::array;
5use core::fmt::{self, Debug, Display, Formatter};
6use core::iter::{Product, Sum};
7use core::marker::PhantomData;
8use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
9
10use itertools::Itertools;
11use num_bigint::BigUint;
12use p3_util::{as_base_slice, as_base_slice_mut, flatten_to_base, reconstitute_from_base};
13use rand::distr::StandardUniform;
14use rand::prelude::Distribution;
15use serde::{Deserialize, Serialize};
16
17use super::{HasFrobenius, HasTwoAdicBinomialExtension, PackedBinomialExtensionField};
18use crate::extension::{BinomiallyExtendable, BinomiallyExtendableAlgebra};
19use crate::field::Field;
20use crate::{
21 Algebra, BasedVectorSpace, ExtensionField, Packable, PrimeCharacteristicRing,
22 RawDataSerializable, TwoAdicField, field_to_array,
23};
24
25#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Serialize, Deserialize, PartialOrd, Ord)]
26#[repr(transparent)] #[must_use]
28pub struct BinomialExtensionField<F, const D: usize, A = F> {
29 #[serde(
30 with = "p3_util::array_serialization",
31 bound(serialize = "A: Serialize", deserialize = "A: Deserialize<'de>")
32 )]
33 pub(crate) value: [A; D],
34 _phantom: PhantomData<F>,
35}
36
37impl<F, A, const D: usize> BinomialExtensionField<F, D, A> {
38 pub(crate) const fn new(value: [A; D]) -> Self {
39 Self {
40 value,
41 _phantom: PhantomData,
42 }
43 }
44}
45
46impl<F: Field, A: Algebra<F>, const D: usize> Default for BinomialExtensionField<F, D, A> {
47 fn default() -> Self {
48 Self::new(array::from_fn(|_| A::ZERO))
49 }
50}
51
52impl<F: Field, A: Algebra<F>, const D: usize> From<A> for BinomialExtensionField<F, D, A> {
53 fn from(x: A) -> Self {
54 Self::new(field_to_array(x))
55 }
56}
57
58impl<F: BinomiallyExtendable<D>, const D: usize> Packable for BinomialExtensionField<F, D> {}
59
60impl<F: BinomiallyExtendable<D>, A: Algebra<F>, const D: usize> BasedVectorSpace<A>
61 for BinomialExtensionField<F, D, A>
62{
63 const DIMENSION: usize = D;
64
65 #[inline]
66 fn as_basis_coefficients_slice(&self) -> &[A] {
67 &self.value
68 }
69
70 #[inline]
71 fn from_basis_coefficients_fn<Fn: FnMut(usize) -> A>(f: Fn) -> Self {
72 Self::new(array::from_fn(f))
73 }
74
75 #[inline]
76 fn from_basis_coefficients_iter<I: ExactSizeIterator<Item = A>>(mut iter: I) -> Option<Self> {
77 (iter.len() == D).then(|| Self::new(array::from_fn(|_| iter.next().unwrap()))) }
79
80 #[inline]
81 fn flatten_to_base(vec: Vec<Self>) -> Vec<A> {
82 unsafe {
83 flatten_to_base::<A, Self>(vec)
86 }
87 }
88
89 #[inline]
90 fn reconstitute_from_base(vec: Vec<A>) -> Vec<Self> {
91 unsafe {
92 reconstitute_from_base::<A, Self>(vec)
95 }
96 }
97}
98
99impl<F: BinomiallyExtendable<D>, const D: usize> ExtensionField<F>
100 for BinomialExtensionField<F, D>
101{
102 type ExtensionPacking = PackedBinomialExtensionField<F, F::Packing, D>;
103
104 #[inline]
105 fn is_in_basefield(&self) -> bool {
106 self.value[1..].iter().all(F::is_zero)
107 }
108
109 #[inline]
110 fn as_base(&self) -> Option<F> {
111 <Self as ExtensionField<F>>::is_in_basefield(self).then(|| self.value[0])
112 }
113}
114
115impl<F: BinomiallyExtendable<D>, const D: usize> HasFrobenius<F> for BinomialExtensionField<F, D> {
116 #[inline]
118 fn frobenius(&self) -> Self {
119 let mut res = Self::ZERO;
121 for (i, z) in F::DTH_ROOT.powers().take(D).enumerate() {
122 res.value[i] = self.value[i] * z;
123 }
124
125 res
126 }
127
128 #[inline]
133 fn repeated_frobenius(&self, count: usize) -> Self {
134 if count == 0 {
135 return *self;
136 } else if count >= D {
137 return self.repeated_frobenius(count % D);
140 }
141
142 let z0 = F::DTH_ROOT.exp_u64(count as u64);
144
145 let mut res = Self::ZERO;
146 for (i, z) in z0.powers().take(D).enumerate() {
147 res.value[i] = self.value[i] * z;
148 }
149
150 res
151 }
152
153 #[inline]
159 fn pseudo_inv(&self) -> Self {
160 let mut prod_conj = self.frobenius();
174 for _ in 2..D {
175 prod_conj = (prod_conj * *self).frobenius();
176 }
177
178 let a = self.value;
181 let b = prod_conj.value;
182 let mut w_coeff = F::ZERO;
183 for i in 1..D {
188 w_coeff += a[i] * b[D - i];
189 }
190 let norm = F::dot_product(&[a[0], F::W], &[b[0], w_coeff]);
191 debug_assert_eq!(Self::from(norm), *self * prod_conj);
192
193 prod_conj * norm.inverse()
194 }
195}
196
197impl<F, A, const D: usize> PrimeCharacteristicRing for BinomialExtensionField<F, D, A>
198where
199 F: BinomiallyExtendable<D>,
200 A: BinomiallyExtendableAlgebra<F, D>,
201{
202 type PrimeSubfield = <A as PrimeCharacteristicRing>::PrimeSubfield;
203
204 const ZERO: Self = Self::new([A::ZERO; D]);
205
206 const ONE: Self = Self::new(field_to_array(A::ONE));
207
208 const TWO: Self = Self::new(field_to_array(A::TWO));
209
210 const NEG_ONE: Self = Self::new(field_to_array(A::NEG_ONE));
211
212 #[inline]
213 fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
214 <A as PrimeCharacteristicRing>::from_prime_subfield(f).into()
215 }
216
217 #[inline]
218 fn halve(&self) -> Self {
219 Self::new(self.value.clone().map(|x| x.halve()))
220 }
221
222 #[inline(always)]
223 fn square(&self) -> Self {
224 let mut res = Self::default();
225 let w = F::W;
226 binomial_square(&self.value, &mut res.value, w);
227 res
228 }
229
230 #[inline]
231 fn mul_2exp_u64(&self, exp: u64) -> Self {
232 Self::new(self.value.clone().map(|x| x.mul_2exp_u64(exp)))
235 }
236
237 #[inline]
238 fn div_2exp_u64(&self, exp: u64) -> Self {
239 Self::new(self.value.clone().map(|x| x.div_2exp_u64(exp)))
242 }
243
244 #[inline]
245 fn zero_vec(len: usize) -> Vec<Self> {
246 unsafe { reconstitute_from_base(F::zero_vec(len * D)) }
248 }
249}
250
251impl<F: BinomiallyExtendable<D>, const D: usize> Algebra<F> for BinomialExtensionField<F, D> {}
252
253impl<F: BinomiallyExtendable<D>, const D: usize> RawDataSerializable
254 for BinomialExtensionField<F, D>
255{
256 const NUM_BYTES: usize = F::NUM_BYTES * D;
257
258 #[inline]
259 fn into_bytes(self) -> impl IntoIterator<Item = u8> {
260 self.value.into_iter().flat_map(|x| x.into_bytes())
261 }
262
263 #[inline]
264 fn into_byte_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u8> {
265 F::into_byte_stream(input.into_iter().flat_map(|x| x.value))
266 }
267
268 #[inline]
269 fn into_u32_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u32> {
270 F::into_u32_stream(input.into_iter().flat_map(|x| x.value))
271 }
272
273 #[inline]
274 fn into_u64_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u64> {
275 F::into_u64_stream(input.into_iter().flat_map(|x| x.value))
276 }
277
278 #[inline]
279 fn into_parallel_byte_streams<const N: usize>(
280 input: impl IntoIterator<Item = [Self; N]>,
281 ) -> impl IntoIterator<Item = [u8; N]> {
282 F::into_parallel_byte_streams(
283 input
284 .into_iter()
285 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
286 )
287 }
288
289 #[inline]
290 fn into_parallel_u32_streams<const N: usize>(
291 input: impl IntoIterator<Item = [Self; N]>,
292 ) -> impl IntoIterator<Item = [u32; N]> {
293 F::into_parallel_u32_streams(
294 input
295 .into_iter()
296 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
297 )
298 }
299
300 #[inline]
301 fn into_parallel_u64_streams<const N: usize>(
302 input: impl IntoIterator<Item = [Self; N]>,
303 ) -> impl IntoIterator<Item = [u64; N]> {
304 F::into_parallel_u64_streams(
305 input
306 .into_iter()
307 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
308 )
309 }
310}
311
312impl<F: BinomiallyExtendable<D>, const D: usize> Field for BinomialExtensionField<F, D> {
313 type Packing = Self;
314
315 const GENERATOR: Self = Self::new(F::EXT_GENERATOR);
316
317 fn try_inverse(&self) -> Option<Self> {
318 if self.is_zero() {
319 return None;
320 }
321
322 let mut res = Self::default();
323
324 match D {
325 2 => quadratic_inv(&self.value, &mut res.value, F::W),
326 3 => cubic_inv(&self.value, &mut res.value, F::W),
327 4 => quartic_inv(&self.value, &mut res.value, F::W),
328 5 => res = quintic_inv(self),
329 8 => octic_inv(&self.value, &mut res.value, F::W),
330 _ => res = self.pseudo_inv(),
331 }
332
333 Some(res)
334 }
335
336 #[inline]
337 fn add_slices(slice_1: &mut [Self], slice_2: &[Self]) {
338 unsafe {
342 let base_slice_1 = as_base_slice_mut(slice_1);
343 let base_slice_2 = as_base_slice(slice_2);
344
345 F::add_slices(base_slice_1, base_slice_2);
346 }
347 }
348
349 #[inline]
350 fn order() -> BigUint {
351 F::order().pow(D as u32)
352 }
353}
354
355impl<F, const D: usize> Display for BinomialExtensionField<F, D>
356where
357 F: BinomiallyExtendable<D>,
358{
359 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
360 if self.is_zero() {
361 write!(f, "0")
362 } else {
363 let str = self
364 .value
365 .iter()
366 .enumerate()
367 .filter(|(_, x)| !x.is_zero())
368 .map(|(i, x)| match (i, x.is_one()) {
369 (0, _) => format!("{x}"),
370 (1, true) => "X".to_string(),
371 (1, false) => format!("{x} X"),
372 (_, true) => format!("X^{i}"),
373 (_, false) => format!("{x} X^{i}"),
374 })
375 .join(" + ");
376 write!(f, "{str}")
377 }
378 }
379}
380
381impl<F, A, const D: usize> Neg for BinomialExtensionField<F, D, A>
382where
383 F: BinomiallyExtendable<D>,
384 A: Algebra<F>,
385{
386 type Output = Self;
387
388 #[inline]
389 fn neg(self) -> Self {
390 Self::new(self.value.map(A::neg))
391 }
392}
393
394impl<F, A, const D: usize> Add for BinomialExtensionField<F, D, A>
395where
396 F: BinomiallyExtendable<D>,
397 A: BinomiallyExtendableAlgebra<F, D>,
398{
399 type Output = Self;
400
401 #[inline]
402 fn add(self, rhs: Self) -> Self {
403 let value = A::binomial_add(&self.value, &rhs.value);
404 Self::new(value)
405 }
406}
407
408impl<F, A, const D: usize> Add<A> for BinomialExtensionField<F, D, A>
409where
410 F: BinomiallyExtendable<D>,
411 A: Algebra<F>,
412{
413 type Output = Self;
414
415 #[inline]
416 fn add(mut self, rhs: A) -> Self {
417 self.value[0] += rhs;
418 self
419 }
420}
421
422impl<F, A, const D: usize> AddAssign for BinomialExtensionField<F, D, A>
423where
424 F: BinomiallyExtendable<D>,
425 A: Algebra<F>,
426{
427 #[inline]
428 fn add_assign(&mut self, rhs: Self) {
429 for i in 0..D {
430 self.value[i] += rhs.value[i].clone();
431 }
432 }
433}
434
435impl<F, A, const D: usize> AddAssign<A> for BinomialExtensionField<F, D, A>
436where
437 F: BinomiallyExtendable<D>,
438 A: Algebra<F>,
439{
440 #[inline]
441 fn add_assign(&mut self, rhs: A) {
442 self.value[0] += rhs;
443 }
444}
445
446impl<F, A, const D: usize> Sum for BinomialExtensionField<F, D, A>
447where
448 F: BinomiallyExtendable<D>,
449 A: BinomiallyExtendableAlgebra<F, D>,
450{
451 #[inline]
452 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
453 iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO)
454 }
455}
456
457impl<F, A, const D: usize> Sub for BinomialExtensionField<F, D, A>
458where
459 F: BinomiallyExtendable<D>,
460 A: BinomiallyExtendableAlgebra<F, D>,
461{
462 type Output = Self;
463
464 #[inline]
465 fn sub(self, rhs: Self) -> Self {
466 let value = A::binomial_sub(&self.value, &rhs.value);
467 Self::new(value)
468 }
469}
470
471impl<F, A, const D: usize> Sub<A> for BinomialExtensionField<F, D, A>
472where
473 F: BinomiallyExtendable<D>,
474 A: Algebra<F>,
475{
476 type Output = Self;
477
478 #[inline]
479 fn sub(self, rhs: A) -> Self {
480 let mut res = self.value;
481 res[0] -= rhs;
482 Self::new(res)
483 }
484}
485
486impl<F, A, const D: usize> SubAssign for BinomialExtensionField<F, D, A>
487where
488 F: BinomiallyExtendable<D>,
489 A: Algebra<F>,
490{
491 #[inline]
492 fn sub_assign(&mut self, rhs: Self) {
493 for i in 0..D {
494 self.value[i] -= rhs.value[i].clone();
495 }
496 }
497}
498
499impl<F, A, const D: usize> SubAssign<A> for BinomialExtensionField<F, D, A>
500where
501 F: BinomiallyExtendable<D>,
502 A: Algebra<F>,
503{
504 #[inline]
505 fn sub_assign(&mut self, rhs: A) {
506 self.value[0] -= rhs;
507 }
508}
509
510impl<F, A, const D: usize> Mul for BinomialExtensionField<F, D, A>
511where
512 F: BinomiallyExtendable<D>,
513 A: BinomiallyExtendableAlgebra<F, D>,
514{
515 type Output = Self;
516
517 #[inline]
518 fn mul(self, rhs: Self) -> Self {
519 let a = self.value;
520 let b = rhs.value;
521 let mut res = Self::default();
522 let w = F::W;
523
524 A::binomial_mul(&a, &b, &mut res.value, w);
525
526 res
527 }
528}
529
530impl<F, A, const D: usize> Mul<A> for BinomialExtensionField<F, D, A>
531where
532 F: BinomiallyExtendable<D>,
533 A: BinomiallyExtendableAlgebra<F, D>,
534{
535 type Output = Self;
536
537 #[inline]
538 fn mul(self, rhs: A) -> Self {
539 Self::new(A::binomial_base_mul(self.value, rhs))
540 }
541}
542
543impl<F, A, const D: usize> MulAssign for BinomialExtensionField<F, D, A>
544where
545 F: BinomiallyExtendable<D>,
546 A: BinomiallyExtendableAlgebra<F, D>,
547{
548 #[inline]
549 fn mul_assign(&mut self, rhs: Self) {
550 *self = self.clone() * rhs;
551 }
552}
553
554impl<F, A, const D: usize> MulAssign<A> for BinomialExtensionField<F, D, A>
555where
556 F: BinomiallyExtendable<D>,
557 A: BinomiallyExtendableAlgebra<F, D>,
558{
559 #[inline]
560 fn mul_assign(&mut self, rhs: A) {
561 *self = self.clone() * rhs;
562 }
563}
564
565impl<F, A, const D: usize> Product for BinomialExtensionField<F, D, A>
566where
567 F: BinomiallyExtendable<D>,
568 A: BinomiallyExtendableAlgebra<F, D>,
569{
570 #[inline]
571 fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
572 iter.reduce(|acc, x| acc * x).unwrap_or(Self::ONE)
573 }
574}
575
576impl<F, const D: usize> Div for BinomialExtensionField<F, D>
577where
578 F: BinomiallyExtendable<D>,
579{
580 type Output = Self;
581
582 #[allow(clippy::suspicious_arithmetic_impl)]
583 #[inline]
584 fn div(self, rhs: Self) -> Self::Output {
585 self * rhs.inverse()
586 }
587}
588
589impl<F, const D: usize> DivAssign for BinomialExtensionField<F, D>
590where
591 F: BinomiallyExtendable<D>,
592{
593 #[inline]
594 fn div_assign(&mut self, rhs: Self) {
595 *self = *self / rhs;
596 }
597}
598
599impl<F: BinomiallyExtendable<D>, const D: usize> Distribution<BinomialExtensionField<F, D>>
600 for StandardUniform
601where
602 Self: Distribution<F>,
603{
604 #[inline]
605 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> BinomialExtensionField<F, D> {
606 BinomialExtensionField::new(array::from_fn(|_| self.sample(rng)))
607 }
608}
609
610impl<F: Field + HasTwoAdicBinomialExtension<D>, const D: usize> TwoAdicField
611 for BinomialExtensionField<F, D>
612{
613 const TWO_ADICITY: usize = F::EXT_TWO_ADICITY;
614
615 #[inline]
616 fn two_adic_generator(bits: usize) -> Self {
617 Self::new(F::ext_two_adic_generator(bits))
618 }
619}
620
621#[inline]
623pub fn vector_add<R: PrimeCharacteristicRing + Add<R2, Output = R>, R2: Clone, const D: usize>(
624 a: &[R; D],
625 b: &[R2; D],
626) -> [R; D] {
627 array::from_fn(|i| a[i].clone() + b[i].clone())
628}
629
630#[inline]
632pub(crate) fn vector_sub<
633 R: PrimeCharacteristicRing + Sub<R2, Output = R>,
634 R2: Clone,
635 const D: usize,
636>(
637 a: &[R; D],
638 b: &[R2; D],
639) -> [R; D] {
640 array::from_fn(|i| a[i].clone() - b[i].clone())
641}
642
643#[inline]
645pub(super) fn binomial_mul<
646 F: Field,
647 R: Algebra<F> + Algebra<R2>,
648 R2: Algebra<F>,
649 const D: usize,
650>(
651 a: &[R; D],
652 b: &[R2; D],
653 res: &mut [R; D],
654 w: F,
655) {
656 match D {
657 2 => quadratic_mul(a, b, res, w),
658 3 => cubic_mul(a, b, res, w),
659 4 => quartic_mul(a, b, res, w),
660 5 => quintic_mul(a, b, res, w),
661 8 => octic_mul(a, b, res, w),
662 _ =>
663 {
664 #[allow(clippy::needless_range_loop)]
665 for i in 0..D {
666 for j in 0..D {
667 if i + j >= D {
668 res[i + j - D] += a[i].clone() * w * b[j].clone();
669 } else {
670 res[i + j] += a[i].clone() * b[j].clone();
671 }
672 }
673 }
674 }
675 }
676}
677
678#[inline]
682pub(super) fn binomial_square<F: Field, R: Algebra<F>, const D: usize>(
683 a: &[R; D],
684 res: &mut [R; D],
685 w: F,
686) {
687 match D {
688 2 => {
689 let a1_w = a[1].clone() * w;
690 res[0] = R::dot_product(a[..].try_into().unwrap(), &[a[0].clone(), a1_w]);
691 res[1] = a[0].clone() * a[1].double();
692 }
693 3 => cubic_square(a, res, w),
694 4 => quartic_square(a, res, w),
695 5 => quintic_square(a, res, w),
696 8 => octic_square(a, res, w),
697 _ => binomial_mul::<F, R, R, D>(a, a, res, w),
698 }
699}
700
701#[inline]
715fn quadratic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
716where
717 F: Field,
718 R: Algebra<F> + Algebra<R2>,
719 R2: Algebra<F>,
720{
721 let b1_w = b[1].clone() * w;
722
723 res[0] = R::dot_product(
725 a[..].try_into().unwrap(),
726 &[b[0].clone().into(), b1_w.into()],
727 );
728
729 res[1] = R::dot_product(
731 &[a[0].clone(), a[1].clone()],
732 &[b[1].clone().into(), b[0].clone().into()],
733 );
734}
735
736#[inline]
738fn quadratic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
739 assert_eq!(D, 2);
740 let neg_a1 = -a[1];
741 let scalar = F::dot_product(&[a[0], neg_a1], &[a[0], w * a[1]]).inverse();
742 res[0] = a[0] * scalar;
743 res[1] = neg_a1 * scalar;
744}
745
746#[inline]
748fn cubic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
749 assert_eq!(D, 3);
750 let a0_square = a[0].square();
751 let a1_square = a[1].square();
752 let a2_w = w * a[2];
753 let a0_a1 = a[0] * a[1];
754
755 let scalar = (a0_square * a[0] + w * a[1] * a1_square + a2_w.square() * a[2]
757 - (F::ONE + F::TWO) * a2_w * a0_a1)
758 .inverse();
759
760 res[0] = scalar * (a0_square - a[1] * a2_w);
762 res[1] = scalar * (a2_w * a[2] - a0_a1);
763 res[2] = scalar * (a1_square - a[0] * a[2]);
764}
765
766#[inline]
768fn cubic_mul<F: Field, R: Algebra<F> + Algebra<R2>, R2: Algebra<F>, const D: usize>(
769 a: &[R; D],
770 b: &[R2; D],
771 res: &mut [R; D],
772 w: F,
773) {
774 assert_eq!(D, 3);
775 let a0_b0 = a[0].clone() * b[0].clone();
779 let a1_b1 = a[1].clone() * b[1].clone();
780 let a2_b2 = a[2].clone() * b[2].clone();
781
782 res[0] = a0_b0.clone()
783 + ((a[1].clone() + a[2].clone()) * (b[1].clone() + b[2].clone())
784 - a1_b1.clone()
785 - a2_b2.clone())
786 * w;
787 res[1] = (a[0].clone() + a[1].clone()) * (b[0].clone() + b[1].clone())
788 - a0_b0.clone()
789 - a1_b1.clone()
790 + a2_b2.clone() * w;
791 res[2] = (a[0].clone() + a[2].clone()) * (b[0].clone() + b[2].clone()) - a0_b0 - a2_b2 + a1_b1;
792}
793
794#[inline]
796fn cubic_square<F: Field, R: Algebra<F>, const D: usize>(a: &[R; D], res: &mut [R; D], w: F) {
797 assert_eq!(D, 3);
798
799 let w_a2 = a[2].clone() * w;
800
801 res[0] = a[0].square() + (a[1].clone() * w_a2.clone()).double();
802 res[1] = w_a2 * a[2].clone() + (a[0].clone() * a[1].clone()).double();
803 res[2] = a[1].square() + (a[0].clone() * a[2].clone()).double();
804}
805
806#[inline]
811pub fn quartic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
812where
813 F: Field,
814 R: Algebra<F> + Algebra<R2>,
815 R2: Algebra<F>,
816{
817 assert_eq!(D, 4);
818 let b_r_rev: [R; 5] = [
819 b[3].clone().into(),
820 b[2].clone().into(),
821 b[1].clone().into(),
822 b[0].clone().into(),
823 w.into(),
824 ];
825
826 let w_coeff_0 =
828 R::dot_product::<3>(a[1..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
829 res[0] = R::dot_product(&[a[0].clone(), w_coeff_0], b_r_rev[3..].try_into().unwrap());
830
831 let w_coeff_1 =
833 R::dot_product::<2>(a[2..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
834 res[1] = R::dot_product(
835 &[a[0].clone(), a[1].clone(), w_coeff_1],
836 b_r_rev[2..].try_into().unwrap(),
837 );
838
839 let b3_w = b[3].clone() * w;
841 res[2] = R::dot_product::<4>(
842 a[..4].try_into().unwrap(),
843 &[
844 b_r_rev[1].clone(),
845 b_r_rev[2].clone(),
846 b_r_rev[3].clone(),
847 b3_w.into(),
848 ],
849 );
850
851 res[3] = R::dot_product::<4>(a[..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
853}
854
855#[inline]
857fn quartic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
858 assert_eq!(D, 4);
859
860 let neg_a1 = -a[1];
872 let a3_w = a[3] * w;
873 let norm_0 = F::dot_product(&[a[0], a[2], neg_a1.double()], &[a[0], a[2] * w, a3_w]);
874 let norm_1 = F::dot_product(&[a[0], a[1], -a[3]], &[a[2].double(), neg_a1, a3_w]);
875
876 let mut inv = [F::ZERO; 2];
878 quadratic_inv(&[norm_0, norm_1], &mut inv, w);
879
880 let mut out_evn = [F::ZERO; 2];
885 let mut out_odd = [F::ZERO; 2];
886 quadratic_mul(&[a[0], a[2]], &inv, &mut out_evn, w);
887 quadratic_mul(&[a[1], a[3]], &inv, &mut out_odd, w);
888
889 res[0] = out_evn[0];
890 res[1] = -out_odd[0];
891 res[2] = out_evn[1];
892 res[3] = -out_odd[1];
893}
894
895#[inline]
900fn quartic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
901where
902 F: Field,
903 R: Algebra<F>,
904{
905 assert_eq!(D, 4);
906
907 let two_a0 = a[0].double();
908 let two_a1 = a[1].double();
909 let two_a2 = a[2].double();
910 let a2_w = a[2].clone() * w;
911 let a3_w = a[3].clone() * w;
912
913 res[0] = R::dot_product(
915 &[a[0].clone(), a2_w, two_a1],
916 &[a[0].clone(), a[2].clone(), a3_w.clone()],
917 );
918
919 res[1] = R::dot_product(
921 &[two_a0.clone(), two_a2.clone()],
922 &[a[1].clone(), a3_w.clone()],
923 );
924
925 res[2] = R::dot_product(
927 &[a[1].clone(), a3_w, two_a0.clone()],
928 &[a[1].clone(), a[3].clone(), a[2].clone()],
929 );
930
931 res[3] = R::dot_product(&[two_a0, two_a2], &[a[3].clone(), a[1].clone()]);
933}
934
935pub fn quintic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
940where
941 F: Field,
942 R: Algebra<F> + Algebra<R2>,
943 R2: Algebra<F>,
944{
945 assert_eq!(D, 5);
946 let b_r_rev: [R; 6] = [
947 b[4].clone().into(),
948 b[3].clone().into(),
949 b[2].clone().into(),
950 b[1].clone().into(),
951 b[0].clone().into(),
952 w.into(),
953 ];
954
955 let w_coeff_0 =
957 R::dot_product::<4>(a[1..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
958 res[0] = R::dot_product(&[a[0].clone(), w_coeff_0], b_r_rev[4..].try_into().unwrap());
959
960 let w_coeff_1 =
962 R::dot_product::<3>(a[2..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
963 res[1] = R::dot_product(
964 &[a[0].clone(), a[1].clone(), w_coeff_1],
965 b_r_rev[3..].try_into().unwrap(),
966 );
967
968 let w_coeff_2 =
970 R::dot_product::<2>(a[3..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
971 res[2] = R::dot_product(
972 &[a[0].clone(), a[1].clone(), a[2].clone(), w_coeff_2],
973 b_r_rev[2..].try_into().unwrap(),
974 );
975
976 let b4_w = b[4].clone() * w;
978 res[3] = R::dot_product::<5>(
979 a[..5].try_into().unwrap(),
980 &[
981 b_r_rev[1].clone(),
982 b_r_rev[2].clone(),
983 b_r_rev[3].clone(),
984 b_r_rev[4].clone(),
985 b4_w.into(),
986 ],
987 );
988
989 res[4] = R::dot_product::<5>(a[..].try_into().unwrap(), b_r_rev[..5].try_into().unwrap());
991}
992
993#[inline]
998fn quintic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
999where
1000 F: Field,
1001 R: Algebra<F>,
1002{
1003 assert_eq!(D, 5);
1004
1005 let two_a0 = a[0].double();
1006 let two_a1 = a[1].double();
1007 let two_a2 = a[2].double();
1008 let two_a3 = a[3].double();
1009 let w_a3 = a[3].clone() * w;
1010 let w_a4 = a[4].clone() * w;
1011
1012 res[0] = R::dot_product(
1014 &[a[0].clone(), w_a4.clone(), w_a3.clone()],
1015 &[a[0].clone(), two_a1.clone(), two_a2.clone()],
1016 );
1017
1018 res[1] = R::dot_product(
1020 &[w_a3, two_a0.clone(), w_a4.clone()],
1021 &[a[3].clone(), a[1].clone(), two_a2],
1022 );
1023
1024 res[2] = R::dot_product(
1026 &[a[1].clone(), two_a0.clone(), w_a4.clone()],
1027 &[a[1].clone(), a[2].clone(), two_a3],
1028 );
1029
1030 res[3] = R::dot_product(
1032 &[w_a4, two_a0.clone(), two_a1.clone()],
1033 &[a[4].clone(), a[3].clone(), a[2].clone()],
1034 );
1035
1036 res[4] = R::dot_product(
1038 &[a[2].clone(), two_a0, two_a1],
1039 &[a[2].clone(), a[4].clone(), a[3].clone()],
1040 );
1041}
1042
1043#[inline]
1048fn octic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
1049where
1050 F: Field,
1051 R: Algebra<F>,
1052{
1053 assert_eq!(D, 8);
1054
1055 let a0_2 = a[0].double();
1056 let a1_2 = a[1].double();
1057 let a2_2 = a[2].double();
1058 let a3_2 = a[3].double();
1059 let w_a4 = a[4].clone() * w;
1060 let w_a5 = a[5].clone() * w;
1061 let w_a6 = a[6].clone() * w;
1062 let w_a7 = a[7].clone() * w;
1063 let w_a5_2 = w_a5.double();
1064 let w_a6_2 = w_a6.double();
1065 let w_a7_2 = w_a7.double();
1066
1067 res[0] = R::dot_product(
1069 &[
1070 a[0].clone(),
1071 a[1].clone(),
1072 a[2].clone(),
1073 a[3].clone(),
1074 a[4].clone(),
1075 ],
1076 &[
1077 a[0].clone(),
1078 w_a7_2.clone(),
1079 w_a6_2.clone(),
1080 w_a5_2.clone(),
1081 w_a4,
1082 ],
1083 );
1084
1085 res[1] = R::dot_product(
1087 &[a0_2.clone(), a[2].clone(), a[3].clone(), a[4].clone()],
1088 &[a[1].clone(), w_a7_2.clone(), w_a6_2.clone(), w_a5_2],
1089 );
1090
1091 res[2] = R::dot_product(
1093 &[
1094 a0_2.clone(),
1095 a[1].clone(),
1096 a[3].clone(),
1097 a[4].clone(),
1098 a[5].clone(),
1099 ],
1100 &[
1101 a[2].clone(),
1102 a[1].clone(),
1103 w_a7_2.clone(),
1104 w_a6_2.clone(),
1105 w_a5,
1106 ],
1107 );
1108
1109 res[3] = R::dot_product(
1111 &[a0_2.clone(), a1_2.clone(), a[4].clone(), a[5].clone()],
1112 &[a[3].clone(), a[2].clone(), w_a7_2.clone(), w_a6_2],
1113 );
1114
1115 res[4] = R::dot_product(
1117 &[
1118 a0_2.clone(),
1119 a1_2.clone(),
1120 a[2].clone(),
1121 a[5].clone(),
1122 a[6].clone(),
1123 ],
1124 &[
1125 a[4].clone(),
1126 a[3].clone(),
1127 a[2].clone(),
1128 w_a7_2.clone(),
1129 w_a6,
1130 ],
1131 );
1132
1133 res[5] = R::dot_product(
1135 &[a0_2.clone(), a1_2.clone(), a2_2.clone(), a[6].clone()],
1136 &[a[5].clone(), a[4].clone(), a[3].clone(), w_a7_2],
1137 );
1138
1139 res[6] = R::dot_product(
1141 &[
1142 a0_2.clone(),
1143 a1_2.clone(),
1144 a2_2.clone(),
1145 a[3].clone(),
1146 a[7].clone(),
1147 ],
1148 &[a[6].clone(), a[5].clone(), a[4].clone(), a[3].clone(), w_a7],
1149 );
1150
1151 res[7] = R::dot_product(
1153 &[a0_2, a1_2, a2_2, a3_2],
1154 &[a[7].clone(), a[6].clone(), a[5].clone(), a[4].clone()],
1155 );
1156}
1157
1158#[inline]
1160fn quintic_inv<F: BinomiallyExtendable<D>, const D: usize>(
1161 a: &BinomialExtensionField<F, D>,
1162) -> BinomialExtensionField<F, D> {
1163 let a_exp_q = a.frobenius();
1165 let a_exp_q_plus_q_sq = (*a * a_exp_q).frobenius();
1166 let prod_conj = a_exp_q_plus_q_sq * a_exp_q_plus_q_sq.repeated_frobenius(2);
1167
1168 let a_vals = a.value;
1171 let mut b = prod_conj.value;
1172 b.reverse();
1173
1174 let w_coeff = F::dot_product::<4>(a.value[1..].try_into().unwrap(), b[..4].try_into().unwrap());
1175 let norm = F::dot_product::<2>(&[a_vals[0], F::W], &[b[4], w_coeff]);
1176 debug_assert_eq!(BinomialExtensionField::<F, D>::from(norm), *a * prod_conj);
1177
1178 prod_conj * norm.inverse()
1179}
1180
1181#[inline]
1190fn compute_coefficient<
1191 F,
1192 R,
1193 const D: usize,
1194 const D_PLUS_1: usize,
1195 const N: usize,
1196 const D_PLUS_1_MIN_N: usize,
1197>(
1198 a: &[R; D],
1199 b_rev: &[R; D_PLUS_1],
1200) -> R
1201where
1202 F: Field,
1203 R: Algebra<F>,
1204{
1205 let w_coeff = R::dot_product::<N>(
1206 a[(D - N)..].try_into().unwrap(),
1207 b_rev[..N].try_into().unwrap(),
1208 );
1209 let mut scratch: [R; D_PLUS_1_MIN_N] = array::from_fn(|i| a[i].clone());
1210 scratch[D_PLUS_1_MIN_N - 1] = w_coeff;
1211 R::dot_product(&scratch, b_rev[N..].try_into().unwrap())
1212}
1213
1214#[inline]
1219pub fn octic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
1220where
1221 F: Field,
1222 R: Algebra<F> + Algebra<R2>,
1223 R2: Algebra<F>,
1224{
1225 assert_eq!(D, 8);
1226 let a: &[R; 8] = a[..].try_into().unwrap();
1227 let mut b_r_rev: [R; 9] = [
1228 b[7].clone().into(),
1229 b[6].clone().into(),
1230 b[5].clone().into(),
1231 b[4].clone().into(),
1232 b[3].clone().into(),
1233 b[2].clone().into(),
1234 b[1].clone().into(),
1235 b[0].clone().into(),
1236 w.into(),
1237 ];
1238
1239 res[0] = compute_coefficient::<F, R, 8, 9, 7, 2>(a, &b_r_rev);
1241
1242 res[1] = compute_coefficient::<F, R, 8, 9, 6, 3>(a, &b_r_rev);
1244
1245 res[2] = compute_coefficient::<F, R, 8, 9, 5, 4>(a, &b_r_rev);
1247
1248 res[3] = compute_coefficient::<F, R, 8, 9, 4, 5>(a, &b_r_rev);
1250
1251 res[4] = compute_coefficient::<F, R, 8, 9, 3, 6>(a, &b_r_rev);
1253
1254 res[5] = compute_coefficient::<F, R, 8, 9, 2, 7>(a, &b_r_rev);
1256
1257 b_r_rev[8] *= b[7].clone();
1259 res[6] = R::dot_product::<8>(a, b_r_rev[1..].try_into().unwrap());
1260
1261 res[7] = R::dot_product::<8>(a, b_r_rev[..8].try_into().unwrap());
1263}
1264
1265#[inline]
1267fn octic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
1268 assert_eq!(D, 8);
1269
1270 let evns = [a[0], a[2], a[4], a[6]];
1285 let odds = [a[1], a[3], a[5], a[7]];
1286 let mut evns_sq = [F::ZERO; 4];
1287 let mut odds_sq = [F::ZERO; 4];
1288 quartic_square(&evns, &mut evns_sq, w);
1289 quartic_square(&odds, &mut odds_sq, w);
1290 let norm = [
1292 evns_sq[0] - w * odds_sq[3],
1293 evns_sq[1] - odds_sq[0],
1294 evns_sq[2] - odds_sq[1],
1295 evns_sq[3] - odds_sq[2],
1296 ];
1297
1298 let mut norm_inv = [F::ZERO; 4];
1300 quartic_inv(&norm, &mut norm_inv, w);
1301
1302 let mut out_evn = [F::ZERO; 4];
1308 let mut out_odd = [F::ZERO; 4];
1309 quartic_mul(&evns, &norm_inv, &mut out_evn, w);
1310 quartic_mul(&odds, &norm_inv, &mut out_odd, w);
1311
1312 res[0] = out_evn[0];
1313 res[1] = -out_odd[0];
1314 res[2] = out_evn[1];
1315 res[3] = -out_odd[1];
1316 res[4] = out_evn[2];
1317 res[5] = -out_odd[2];
1318 res[6] = out_evn[3];
1319 res[7] = -out_odd[3];
1320}