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 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, A, const D: usize> From<[A; D]> for BinomialExtensionField<F, D, A> {
84 #[inline]
85 fn from(x: [A; D]) -> Self {
86 Self {
87 value: x,
88 _phantom: PhantomData,
89 }
90 }
91}
92
93impl<F: BinomiallyExtendable<D>, const D: usize> Packable for BinomialExtensionField<F, D> {}
94
95impl<F: BinomiallyExtendable<D>, A: Algebra<F>, const D: usize> BasedVectorSpace<A>
96 for BinomialExtensionField<F, D, A>
97{
98 const DIMENSION: usize = D;
99
100 #[inline]
101 fn as_basis_coefficients_slice(&self) -> &[A] {
102 &self.value
103 }
104
105 #[inline]
106 fn from_basis_coefficients_fn<Fn: FnMut(usize) -> A>(f: Fn) -> Self {
107 Self::new(array::from_fn(f))
108 }
109
110 #[inline]
111 fn from_basis_coefficients_iter<I: ExactSizeIterator<Item = A>>(mut iter: I) -> Option<Self> {
112 (iter.len() == D).then(|| Self::new(array::from_fn(|_| iter.next().unwrap()))) }
114
115 #[inline]
116 fn flatten_to_base(vec: Vec<Self>) -> Vec<A> {
117 unsafe {
118 flatten_to_base::<A, Self>(vec)
121 }
122 }
123
124 #[inline]
125 fn reconstitute_from_base(vec: Vec<A>) -> Vec<Self> {
126 unsafe {
127 reconstitute_from_base::<A, Self>(vec)
130 }
131 }
132}
133
134impl<F: BinomiallyExtendable<D>, const D: usize> ExtensionField<F>
135 for BinomialExtensionField<F, D>
136{
137 type ExtensionPacking = PackedBinomialExtensionField<F, F::Packing, D>;
138
139 #[inline]
140 fn is_in_basefield(&self) -> bool {
141 self.value[1..].iter().all(F::is_zero)
142 }
143
144 #[inline]
145 fn as_base(&self) -> Option<F> {
146 <Self as ExtensionField<F>>::is_in_basefield(self).then(|| self.value[0])
147 }
148}
149
150impl<F: BinomiallyExtendable<D>, const D: usize> HasFrobenius<F> for BinomialExtensionField<F, D> {
151 #[inline]
153 fn frobenius(&self) -> Self {
154 let mut res = Self::ZERO;
156 for (i, z) in F::DTH_ROOT.powers().take(D).enumerate() {
157 res.value[i] = self.value[i] * z;
158 }
159
160 res
161 }
162
163 #[inline]
168 fn repeated_frobenius(&self, count: usize) -> Self {
169 if count == 0 {
170 return *self;
171 } else if count >= D {
172 return self.repeated_frobenius(count % D);
175 }
176
177 let z0 = F::DTH_ROOT.exp_u64(count as u64);
179
180 let mut res = Self::ZERO;
181 for (i, z) in z0.powers().take(D).enumerate() {
182 res.value[i] = self.value[i] * z;
183 }
184
185 res
186 }
187
188 #[inline]
194 fn pseudo_inv(&self) -> Self {
195 let mut prod_conj = self.frobenius();
209 for _ in 2..D {
210 prod_conj = (prod_conj * *self).frobenius();
211 }
212
213 let a = self.value;
216 let b = prod_conj.value;
217 let mut w_coeff = F::ZERO;
218 for i in 1..D {
223 w_coeff += a[i] * b[D - i];
224 }
225 let norm = F::dot_product(&[a[0], F::W], &[b[0], w_coeff]);
226 debug_assert_eq!(Self::from(norm), *self * prod_conj);
227
228 prod_conj * norm.inverse()
229 }
230}
231
232impl<F, A, const D: usize> PrimeCharacteristicRing for BinomialExtensionField<F, D, A>
233where
234 F: BinomiallyExtendable<D>,
235 A: BinomiallyExtendableAlgebra<F, D> + Copy,
236{
237 type PrimeSubfield = <A as PrimeCharacteristicRing>::PrimeSubfield;
238
239 const ZERO: Self = Self::new([A::ZERO; D]);
240
241 const ONE: Self = Self::new(field_to_array(A::ONE));
242
243 const TWO: Self = Self::new(field_to_array(A::TWO));
244
245 const NEG_ONE: Self = Self::new(field_to_array(A::NEG_ONE));
246
247 #[inline]
248 fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
249 <A as PrimeCharacteristicRing>::from_prime_subfield(f).into()
250 }
251
252 #[inline]
253 fn halve(&self) -> Self {
254 Self::new(array::from_fn(|i| self.value[i].halve()))
255 }
256
257 #[inline(always)]
258 fn square(&self) -> Self {
259 let mut res = Self::default();
260 let w = F::W;
261 binomial_square(&self.value, &mut res.value, w);
262 res
263 }
264
265 #[inline]
266 fn mul_2exp_u64(&self, exp: u64) -> Self {
267 Self::new(array::from_fn(|i| self.value[i].mul_2exp_u64(exp)))
270 }
271
272 #[inline]
273 fn div_2exp_u64(&self, exp: u64) -> Self {
274 Self::new(array::from_fn(|i| self.value[i].div_2exp_u64(exp)))
277 }
278
279 #[inline]
280 fn zero_vec(len: usize) -> Vec<Self> {
281 unsafe { reconstitute_from_base(F::zero_vec(len * D)) }
283 }
284}
285
286impl<F: BinomiallyExtendable<D>, const D: usize> Algebra<F> for BinomialExtensionField<F, D> {}
287
288impl<F: BinomiallyExtendable<D>, const D: usize> RawDataSerializable
289 for BinomialExtensionField<F, D>
290{
291 const NUM_BYTES: usize = F::NUM_BYTES * D;
292
293 #[inline]
294 fn into_bytes(self) -> impl IntoIterator<Item = u8> {
295 self.value.into_iter().flat_map(|x| x.into_bytes())
296 }
297
298 #[inline]
299 fn into_byte_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u8> {
300 F::into_byte_stream(input.into_iter().flat_map(|x| x.value))
301 }
302
303 #[inline]
304 fn into_u32_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u32> {
305 F::into_u32_stream(input.into_iter().flat_map(|x| x.value))
306 }
307
308 #[inline]
309 fn into_u64_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u64> {
310 F::into_u64_stream(input.into_iter().flat_map(|x| x.value))
311 }
312
313 #[inline]
314 fn into_parallel_byte_streams<const N: usize>(
315 input: impl IntoIterator<Item = [Self; N]>,
316 ) -> impl IntoIterator<Item = [u8; N]> {
317 F::into_parallel_byte_streams(
318 input
319 .into_iter()
320 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
321 )
322 }
323
324 #[inline]
325 fn into_parallel_u32_streams<const N: usize>(
326 input: impl IntoIterator<Item = [Self; N]>,
327 ) -> impl IntoIterator<Item = [u32; N]> {
328 F::into_parallel_u32_streams(
329 input
330 .into_iter()
331 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
332 )
333 }
334
335 #[inline]
336 fn into_parallel_u64_streams<const N: usize>(
337 input: impl IntoIterator<Item = [Self; N]>,
338 ) -> impl IntoIterator<Item = [u64; N]> {
339 F::into_parallel_u64_streams(
340 input
341 .into_iter()
342 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
343 )
344 }
345}
346
347impl<F: BinomiallyExtendable<D>, const D: usize> Field for BinomialExtensionField<F, D> {
348 type Packing = Self;
349
350 const GENERATOR: Self = Self::new(F::EXT_GENERATOR);
351
352 fn try_inverse(&self) -> Option<Self> {
353 if self.is_zero() {
354 return None;
355 }
356
357 let mut res = Self::default();
358
359 match D {
360 2 => quadratic_inv(&self.value, &mut res.value, F::W),
361 3 => cubic_inv(&self.value, &mut res.value, F::W),
362 4 => quartic_inv(&self.value, &mut res.value, F::W),
363 5 => res = quintic_inv(self),
364 8 => octic_inv(&self.value, &mut res.value, F::W),
365 _ => res = self.pseudo_inv(),
366 }
367
368 Some(res)
369 }
370
371 #[inline]
372 fn add_slices(slice_1: &mut [Self], slice_2: &[Self]) {
373 unsafe {
377 let base_slice_1 = as_base_slice_mut(slice_1);
378 let base_slice_2 = as_base_slice(slice_2);
379
380 F::add_slices(base_slice_1, base_slice_2);
381 }
382 }
383
384 #[inline]
385 fn order() -> BigUint {
386 F::order().pow(D as u32)
387 }
388}
389
390impl<F, const D: usize> Display for BinomialExtensionField<F, D>
391where
392 F: BinomiallyExtendable<D>,
393{
394 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
395 if self.is_zero() {
396 write!(f, "0")
397 } else {
398 let str = self
399 .value
400 .iter()
401 .enumerate()
402 .filter(|(_, x)| !x.is_zero())
403 .map(|(i, x)| match (i, x.is_one()) {
404 (0, _) => format!("{x}"),
405 (1, true) => "X".to_string(),
406 (1, false) => format!("{x} X"),
407 (_, true) => format!("X^{i}"),
408 (_, false) => format!("{x} X^{i}"),
409 })
410 .join(" + ");
411 write!(f, "{str}")
412 }
413 }
414}
415
416impl<F, A, const D: usize> Neg for BinomialExtensionField<F, D, A>
417where
418 F: BinomiallyExtendable<D>,
419 A: Algebra<F>,
420{
421 type Output = Self;
422
423 #[inline]
424 fn neg(self) -> Self {
425 Self::new(self.value.map(A::neg))
426 }
427}
428
429impl<F, A, const D: usize> Add for BinomialExtensionField<F, D, A>
430where
431 F: BinomiallyExtendable<D>,
432 A: BinomiallyExtendableAlgebra<F, D>,
433{
434 type Output = Self;
435
436 #[inline]
437 fn add(self, rhs: Self) -> Self {
438 let value = A::binomial_add(&self.value, &rhs.value);
439 Self::new(value)
440 }
441}
442
443impl<F, A, const D: usize> Add<A> for BinomialExtensionField<F, D, A>
444where
445 F: BinomiallyExtendable<D>,
446 A: Algebra<F>,
447{
448 type Output = Self;
449
450 #[inline]
451 fn add(mut self, rhs: A) -> Self {
452 self.value[0] += rhs;
453 self
454 }
455}
456
457impl<F, A, const D: usize> AddAssign for BinomialExtensionField<F, D, A>
458where
459 F: BinomiallyExtendable<D>,
460 A: BinomiallyExtendableAlgebra<F, D>,
461{
462 #[inline]
463 fn add_assign(&mut self, rhs: Self) {
464 self.value = A::binomial_add(&self.value, &rhs.value);
465 }
466}
467
468impl<F, A, const D: usize> AddAssign<A> for BinomialExtensionField<F, D, A>
469where
470 F: BinomiallyExtendable<D>,
471 A: Algebra<F>,
472{
473 #[inline]
474 fn add_assign(&mut self, rhs: A) {
475 self.value[0] += rhs;
476 }
477}
478
479impl<F, A, const D: usize> Sum for BinomialExtensionField<F, D, A>
480where
481 F: BinomiallyExtendable<D>,
482 A: BinomiallyExtendableAlgebra<F, D> + Copy,
483{
484 #[inline]
485 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
486 iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO)
487 }
488}
489
490impl<F, A, const D: usize> Sub for BinomialExtensionField<F, D, A>
491where
492 F: BinomiallyExtendable<D>,
493 A: BinomiallyExtendableAlgebra<F, D>,
494{
495 type Output = Self;
496
497 #[inline]
498 fn sub(self, rhs: Self) -> Self {
499 let value = A::binomial_sub(&self.value, &rhs.value);
500 Self::new(value)
501 }
502}
503
504impl<F, A, const D: usize> Sub<A> for BinomialExtensionField<F, D, A>
505where
506 F: BinomiallyExtendable<D>,
507 A: Algebra<F>,
508{
509 type Output = Self;
510
511 #[inline]
512 fn sub(self, rhs: A) -> Self {
513 let mut res = self.value;
514 res[0] -= rhs;
515 Self::new(res)
516 }
517}
518
519impl<F, A, const D: usize> SubAssign for BinomialExtensionField<F, D, A>
520where
521 F: BinomiallyExtendable<D>,
522 A: BinomiallyExtendableAlgebra<F, D>,
523{
524 #[inline]
525 fn sub_assign(&mut self, rhs: Self) {
526 self.value = A::binomial_sub(&self.value, &rhs.value);
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> + Copy,
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: Dup, const D: usize>(
655 a: &[R; D],
656 b: &[R2; D],
657) -> [R; D] {
658 array::from_fn(|i| a[i].dup() + b[i].dup())
659}
660
661#[inline]
663pub(crate) fn vector_sub<
664 R: PrimeCharacteristicRing + Sub<R2, Output = R>,
665 R2: Dup,
666 const D: usize,
667>(
668 a: &[R; D],
669 b: &[R2; D],
670) -> [R; D] {
671 array::from_fn(|i| a[i].dup() - b[i].dup())
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].dup() * w * b[j].dup();
700 } else {
701 res[i + j] += a[i].dup() * b[j].dup();
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].dup() * w;
721 res[0] = R::dot_product(a[..].try_into().unwrap(), &[a[0].dup(), a1_w]);
722 res[1] = a[0].dup() * 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].dup() * w;
753
754 res[0] = R::dot_product(a[..].try_into().unwrap(), &[b[0].dup().into(), b1_w.into()]);
756
757 res[1] = R::dot_product(
759 &[a[0].dup(), a[1].dup()],
760 &[b[1].dup().into(), b[0].dup().into()],
761 );
762}
763
764#[inline]
766fn quadratic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
767 assert_eq!(D, 2);
768 let neg_a1 = -a[1];
769 let scalar = F::dot_product(&[a[0], neg_a1], &[a[0], w * a[1]]).inverse();
770 res[0] = a[0] * scalar;
771 res[1] = neg_a1 * scalar;
772}
773
774#[inline]
776fn cubic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
777 assert_eq!(D, 3);
778 let a0_square = a[0].square();
779 let a1_square = a[1].square();
780 let a2_w = w * a[2];
781 let a0_a1 = a[0] * a[1];
782
783 let scalar = (a0_square * a[0] + w * a[1] * a1_square + a2_w.square() * a[2]
785 - (F::ONE + F::TWO) * a2_w * a0_a1)
786 .inverse();
787
788 res[0] = scalar * (a0_square - a[1] * a2_w);
790 res[1] = scalar * (a2_w * a[2] - a0_a1);
791 res[2] = scalar * (a1_square - a[0] * a[2]);
792}
793
794#[inline]
796fn cubic_mul<F: Field, R: Algebra<F> + Algebra<R2>, R2: Algebra<F>, const D: usize>(
797 a: &[R; D],
798 b: &[R2; D],
799 res: &mut [R; D],
800 w: F,
801) {
802 assert_eq!(D, 3);
803 let a0_b0 = a[0].dup() * b[0].dup();
807 let a1_b1 = a[1].dup() * b[1].dup();
808 let a2_b2 = a[2].dup() * b[2].dup();
809
810 res[0] = a0_b0.dup()
811 + ((a[1].dup() + a[2].dup()) * (b[1].dup() + b[2].dup()) - a1_b1.dup() - a2_b2.dup()) * w;
812 res[1] = (a[0].dup() + a[1].dup()) * (b[0].dup() + b[1].dup()) - a0_b0.dup() - a1_b1.dup()
813 + a2_b2.dup() * w;
814 res[2] = (a[0].dup() + a[2].dup()) * (b[0].dup() + b[2].dup()) - a0_b0 - a2_b2 + a1_b1;
815}
816
817#[inline]
819fn cubic_square<F: Field, R: Algebra<F>, const D: usize>(a: &[R; D], res: &mut [R; D], w: F) {
820 assert_eq!(D, 3);
821
822 let w_a2 = a[2].dup() * w;
823
824 res[0] = a[0].square() + (a[1].dup() * w_a2.dup()).double();
825 res[1] = w_a2 * a[2].dup() + (a[0].dup() * a[1].dup()).double();
826 res[2] = a[1].square() + (a[0].dup() * a[2].dup()).double();
827}
828
829#[inline]
834pub fn quartic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
835where
836 F: Field,
837 R: Algebra<F> + Algebra<R2>,
838 R2: Algebra<F>,
839{
840 assert_eq!(D, 4);
841 let b_r_rev: [R; 5] = [
842 b[3].dup().into(),
843 b[2].dup().into(),
844 b[1].dup().into(),
845 b[0].dup().into(),
846 w.into(),
847 ];
848
849 let w_coeff_0 =
851 R::dot_product::<3>(a[1..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
852 res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[3..].try_into().unwrap());
853
854 let w_coeff_1 =
856 R::dot_product::<2>(a[2..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
857 res[1] = R::dot_product(
858 &[a[0].dup(), a[1].dup(), w_coeff_1],
859 b_r_rev[2..].try_into().unwrap(),
860 );
861
862 let b3_w = b[3].dup() * w;
864 res[2] = R::dot_product::<4>(
865 a[..4].try_into().unwrap(),
866 &[
867 b_r_rev[1].dup(),
868 b_r_rev[2].dup(),
869 b_r_rev[3].dup(),
870 b3_w.into(),
871 ],
872 );
873
874 res[3] = R::dot_product::<4>(a[..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
876}
877
878#[inline]
880fn quartic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
881 assert_eq!(D, 4);
882
883 let neg_a1 = -a[1];
895 let a3_w = a[3] * w;
896 let norm_0 = F::dot_product(&[a[0], a[2], neg_a1.double()], &[a[0], a[2] * w, a3_w]);
897 let norm_1 = F::dot_product(&[a[0], a[1], -a[3]], &[a[2].double(), neg_a1, a3_w]);
898
899 let mut inv = [F::ZERO; 2];
901 quadratic_inv(&[norm_0, norm_1], &mut inv, w);
902
903 let mut out_evn = [F::ZERO; 2];
908 let mut out_odd = [F::ZERO; 2];
909 quadratic_mul(&[a[0], a[2]], &inv, &mut out_evn, w);
910 quadratic_mul(&[a[1], a[3]], &inv, &mut out_odd, w);
911
912 res[0] = out_evn[0];
913 res[1] = -out_odd[0];
914 res[2] = out_evn[1];
915 res[3] = -out_odd[1];
916}
917
918#[inline]
923fn quartic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
924where
925 F: Field,
926 R: Algebra<F>,
927{
928 assert_eq!(D, 4);
929
930 let two_a0 = a[0].double();
931 let two_a1 = a[1].double();
932 let two_a2 = a[2].double();
933 let a2_w = a[2].dup() * w;
934 let a3_w = a[3].dup() * w;
935
936 res[0] = R::dot_product(
938 &[a[0].dup(), a2_w, two_a1],
939 &[a[0].dup(), a[2].dup(), a3_w.dup()],
940 );
941
942 res[1] = R::dot_product(&[two_a0.dup(), two_a2.dup()], &[a[1].dup(), a3_w.dup()]);
944
945 res[2] = R::dot_product(
947 &[a[1].dup(), a3_w, two_a0.dup()],
948 &[a[1].dup(), a[3].dup(), a[2].dup()],
949 );
950
951 res[3] = R::dot_product(&[two_a0, two_a2], &[a[3].dup(), a[1].dup()]);
953}
954
955pub fn quintic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
960where
961 F: Field,
962 R: Algebra<F> + Algebra<R2>,
963 R2: Algebra<F>,
964{
965 assert_eq!(D, 5);
966 let b_r_rev: [R; 6] = [
967 b[4].dup().into(),
968 b[3].dup().into(),
969 b[2].dup().into(),
970 b[1].dup().into(),
971 b[0].dup().into(),
972 w.into(),
973 ];
974
975 let w_coeff_0 =
977 R::dot_product::<4>(a[1..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
978 res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[4..].try_into().unwrap());
979
980 let w_coeff_1 =
982 R::dot_product::<3>(a[2..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
983 res[1] = R::dot_product(
984 &[a[0].dup(), a[1].dup(), w_coeff_1],
985 b_r_rev[3..].try_into().unwrap(),
986 );
987
988 let w_coeff_2 =
990 R::dot_product::<2>(a[3..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
991 res[2] = R::dot_product(
992 &[a[0].dup(), a[1].dup(), a[2].dup(), w_coeff_2],
993 b_r_rev[2..].try_into().unwrap(),
994 );
995
996 let b4_w = b[4].dup() * w;
998 res[3] = R::dot_product::<5>(
999 a[..5].try_into().unwrap(),
1000 &[
1001 b_r_rev[1].dup(),
1002 b_r_rev[2].dup(),
1003 b_r_rev[3].dup(),
1004 b_r_rev[4].dup(),
1005 b4_w.into(),
1006 ],
1007 );
1008
1009 res[4] = R::dot_product::<5>(a[..].try_into().unwrap(), b_r_rev[..5].try_into().unwrap());
1011}
1012
1013#[inline]
1018fn quintic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
1019where
1020 F: Field,
1021 R: Algebra<F>,
1022{
1023 assert_eq!(D, 5);
1024
1025 let two_a0 = a[0].double();
1026 let two_a1 = a[1].double();
1027 let two_a2 = a[2].double();
1028 let two_a3 = a[3].double();
1029 let w_a3 = a[3].dup() * w;
1030 let w_a4 = a[4].dup() * w;
1031
1032 res[0] = R::dot_product(
1034 &[a[0].dup(), w_a4.dup(), w_a3.dup()],
1035 &[a[0].dup(), two_a1.dup(), two_a2.dup()],
1036 );
1037
1038 res[1] = R::dot_product(
1040 &[w_a3, two_a0.dup(), w_a4.dup()],
1041 &[a[3].dup(), a[1].dup(), two_a2],
1042 );
1043
1044 res[2] = R::dot_product(
1046 &[a[1].dup(), two_a0.dup(), w_a4.dup()],
1047 &[a[1].dup(), a[2].dup(), two_a3],
1048 );
1049
1050 res[3] = R::dot_product(
1052 &[w_a4, two_a0.dup(), two_a1.dup()],
1053 &[a[4].dup(), a[3].dup(), a[2].dup()],
1054 );
1055
1056 res[4] = R::dot_product(
1058 &[a[2].dup(), two_a0, two_a1],
1059 &[a[2].dup(), a[4].dup(), a[3].dup()],
1060 );
1061}
1062
1063#[inline]
1068fn octic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
1069where
1070 F: Field,
1071 R: Algebra<F>,
1072{
1073 assert_eq!(D, 8);
1074
1075 let a0_2 = a[0].double();
1076 let a1_2 = a[1].double();
1077 let a2_2 = a[2].double();
1078 let a3_2 = a[3].double();
1079 let w_a4 = a[4].dup() * w;
1080 let w_a5 = a[5].dup() * w;
1081 let w_a6 = a[6].dup() * w;
1082 let w_a7 = a[7].dup() * w;
1083 let w_a5_2 = w_a5.double();
1084 let w_a6_2 = w_a6.double();
1085 let w_a7_2 = w_a7.double();
1086
1087 res[0] = R::dot_product(
1089 &[a[0].dup(), a[1].dup(), a[2].dup(), a[3].dup(), a[4].dup()],
1090 &[a[0].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2.dup(), w_a4],
1091 );
1092
1093 res[1] = R::dot_product(
1095 &[a0_2.dup(), a[2].dup(), a[3].dup(), a[4].dup()],
1096 &[a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2],
1097 );
1098
1099 res[2] = R::dot_product(
1101 &[a0_2.dup(), a[1].dup(), a[3].dup(), a[4].dup(), a[5].dup()],
1102 &[a[2].dup(), a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5],
1103 );
1104
1105 res[3] = R::dot_product(
1107 &[a0_2.dup(), a1_2.dup(), a[4].dup(), a[5].dup()],
1108 &[a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6_2],
1109 );
1110
1111 res[4] = R::dot_product(
1113 &[a0_2.dup(), a1_2.dup(), a[2].dup(), a[5].dup(), a[6].dup()],
1114 &[a[4].dup(), a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6],
1115 );
1116
1117 res[5] = R::dot_product(
1119 &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[6].dup()],
1120 &[a[5].dup(), a[4].dup(), a[3].dup(), w_a7_2],
1121 );
1122
1123 res[6] = R::dot_product(
1125 &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[3].dup(), a[7].dup()],
1126 &[a[6].dup(), a[5].dup(), a[4].dup(), a[3].dup(), w_a7],
1127 );
1128
1129 res[7] = R::dot_product(
1131 &[a0_2, a1_2, a2_2, a3_2],
1132 &[a[7].dup(), a[6].dup(), a[5].dup(), a[4].dup()],
1133 );
1134}
1135
1136#[inline]
1138fn quintic_inv<F: BinomiallyExtendable<D>, const D: usize>(
1139 a: &BinomialExtensionField<F, D>,
1140) -> BinomialExtensionField<F, D> {
1141 let a_exp_q = a.frobenius();
1143 let a_exp_q_plus_q_sq = (*a * a_exp_q).frobenius();
1144 let prod_conj = a_exp_q_plus_q_sq * a_exp_q_plus_q_sq.repeated_frobenius(2);
1145
1146 let a_vals = a.value;
1149 let mut b = prod_conj.value;
1150 b.reverse();
1151
1152 let w_coeff = F::dot_product::<4>(a.value[1..].try_into().unwrap(), b[..4].try_into().unwrap());
1153 let norm = F::dot_product::<2>(&[a_vals[0], F::W], &[b[4], w_coeff]);
1154 debug_assert_eq!(BinomialExtensionField::<F, D>::from(norm), *a * prod_conj);
1155
1156 prod_conj * norm.inverse()
1157}
1158
1159#[inline]
1168fn compute_coefficient<
1169 F,
1170 R,
1171 const D: usize,
1172 const D_PLUS_1: usize,
1173 const N: usize,
1174 const D_PLUS_1_MIN_N: usize,
1175>(
1176 a: &[R; D],
1177 b_rev: &[R; D_PLUS_1],
1178) -> R
1179where
1180 F: Field,
1181 R: Algebra<F>,
1182{
1183 let w_coeff = R::dot_product::<N>(
1184 a[(D - N)..].try_into().unwrap(),
1185 b_rev[..N].try_into().unwrap(),
1186 );
1187 let mut scratch: [R; D_PLUS_1_MIN_N] = array::from_fn(|i| a[i].dup());
1188 scratch[D_PLUS_1_MIN_N - 1] = w_coeff;
1189 R::dot_product(&scratch, b_rev[N..].try_into().unwrap())
1190}
1191
1192#[inline]
1197pub fn octic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
1198where
1199 F: Field,
1200 R: Algebra<F> + Algebra<R2>,
1201 R2: Algebra<F>,
1202{
1203 assert_eq!(D, 8);
1204 let a: &[R; 8] = a[..].try_into().unwrap();
1205 let mut b_r_rev: [R; 9] = [
1206 b[7].dup().into(),
1207 b[6].dup().into(),
1208 b[5].dup().into(),
1209 b[4].dup().into(),
1210 b[3].dup().into(),
1211 b[2].dup().into(),
1212 b[1].dup().into(),
1213 b[0].dup().into(),
1214 w.into(),
1215 ];
1216
1217 res[0] = compute_coefficient::<F, R, 8, 9, 7, 2>(a, &b_r_rev);
1219
1220 res[1] = compute_coefficient::<F, R, 8, 9, 6, 3>(a, &b_r_rev);
1222
1223 res[2] = compute_coefficient::<F, R, 8, 9, 5, 4>(a, &b_r_rev);
1225
1226 res[3] = compute_coefficient::<F, R, 8, 9, 4, 5>(a, &b_r_rev);
1228
1229 res[4] = compute_coefficient::<F, R, 8, 9, 3, 6>(a, &b_r_rev);
1231
1232 res[5] = compute_coefficient::<F, R, 8, 9, 2, 7>(a, &b_r_rev);
1234
1235 b_r_rev[8] *= b[7].dup();
1237 res[6] = R::dot_product::<8>(a, b_r_rev[1..].try_into().unwrap());
1238
1239 res[7] = R::dot_product::<8>(a, b_r_rev[..8].try_into().unwrap());
1241}
1242
1243#[inline]
1245fn octic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
1246 assert_eq!(D, 8);
1247
1248 let evns = [a[0], a[2], a[4], a[6]];
1263 let odds = [a[1], a[3], a[5], a[7]];
1264 let mut evns_sq = [F::ZERO; 4];
1265 let mut odds_sq = [F::ZERO; 4];
1266 quartic_square(&evns, &mut evns_sq, w);
1267 quartic_square(&odds, &mut odds_sq, w);
1268 let norm = [
1270 evns_sq[0] - w * odds_sq[3],
1271 evns_sq[1] - odds_sq[0],
1272 evns_sq[2] - odds_sq[1],
1273 evns_sq[3] - odds_sq[2],
1274 ];
1275
1276 let mut norm_inv = [F::ZERO; 4];
1278 quartic_inv(&norm, &mut norm_inv, w);
1279
1280 let mut out_evn = [F::ZERO; 4];
1286 let mut out_odd = [F::ZERO; 4];
1287 quartic_mul(&evns, &norm_inv, &mut out_evn, w);
1288 quartic_mul(&odds, &norm_inv, &mut out_odd, w);
1289
1290 res[0] = out_evn[0];
1291 res[1] = -out_odd[0];
1292 res[2] = out_evn[1];
1293 res[3] = -out_odd[1];
1294 res[4] = out_evn[2];
1295 res[5] = -out_odd[2];
1296 res[6] = out_evn[3];
1297 res[7] = -out_odd[3];
1298}