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, Dup, 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 #[inline]
43 pub const fn new(value: [A; D]) -> Self {
44 const { assert!(D > 1) }
45 Self {
46 value,
47 _phantom: PhantomData,
48 }
49 }
50}
51
52impl<F: Copy, const D: usize> BinomialExtensionField<F, D, F> {
53 #[inline]
60 pub const fn new_array<const N: usize>(input: [[F; D]; N]) -> [Self; N] {
61 const { assert!(N > 0) }
62 let mut output = [Self::new(input[0]); N];
63 let mut i = 1;
64 while i < N {
65 output[i] = Self::new(input[i]);
66 i += 1;
67 }
68 output
69 }
70}
71
72impl<F: Field, A: Algebra<F>, const D: usize> Default for BinomialExtensionField<F, D, A> {
73 fn default() -> Self {
74 Self::new(array::from_fn(|_| A::ZERO))
75 }
76}
77
78impl<F: Field, A: Algebra<F>, const D: usize> From<A> for BinomialExtensionField<F, D, A> {
79 fn from(x: A) -> Self {
80 Self::new(field_to_array(x))
81 }
82}
83
84impl<F, A, const D: usize> From<[A; D]> for BinomialExtensionField<F, D, A> {
85 #[inline]
86 fn from(x: [A; D]) -> Self {
87 Self {
88 value: x,
89 _phantom: PhantomData,
90 }
91 }
92}
93
94impl<F: BinomiallyExtendable<D>, const D: usize> Packable for BinomialExtensionField<F, D> {}
95
96impl<F: BinomiallyExtendable<D>, A: Algebra<F>, const D: usize> BasedVectorSpace<A>
97 for BinomialExtensionField<F, D, A>
98{
99 const DIMENSION: usize = D;
100
101 #[inline]
102 fn as_basis_coefficients_slice(&self) -> &[A] {
103 &self.value
104 }
105
106 #[inline]
107 fn from_basis_coefficients_fn<Fn: FnMut(usize) -> A>(f: Fn) -> Self {
108 Self::new(array::from_fn(f))
109 }
110
111 #[inline]
112 fn from_basis_coefficients_iter<I: ExactSizeIterator<Item = A>>(mut iter: I) -> Option<Self> {
113 (iter.len() == D).then(|| Self::new(array::from_fn(|_| iter.next().unwrap()))) }
115
116 #[inline]
117 fn flatten_to_base(vec: Vec<Self>) -> Vec<A> {
118 unsafe {
119 flatten_to_base::<A, Self>(vec)
122 }
123 }
124
125 #[inline]
126 fn reconstitute_from_base(vec: Vec<A>) -> Vec<Self> {
127 unsafe {
128 reconstitute_from_base::<A, Self>(vec)
131 }
132 }
133}
134
135impl<F: BinomiallyExtendable<D>, const D: usize> ExtensionField<F>
136 for BinomialExtensionField<F, D>
137{
138 type ExtensionPacking = PackedBinomialExtensionField<F, F::Packing, D>;
139
140 #[inline]
141 fn is_in_basefield(&self) -> bool {
142 self.value[1..].iter().all(F::is_zero)
143 }
144
145 #[inline]
146 fn as_base(&self) -> Option<F> {
147 <Self as ExtensionField<F>>::is_in_basefield(self).then(|| self.value[0])
148 }
149}
150
151impl<F: BinomiallyExtendable<D>, const D: usize> HasFrobenius<F> for BinomialExtensionField<F, D> {
152 #[inline]
154 fn frobenius(&self) -> Self {
155 let mut res = Self::ZERO;
157 for (i, z) in F::DTH_ROOT.powers().take(D).enumerate() {
158 res.value[i] = self.value[i] * z;
159 }
160
161 res
162 }
163
164 #[inline]
169 fn repeated_frobenius(&self, count: usize) -> Self {
170 if count == 0 {
171 return *self;
172 } else if count >= D {
173 return self.repeated_frobenius(count % D);
176 }
177
178 let z0 = F::DTH_ROOT.exp_u64(count as u64);
180
181 let mut res = Self::ZERO;
182 for (i, z) in z0.powers().take(D).enumerate() {
183 res.value[i] = self.value[i] * z;
184 }
185
186 res
187 }
188
189 #[inline]
195 fn pseudo_inv(&self) -> Self {
196 let mut prod_conj = self.frobenius();
210 for _ in 2..D {
211 prod_conj = (prod_conj * *self).frobenius();
212 }
213
214 let a = self.value;
217 let b = prod_conj.value;
218 let mut w_coeff = F::ZERO;
219 for i in 1..D {
224 w_coeff += a[i] * b[D - i];
225 }
226 let norm = F::dot_product(&[a[0], F::W], &[b[0], w_coeff]);
227 debug_assert_eq!(Self::from(norm), *self * prod_conj);
228
229 prod_conj * norm.inverse()
230 }
231}
232
233impl<F, A, const D: usize> PrimeCharacteristicRing for BinomialExtensionField<F, D, A>
234where
235 F: BinomiallyExtendable<D>,
236 A: BinomiallyExtendableAlgebra<F, D> + Copy,
237{
238 type PrimeSubfield = <A as PrimeCharacteristicRing>::PrimeSubfield;
239
240 const ZERO: Self = Self::new([A::ZERO; D]);
241
242 const ONE: Self = Self::new(field_to_array(A::ONE));
243
244 const TWO: Self = Self::new(field_to_array(A::TWO));
245
246 const NEG_ONE: Self = Self::new(field_to_array(A::NEG_ONE));
247
248 #[inline]
249 fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
250 <A as PrimeCharacteristicRing>::from_prime_subfield(f).into()
251 }
252
253 #[inline]
254 fn halve(&self) -> Self {
255 Self::new(array::from_fn(|i| self.value[i].halve()))
256 }
257
258 #[inline(always)]
259 fn square(&self) -> Self {
260 let mut res = Self::default();
261 let w = F::W;
262 binomial_square(&self.value, &mut res.value, w);
263 res
264 }
265
266 #[inline]
267 fn mul_2exp_u64(&self, exp: u64) -> Self {
268 Self::new(array::from_fn(|i| self.value[i].mul_2exp_u64(exp)))
271 }
272
273 #[inline]
274 fn div_2exp_u64(&self, exp: u64) -> Self {
275 Self::new(array::from_fn(|i| self.value[i].div_2exp_u64(exp)))
278 }
279
280 #[inline]
281 fn zero_vec(len: usize) -> Vec<Self> {
282 unsafe { reconstitute_from_base(F::zero_vec(len * D)) }
284 }
285}
286
287impl<F: BinomiallyExtendable<D>, const D: usize> Algebra<F> for BinomialExtensionField<F, D> {}
288
289impl<F: BinomiallyExtendable<D>, const D: usize> RawDataSerializable
290 for BinomialExtensionField<F, D>
291{
292 const NUM_BYTES: usize = F::NUM_BYTES * D;
293
294 #[inline]
295 fn into_bytes(self) -> impl IntoIterator<Item = u8> {
296 self.value.into_iter().flat_map(|x| x.into_bytes())
297 }
298
299 #[inline]
300 fn into_byte_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u8> {
301 F::into_byte_stream(input.into_iter().flat_map(|x| x.value))
302 }
303
304 #[inline]
305 fn into_u32_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u32> {
306 F::into_u32_stream(input.into_iter().flat_map(|x| x.value))
307 }
308
309 #[inline]
310 fn into_u64_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u64> {
311 F::into_u64_stream(input.into_iter().flat_map(|x| x.value))
312 }
313
314 #[inline]
315 fn into_parallel_byte_streams<const N: usize>(
316 input: impl IntoIterator<Item = [Self; N]>,
317 ) -> impl IntoIterator<Item = [u8; N]> {
318 F::into_parallel_byte_streams(
319 input
320 .into_iter()
321 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
322 )
323 }
324
325 #[inline]
326 fn into_parallel_u32_streams<const N: usize>(
327 input: impl IntoIterator<Item = [Self; N]>,
328 ) -> impl IntoIterator<Item = [u32; N]> {
329 F::into_parallel_u32_streams(
330 input
331 .into_iter()
332 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
333 )
334 }
335
336 #[inline]
337 fn into_parallel_u64_streams<const N: usize>(
338 input: impl IntoIterator<Item = [Self; N]>,
339 ) -> impl IntoIterator<Item = [u64; N]> {
340 F::into_parallel_u64_streams(
341 input
342 .into_iter()
343 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
344 )
345 }
346}
347
348impl<F: BinomiallyExtendable<D>, const D: usize> Field for BinomialExtensionField<F, D> {
349 type Packing = Self;
350
351 const GENERATOR: Self = Self::new(F::EXT_GENERATOR);
352
353 fn try_inverse(&self) -> Option<Self> {
354 if self.is_zero() {
355 return None;
356 }
357
358 let mut res = Self::default();
359
360 match D {
361 2 => quadratic_inv(&self.value, &mut res.value, F::W),
362 3 => cubic_inv(&self.value, &mut res.value, F::W),
363 4 => quartic_inv(&self.value, &mut res.value, F::W),
364 5 => res = quintic_inv(self),
365 8 => octic_inv(&self.value, &mut res.value, F::W),
366 _ => res = self.pseudo_inv(),
367 }
368
369 Some(res)
370 }
371
372 #[inline]
373 fn add_slices(slice_1: &mut [Self], slice_2: &[Self]) {
374 unsafe {
378 let base_slice_1 = as_base_slice_mut(slice_1);
379 let base_slice_2 = as_base_slice(slice_2);
380
381 F::add_slices(base_slice_1, base_slice_2);
382 }
383 }
384
385 #[inline]
386 fn order() -> BigUint {
387 F::order().pow(D as u32)
388 }
389}
390
391impl<F, const D: usize> Display for BinomialExtensionField<F, D>
392where
393 F: BinomiallyExtendable<D>,
394{
395 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
396 if self.is_zero() {
397 write!(f, "0")
398 } else {
399 let str = self
400 .value
401 .iter()
402 .enumerate()
403 .filter(|(_, x)| !x.is_zero())
404 .map(|(i, x)| match (i, x.is_one()) {
405 (0, _) => format!("{x}"),
406 (1, true) => "X".to_string(),
407 (1, false) => format!("{x} X"),
408 (_, true) => format!("X^{i}"),
409 (_, false) => format!("{x} X^{i}"),
410 })
411 .join(" + ");
412 write!(f, "{str}")
413 }
414 }
415}
416
417impl<F, A, const D: usize> Neg for BinomialExtensionField<F, D, A>
418where
419 F: BinomiallyExtendable<D>,
420 A: Algebra<F>,
421{
422 type Output = Self;
423
424 #[inline]
425 fn neg(self) -> Self {
426 Self::new(self.value.map(A::neg))
427 }
428}
429
430impl<F, A, const D: usize> Add for BinomialExtensionField<F, D, A>
431where
432 F: BinomiallyExtendable<D>,
433 A: BinomiallyExtendableAlgebra<F, D>,
434{
435 type Output = Self;
436
437 #[inline]
438 fn add(self, rhs: Self) -> Self {
439 let value = A::binomial_add(&self.value, &rhs.value);
440 Self::new(value)
441 }
442}
443
444impl<F, A, const D: usize> Add<A> for BinomialExtensionField<F, D, A>
445where
446 F: BinomiallyExtendable<D>,
447 A: Algebra<F>,
448{
449 type Output = Self;
450
451 #[inline]
452 fn add(mut self, rhs: A) -> Self {
453 self.value[0] += rhs;
454 self
455 }
456}
457
458impl<F, A, const D: usize> AddAssign for BinomialExtensionField<F, D, A>
459where
460 F: BinomiallyExtendable<D>,
461 A: BinomiallyExtendableAlgebra<F, D>,
462{
463 #[inline]
464 fn add_assign(&mut self, rhs: Self) {
465 self.value = A::binomial_add(&self.value, &rhs.value);
466 }
467}
468
469impl<F, A, const D: usize> AddAssign<A> for BinomialExtensionField<F, D, A>
470where
471 F: BinomiallyExtendable<D>,
472 A: Algebra<F>,
473{
474 #[inline]
475 fn add_assign(&mut self, rhs: A) {
476 self.value[0] += rhs;
477 }
478}
479
480impl<F, A, const D: usize> Sum for BinomialExtensionField<F, D, A>
481where
482 F: BinomiallyExtendable<D>,
483 A: BinomiallyExtendableAlgebra<F, D> + Copy,
484{
485 #[inline]
486 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
487 iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO)
488 }
489}
490
491impl<F, A, const D: usize> Sub for BinomialExtensionField<F, D, A>
492where
493 F: BinomiallyExtendable<D>,
494 A: BinomiallyExtendableAlgebra<F, D>,
495{
496 type Output = Self;
497
498 #[inline]
499 fn sub(self, rhs: Self) -> Self {
500 let value = A::binomial_sub(&self.value, &rhs.value);
501 Self::new(value)
502 }
503}
504
505impl<F, A, const D: usize> Sub<A> for BinomialExtensionField<F, D, A>
506where
507 F: BinomiallyExtendable<D>,
508 A: Algebra<F>,
509{
510 type Output = Self;
511
512 #[inline]
513 fn sub(self, rhs: A) -> Self {
514 let mut res = self.value;
515 res[0] -= rhs;
516 Self::new(res)
517 }
518}
519
520impl<F, A, const D: usize> SubAssign for BinomialExtensionField<F, D, A>
521where
522 F: BinomiallyExtendable<D>,
523 A: BinomiallyExtendableAlgebra<F, D>,
524{
525 #[inline]
526 fn sub_assign(&mut self, rhs: Self) {
527 self.value = A::binomial_sub(&self.value, &rhs.value);
528 }
529}
530
531impl<F, A, const D: usize> SubAssign<A> for BinomialExtensionField<F, D, A>
532where
533 F: BinomiallyExtendable<D>,
534 A: Algebra<F>,
535{
536 #[inline]
537 fn sub_assign(&mut self, rhs: A) {
538 self.value[0] -= rhs;
539 }
540}
541
542impl<F, A, const D: usize> Mul for BinomialExtensionField<F, D, A>
543where
544 F: BinomiallyExtendable<D>,
545 A: BinomiallyExtendableAlgebra<F, D>,
546{
547 type Output = Self;
548
549 #[inline]
550 fn mul(self, rhs: Self) -> Self {
551 let a = self.value;
552 let b = rhs.value;
553 let mut res = Self::default();
554 let w = F::W;
555
556 A::binomial_mul(&a, &b, &mut res.value, w);
557
558 res
559 }
560}
561
562impl<F, A, const D: usize> Mul<A> for BinomialExtensionField<F, D, A>
563where
564 F: BinomiallyExtendable<D>,
565 A: BinomiallyExtendableAlgebra<F, D>,
566{
567 type Output = Self;
568
569 #[inline]
570 fn mul(self, rhs: A) -> Self {
571 Self::new(A::binomial_base_mul(self.value, rhs))
572 }
573}
574
575impl<F, A, const D: usize> MulAssign for BinomialExtensionField<F, D, A>
576where
577 F: BinomiallyExtendable<D>,
578 A: BinomiallyExtendableAlgebra<F, D>,
579{
580 #[inline]
581 fn mul_assign(&mut self, rhs: Self) {
582 *self = self.clone() * rhs;
583 }
584}
585
586impl<F, A, const D: usize> MulAssign<A> for BinomialExtensionField<F, D, A>
587where
588 F: BinomiallyExtendable<D>,
589 A: BinomiallyExtendableAlgebra<F, D>,
590{
591 #[inline]
592 fn mul_assign(&mut self, rhs: A) {
593 *self = self.clone() * rhs;
594 }
595}
596
597impl<F, A, const D: usize> Product for BinomialExtensionField<F, D, A>
598where
599 F: BinomiallyExtendable<D>,
600 A: BinomiallyExtendableAlgebra<F, D> + Copy,
601{
602 #[inline]
603 fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
604 iter.reduce(|acc, x| acc * x).unwrap_or(Self::ONE)
605 }
606}
607
608impl<F, const D: usize> Div for BinomialExtensionField<F, D>
609where
610 F: BinomiallyExtendable<D>,
611{
612 type Output = Self;
613
614 #[allow(clippy::suspicious_arithmetic_impl)]
615 #[inline]
616 fn div(self, rhs: Self) -> Self::Output {
617 self * rhs.inverse()
618 }
619}
620
621impl<F, const D: usize> DivAssign for BinomialExtensionField<F, D>
622where
623 F: BinomiallyExtendable<D>,
624{
625 #[inline]
626 fn div_assign(&mut self, rhs: Self) {
627 *self = *self / rhs;
628 }
629}
630
631impl<F: BinomiallyExtendable<D>, const D: usize> Distribution<BinomialExtensionField<F, D>>
632 for StandardUniform
633where
634 Self: Distribution<F>,
635{
636 #[inline]
637 fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> BinomialExtensionField<F, D> {
638 BinomialExtensionField::new(array::from_fn(|_| self.sample(rng)))
639 }
640}
641
642impl<F: Field + HasTwoAdicBinomialExtension<D>, const D: usize> TwoAdicField
643 for BinomialExtensionField<F, D>
644{
645 const TWO_ADICITY: usize = F::EXT_TWO_ADICITY;
646
647 #[inline]
648 fn two_adic_generator(bits: usize) -> Self {
649 Self::new(F::ext_two_adic_generator(bits))
650 }
651}
652
653#[inline]
655pub fn vector_add<R: PrimeCharacteristicRing + Add<R2, Output = R>, R2: Dup, const D: usize>(
656 a: &[R; D],
657 b: &[R2; D],
658) -> [R; D] {
659 array::from_fn(|i| a[i].dup() + b[i].dup())
660}
661
662#[inline]
664pub(crate) fn vector_sub<
665 R: PrimeCharacteristicRing + Sub<R2, Output = R>,
666 R2: Dup,
667 const D: usize,
668>(
669 a: &[R; D],
670 b: &[R2; D],
671) -> [R; D] {
672 array::from_fn(|i| a[i].dup() - b[i].dup())
673}
674
675#[inline]
677pub(super) fn binomial_mul<
678 F: Field,
679 R: Algebra<F> + Algebra<R2>,
680 R2: Algebra<F>,
681 const D: usize,
682>(
683 a: &[R; D],
684 b: &[R2; D],
685 res: &mut [R; D],
686 w: F,
687) {
688 match D {
689 2 => quadratic_mul(a, b, res, w),
690 3 => cubic_mul(a, b, res, w),
691 4 => quartic_mul(a, b, res, w),
692 5 => quintic_mul(a, b, res, w),
693 8 => octic_mul(a, b, res, w),
694 _ =>
695 {
696 #[allow(clippy::needless_range_loop)]
697 for i in 0..D {
698 for j in 0..D {
699 if i + j >= D {
700 res[i + j - D] += a[i].dup() * w * b[j].dup();
701 } else {
702 res[i + j] += a[i].dup() * b[j].dup();
703 }
704 }
705 }
706 }
707 }
708}
709
710#[inline]
714pub(super) fn binomial_square<F: Field, R: Algebra<F>, const D: usize>(
715 a: &[R; D],
716 res: &mut [R; D],
717 w: F,
718) {
719 match D {
720 2 => {
721 let a1_w = a[1].dup() * w;
722 res[0] = R::dot_product(a[..].try_into().unwrap(), &[a[0].dup(), a1_w]);
723 res[1] = a[0].dup() * a[1].double();
724 }
725 3 => cubic_square(a, res, w),
726 4 => quartic_square(a, res, w),
727 5 => quintic_square(a, res, w),
728 8 => octic_square(a, res, w),
729 _ => binomial_mul::<F, R, R, D>(a, a, res, w),
730 }
731}
732
733#[inline]
747fn quadratic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
748where
749 F: Field,
750 R: Algebra<F> + Algebra<R2>,
751 R2: Algebra<F>,
752{
753 let b1_w = b[1].dup() * w;
754
755 res[0] = R::dot_product(a[..].try_into().unwrap(), &[b[0].dup().into(), b1_w.into()]);
757
758 res[1] = R::dot_product(
760 &[a[0].dup(), a[1].dup()],
761 &[b[1].dup().into(), b[0].dup().into()],
762 );
763}
764
765#[inline]
767fn quadratic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
768 assert_eq!(D, 2);
769 let neg_a1 = -a[1];
770 let scalar = F::dot_product(&[a[0], neg_a1], &[a[0], w * a[1]]).inverse();
771 res[0] = a[0] * scalar;
772 res[1] = neg_a1 * scalar;
773}
774
775#[inline]
777fn cubic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
778 assert_eq!(D, 3);
779 let a0_square = a[0].square();
780 let a1_square = a[1].square();
781 let a2_w = w * a[2];
782 let a0_a1 = a[0] * a[1];
783
784 let scalar = (a0_square * a[0] + w * a[1] * a1_square + a2_w.square() * a[2]
786 - (F::ONE + F::TWO) * a2_w * a0_a1)
787 .inverse();
788
789 res[0] = scalar * (a0_square - a[1] * a2_w);
791 res[1] = scalar * (a2_w * a[2] - a0_a1);
792 res[2] = scalar * (a1_square - a[0] * a[2]);
793}
794
795#[inline]
797fn cubic_mul<F: Field, R: Algebra<F> + Algebra<R2>, R2: Algebra<F>, const D: usize>(
798 a: &[R; D],
799 b: &[R2; D],
800 res: &mut [R; D],
801 w: F,
802) {
803 assert_eq!(D, 3);
804 let a0_b0 = a[0].dup() * b[0].dup();
808 let a1_b1 = a[1].dup() * b[1].dup();
809 let a2_b2 = a[2].dup() * b[2].dup();
810
811 res[0] = a0_b0.dup()
812 + ((a[1].dup() + a[2].dup()) * (b[1].dup() + b[2].dup()) - a1_b1.dup() - a2_b2.dup()) * w;
813 res[1] = (a[0].dup() + a[1].dup()) * (b[0].dup() + b[1].dup()) - a0_b0.dup() - a1_b1.dup()
814 + a2_b2.dup() * w;
815 res[2] = (a[0].dup() + a[2].dup()) * (b[0].dup() + b[2].dup()) - a0_b0 - a2_b2 + a1_b1;
816}
817
818#[inline]
820fn cubic_square<F: Field, R: Algebra<F>, const D: usize>(a: &[R; D], res: &mut [R; D], w: F) {
821 assert_eq!(D, 3);
822
823 let w_a2 = a[2].dup() * w;
824
825 res[0] = a[0].square() + (a[1].dup() * w_a2.dup()).double();
826 res[1] = w_a2 * a[2].dup() + (a[0].dup() * a[1].dup()).double();
827 res[2] = a[1].square() + (a[0].dup() * a[2].dup()).double();
828}
829
830#[inline]
835pub fn quartic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
836where
837 F: Field,
838 R: Algebra<F> + Algebra<R2>,
839 R2: Algebra<F>,
840{
841 assert_eq!(D, 4);
842 let b_r_rev: [R; 5] = [
843 b[3].dup().into(),
844 b[2].dup().into(),
845 b[1].dup().into(),
846 b[0].dup().into(),
847 w.into(),
848 ];
849
850 let w_coeff_0 =
852 R::dot_product::<3>(a[1..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
853 res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[3..].try_into().unwrap());
854
855 let w_coeff_1 =
857 R::dot_product::<2>(a[2..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
858 res[1] = R::dot_product(
859 &[a[0].dup(), a[1].dup(), w_coeff_1],
860 b_r_rev[2..].try_into().unwrap(),
861 );
862
863 let b3_w = b[3].dup() * w;
865 res[2] = R::dot_product::<4>(
866 a[..4].try_into().unwrap(),
867 &[
868 b_r_rev[1].dup(),
869 b_r_rev[2].dup(),
870 b_r_rev[3].dup(),
871 b3_w.into(),
872 ],
873 );
874
875 res[3] = R::dot_product::<4>(a[..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
877}
878
879#[inline]
881fn quartic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
882 assert_eq!(D, 4);
883
884 let neg_a1 = -a[1];
896 let a3_w = a[3] * w;
897 let norm_0 = F::dot_product(&[a[0], a[2], neg_a1.double()], &[a[0], a[2] * w, a3_w]);
898 let norm_1 = F::dot_product(&[a[0], a[1], -a[3]], &[a[2].double(), neg_a1, a3_w]);
899
900 let mut inv = [F::ZERO; 2];
902 quadratic_inv(&[norm_0, norm_1], &mut inv, w);
903
904 let mut out_evn = [F::ZERO; 2];
909 let mut out_odd = [F::ZERO; 2];
910 quadratic_mul(&[a[0], a[2]], &inv, &mut out_evn, w);
911 quadratic_mul(&[a[1], a[3]], &inv, &mut out_odd, w);
912
913 res[0] = out_evn[0];
914 res[1] = -out_odd[0];
915 res[2] = out_evn[1];
916 res[3] = -out_odd[1];
917}
918
919#[inline]
924fn quartic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
925where
926 F: Field,
927 R: Algebra<F>,
928{
929 assert_eq!(D, 4);
930
931 let two_a0 = a[0].double();
932 let two_a1 = a[1].double();
933 let two_a2 = a[2].double();
934 let a2_w = a[2].dup() * w;
935 let a3_w = a[3].dup() * w;
936
937 res[0] = R::dot_product(
939 &[a[0].dup(), a2_w, two_a1],
940 &[a[0].dup(), a[2].dup(), a3_w.dup()],
941 );
942
943 res[1] = R::dot_product(&[two_a0.dup(), two_a2.dup()], &[a[1].dup(), a3_w.dup()]);
945
946 res[2] = R::dot_product(
948 &[a[1].dup(), a3_w, two_a0.dup()],
949 &[a[1].dup(), a[3].dup(), a[2].dup()],
950 );
951
952 res[3] = R::dot_product(&[two_a0, two_a2], &[a[3].dup(), a[1].dup()]);
954}
955
956pub fn quintic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
961where
962 F: Field,
963 R: Algebra<F> + Algebra<R2>,
964 R2: Algebra<F>,
965{
966 assert_eq!(D, 5);
967 let b_r_rev: [R; 6] = [
968 b[4].dup().into(),
969 b[3].dup().into(),
970 b[2].dup().into(),
971 b[1].dup().into(),
972 b[0].dup().into(),
973 w.into(),
974 ];
975
976 let w_coeff_0 =
978 R::dot_product::<4>(a[1..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
979 res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[4..].try_into().unwrap());
980
981 let w_coeff_1 =
983 R::dot_product::<3>(a[2..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
984 res[1] = R::dot_product(
985 &[a[0].dup(), a[1].dup(), w_coeff_1],
986 b_r_rev[3..].try_into().unwrap(),
987 );
988
989 let w_coeff_2 =
991 R::dot_product::<2>(a[3..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
992 res[2] = R::dot_product(
993 &[a[0].dup(), a[1].dup(), a[2].dup(), w_coeff_2],
994 b_r_rev[2..].try_into().unwrap(),
995 );
996
997 let b4_w = b[4].dup() * w;
999 res[3] = R::dot_product::<5>(
1000 a[..5].try_into().unwrap(),
1001 &[
1002 b_r_rev[1].dup(),
1003 b_r_rev[2].dup(),
1004 b_r_rev[3].dup(),
1005 b_r_rev[4].dup(),
1006 b4_w.into(),
1007 ],
1008 );
1009
1010 res[4] = R::dot_product::<5>(a[..].try_into().unwrap(), b_r_rev[..5].try_into().unwrap());
1012}
1013
1014#[inline]
1019fn quintic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
1020where
1021 F: Field,
1022 R: Algebra<F>,
1023{
1024 assert_eq!(D, 5);
1025
1026 let two_a0 = a[0].double();
1027 let two_a1 = a[1].double();
1028 let two_a2 = a[2].double();
1029 let two_a3 = a[3].double();
1030 let w_a3 = a[3].dup() * w;
1031 let w_a4 = a[4].dup() * w;
1032
1033 res[0] = R::dot_product(
1035 &[a[0].dup(), w_a4.dup(), w_a3.dup()],
1036 &[a[0].dup(), two_a1.dup(), two_a2.dup()],
1037 );
1038
1039 res[1] = R::dot_product(
1041 &[w_a3, two_a0.dup(), w_a4.dup()],
1042 &[a[3].dup(), a[1].dup(), two_a2],
1043 );
1044
1045 res[2] = R::dot_product(
1047 &[a[1].dup(), two_a0.dup(), w_a4.dup()],
1048 &[a[1].dup(), a[2].dup(), two_a3],
1049 );
1050
1051 res[3] = R::dot_product(
1053 &[w_a4, two_a0.dup(), two_a1.dup()],
1054 &[a[4].dup(), a[3].dup(), a[2].dup()],
1055 );
1056
1057 res[4] = R::dot_product(
1059 &[a[2].dup(), two_a0, two_a1],
1060 &[a[2].dup(), a[4].dup(), a[3].dup()],
1061 );
1062}
1063
1064#[inline]
1069fn octic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
1070where
1071 F: Field,
1072 R: Algebra<F>,
1073{
1074 assert_eq!(D, 8);
1075
1076 let a0_2 = a[0].double();
1077 let a1_2 = a[1].double();
1078 let a2_2 = a[2].double();
1079 let a3_2 = a[3].double();
1080 let w_a4 = a[4].dup() * w;
1081 let w_a5 = a[5].dup() * w;
1082 let w_a6 = a[6].dup() * w;
1083 let w_a7 = a[7].dup() * w;
1084 let w_a5_2 = w_a5.double();
1085 let w_a6_2 = w_a6.double();
1086 let w_a7_2 = w_a7.double();
1087
1088 res[0] = R::dot_product(
1090 &[a[0].dup(), a[1].dup(), a[2].dup(), a[3].dup(), a[4].dup()],
1091 &[a[0].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2.dup(), w_a4],
1092 );
1093
1094 res[1] = R::dot_product(
1096 &[a0_2.dup(), a[2].dup(), a[3].dup(), a[4].dup()],
1097 &[a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2],
1098 );
1099
1100 res[2] = R::dot_product(
1102 &[a0_2.dup(), a[1].dup(), a[3].dup(), a[4].dup(), a[5].dup()],
1103 &[a[2].dup(), a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5],
1104 );
1105
1106 res[3] = R::dot_product(
1108 &[a0_2.dup(), a1_2.dup(), a[4].dup(), a[5].dup()],
1109 &[a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6_2],
1110 );
1111
1112 res[4] = R::dot_product(
1114 &[a0_2.dup(), a1_2.dup(), a[2].dup(), a[5].dup(), a[6].dup()],
1115 &[a[4].dup(), a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6],
1116 );
1117
1118 res[5] = R::dot_product(
1120 &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[6].dup()],
1121 &[a[5].dup(), a[4].dup(), a[3].dup(), w_a7_2],
1122 );
1123
1124 res[6] = R::dot_product(
1126 &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[3].dup(), a[7].dup()],
1127 &[a[6].dup(), a[5].dup(), a[4].dup(), a[3].dup(), w_a7],
1128 );
1129
1130 res[7] = R::dot_product(
1132 &[a0_2, a1_2, a2_2, a3_2],
1133 &[a[7].dup(), a[6].dup(), a[5].dup(), a[4].dup()],
1134 );
1135}
1136
1137#[inline]
1139fn quintic_inv<F: BinomiallyExtendable<D>, const D: usize>(
1140 a: &BinomialExtensionField<F, D>,
1141) -> BinomialExtensionField<F, D> {
1142 let a_exp_q = a.frobenius();
1144 let a_exp_q_plus_q_sq = (*a * a_exp_q).frobenius();
1145 let prod_conj = a_exp_q_plus_q_sq * a_exp_q_plus_q_sq.repeated_frobenius(2);
1146
1147 let a_vals = a.value;
1150 let mut b = prod_conj.value;
1151 b.reverse();
1152
1153 let w_coeff = F::dot_product::<4>(a.value[1..].try_into().unwrap(), b[..4].try_into().unwrap());
1154 let norm = F::dot_product::<2>(&[a_vals[0], F::W], &[b[4], w_coeff]);
1155 debug_assert_eq!(BinomialExtensionField::<F, D>::from(norm), *a * prod_conj);
1156
1157 prod_conj * norm.inverse()
1158}
1159
1160#[inline]
1169fn compute_coefficient<
1170 F,
1171 R,
1172 const D: usize,
1173 const D_PLUS_1: usize,
1174 const N: usize,
1175 const D_PLUS_1_MIN_N: usize,
1176>(
1177 a: &[R; D],
1178 b_rev: &[R; D_PLUS_1],
1179) -> R
1180where
1181 F: Field,
1182 R: Algebra<F>,
1183{
1184 let w_coeff = R::dot_product::<N>(
1185 a[(D - N)..].try_into().unwrap(),
1186 b_rev[..N].try_into().unwrap(),
1187 );
1188 let mut scratch: [R; D_PLUS_1_MIN_N] = array::from_fn(|i| a[i].dup());
1189 scratch[D_PLUS_1_MIN_N - 1] = w_coeff;
1190 R::dot_product(&scratch, b_rev[N..].try_into().unwrap())
1191}
1192
1193#[inline]
1198pub fn octic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
1199where
1200 F: Field,
1201 R: Algebra<F> + Algebra<R2>,
1202 R2: Algebra<F>,
1203{
1204 assert_eq!(D, 8);
1205 let a: &[R; 8] = a[..].try_into().unwrap();
1206 let mut b_r_rev: [R; 9] = [
1207 b[7].dup().into(),
1208 b[6].dup().into(),
1209 b[5].dup().into(),
1210 b[4].dup().into(),
1211 b[3].dup().into(),
1212 b[2].dup().into(),
1213 b[1].dup().into(),
1214 b[0].dup().into(),
1215 w.into(),
1216 ];
1217
1218 res[0] = compute_coefficient::<F, R, 8, 9, 7, 2>(a, &b_r_rev);
1220
1221 res[1] = compute_coefficient::<F, R, 8, 9, 6, 3>(a, &b_r_rev);
1223
1224 res[2] = compute_coefficient::<F, R, 8, 9, 5, 4>(a, &b_r_rev);
1226
1227 res[3] = compute_coefficient::<F, R, 8, 9, 4, 5>(a, &b_r_rev);
1229
1230 res[4] = compute_coefficient::<F, R, 8, 9, 3, 6>(a, &b_r_rev);
1232
1233 res[5] = compute_coefficient::<F, R, 8, 9, 2, 7>(a, &b_r_rev);
1235
1236 b_r_rev[8] *= b[7].dup();
1238 res[6] = R::dot_product::<8>(a, b_r_rev[1..].try_into().unwrap());
1239
1240 res[7] = R::dot_product::<8>(a, b_r_rev[..8].try_into().unwrap());
1242}
1243
1244#[inline]
1246fn octic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
1247 assert_eq!(D, 8);
1248
1249 let evns = [a[0], a[2], a[4], a[6]];
1264 let odds = [a[1], a[3], a[5], a[7]];
1265 let mut evns_sq = [F::ZERO; 4];
1266 let mut odds_sq = [F::ZERO; 4];
1267 quartic_square(&evns, &mut evns_sq, w);
1268 quartic_square(&odds, &mut odds_sq, w);
1269 let norm = [
1271 evns_sq[0] - w * odds_sq[3],
1272 evns_sq[1] - odds_sq[0],
1273 evns_sq[2] - odds_sq[1],
1274 evns_sq[3] - odds_sq[2],
1275 ];
1276
1277 let mut norm_inv = [F::ZERO; 4];
1279 quartic_inv(&norm, &mut norm_inv, w);
1280
1281 let mut out_evn = [F::ZERO; 4];
1287 let mut out_odd = [F::ZERO; 4];
1288 quartic_mul(&evns, &norm_inv, &mut out_evn, w);
1289 quartic_mul(&odds, &norm_inv, &mut out_odd, w);
1290
1291 res[0] = out_evn[0];
1292 res[1] = -out_odd[0];
1293 res[2] = out_evn[1];
1294 res[3] = -out_odd[1];
1295 res[4] = out_evn[2];
1296 res[5] = -out_odd[2];
1297 res[6] = out_evn[3];
1298 res[7] = -out_odd[3];
1299}