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