1use alloc::format;
2use alloc::string::ToString;
3use alloc::vec::Vec;
4use core::array;
5use core::fmt::{self, Display, Formatter};
6use core::iter::{Product, Sum};
7use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
8
9use itertools::Itertools;
10use num_bigint::BigUint;
11use p3_util::{as_base_slice, as_base_slice_mut, reconstitute_from_base};
12
13use super::{ExtField, HasFrobenius, HasTwoAdicBinomialExtension, PackedBinomialExtensionField};
14use crate::extension::{Binomial, BinomiallyExtendable, ExtensionAlgebra};
15use crate::field::Field;
16use crate::{
17 Algebra, Dup, ExtensionField, PrimeCharacteristicRing, RawDataSerializable, TwoAdicField,
18 field_to_array,
19};
20
21pub type BinomialExtensionField<F, const D: usize, A = F> = ExtField<F, D, Binomial<F>, A>;
25
26impl<F: Copy, const D: usize> BinomialExtensionField<F, D, F> {
27 #[inline]
34 pub const fn new_array<const N: usize>(input: [[F; D]; N]) -> [Self; N] {
35 const { assert!(N > 0) }
36 let mut output = [Self::new(input[0]); N];
37 let mut i = 1;
38 while i < N {
39 output[i] = Self::new(input[i]);
40 i += 1;
41 }
42 output
43 }
44}
45
46impl<F: BinomiallyExtendable<D>, const D: usize> ExtensionField<F>
47 for BinomialExtensionField<F, D>
48{
49 type ExtensionPacking = PackedBinomialExtensionField<F, F::Packing, D>;
50
51 #[inline]
52 fn is_in_basefield(&self) -> bool {
53 self.value[1..].iter().all(F::is_zero)
54 }
55
56 #[inline]
57 fn as_base(&self) -> Option<F> {
58 <Self as ExtensionField<F>>::is_in_basefield(self).then(|| self.value[0])
59 }
60}
61
62impl<F: BinomiallyExtendable<D>, const D: usize> HasFrobenius<F> for BinomialExtensionField<F, D> {
63 #[inline]
65 fn frobenius(&self) -> Self {
66 let mut res = Self::ZERO;
68 for (i, z) in F::DTH_ROOT.powers().take(D).enumerate() {
69 res.value[i] = self.value[i] * z;
70 }
71
72 res
73 }
74
75 #[inline]
80 fn repeated_frobenius(&self, count: usize) -> Self {
81 if count == 0 {
82 return *self;
83 } else if count >= D {
84 return self.repeated_frobenius(count % D);
87 }
88
89 let z0 = F::DTH_ROOT.exp_u64(count as u64);
91
92 let mut res = Self::ZERO;
93 for (i, z) in z0.powers().take(D).enumerate() {
94 res.value[i] = self.value[i] * z;
95 }
96
97 res
98 }
99
100 #[inline]
106 fn pseudo_inv(&self) -> Self {
107 let mut prod_conj = self.frobenius();
121 for _ in 2..D {
122 prod_conj = (prod_conj * *self).frobenius();
123 }
124
125 let a = self.value;
128 let b = prod_conj.value;
129 let mut w_coeff = F::ZERO;
130 for i in 1..D {
135 w_coeff += a[i] * b[D - i];
136 }
137 let norm = F::dot_product(&[a[0], F::W], &[b[0], w_coeff]);
138 debug_assert_eq!(Self::from(norm), *self * prod_conj);
139
140 prod_conj * norm.inverse()
141 }
142}
143
144impl<F, A, const D: usize> PrimeCharacteristicRing for BinomialExtensionField<F, D, A>
145where
146 F: BinomiallyExtendable<D>,
147 A: ExtensionAlgebra<F, D, Binomial<F>> + Copy,
148{
149 type PrimeSubfield = <A as PrimeCharacteristicRing>::PrimeSubfield;
150
151 const ZERO: Self = Self::new([A::ZERO; D]);
152
153 const ONE: Self = Self::new(field_to_array(A::ONE));
154
155 const TWO: Self = Self::new(field_to_array(A::TWO));
156
157 const NEG_ONE: Self = Self::new(field_to_array(A::NEG_ONE));
158
159 #[inline]
160 fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
161 <A as PrimeCharacteristicRing>::from_prime_subfield(f).into()
162 }
163
164 #[inline]
165 fn halve(&self) -> Self {
166 Self::new(array::from_fn(|i| self.value[i].halve()))
167 }
168
169 #[inline(always)]
170 fn square(&self) -> Self {
171 let mut res = Self::default();
172 let w = F::W;
173 binomial_square(&self.value, &mut res.value, w);
174 res
175 }
176
177 #[inline]
178 fn mul_2exp_u64(&self, exp: u64) -> Self {
179 Self::new(array::from_fn(|i| self.value[i].mul_2exp_u64(exp)))
182 }
183
184 #[inline]
185 fn div_2exp_u64(&self, exp: u64) -> Self {
186 Self::new(array::from_fn(|i| self.value[i].div_2exp_u64(exp)))
189 }
190
191 #[inline]
192 fn zero_vec(len: usize) -> Vec<Self> {
193 unsafe { reconstitute_from_base(F::zero_vec(len * D)) }
195 }
196}
197
198impl<F: BinomiallyExtendable<D>, const D: usize> Algebra<F> for BinomialExtensionField<F, D> {}
199
200impl<F: BinomiallyExtendable<D>, const D: usize> RawDataSerializable
201 for BinomialExtensionField<F, D>
202{
203 const NUM_BYTES: usize = F::NUM_BYTES * D;
204
205 #[inline]
206 fn into_bytes(self) -> impl IntoIterator<Item = u8> {
207 self.value.into_iter().flat_map(|x| x.into_bytes())
208 }
209
210 #[inline]
211 fn into_byte_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u8> {
212 F::into_byte_stream(input.into_iter().flat_map(|x| x.value))
213 }
214
215 #[inline]
216 fn into_u32_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u32> {
217 F::into_u32_stream(input.into_iter().flat_map(|x| x.value))
218 }
219
220 #[inline]
221 fn into_u64_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u64> {
222 F::into_u64_stream(input.into_iter().flat_map(|x| x.value))
223 }
224
225 #[inline]
226 fn into_parallel_byte_streams<const N: usize>(
227 input: impl IntoIterator<Item = [Self; N]>,
228 ) -> impl IntoIterator<Item = [u8; N]> {
229 F::into_parallel_byte_streams(
230 input
231 .into_iter()
232 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
233 )
234 }
235
236 #[inline]
237 fn into_parallel_u32_streams<const N: usize>(
238 input: impl IntoIterator<Item = [Self; N]>,
239 ) -> impl IntoIterator<Item = [u32; N]> {
240 F::into_parallel_u32_streams(
241 input
242 .into_iter()
243 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
244 )
245 }
246
247 #[inline]
248 fn into_parallel_u64_streams<const N: usize>(
249 input: impl IntoIterator<Item = [Self; N]>,
250 ) -> impl IntoIterator<Item = [u64; N]> {
251 F::into_parallel_u64_streams(
252 input
253 .into_iter()
254 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
255 )
256 }
257}
258
259impl<F: BinomiallyExtendable<D>, const D: usize> Field for BinomialExtensionField<F, D> {
260 type Packing = Self;
261
262 const GENERATOR: Self = Self::new(F::EXT_GENERATOR);
263
264 fn try_inverse(&self) -> Option<Self> {
265 if self.is_zero() {
266 return None;
267 }
268
269 let mut res = Self::default();
270
271 match D {
272 2 => quadratic_inv(&self.value, &mut res.value, F::W),
273 3 => cubic_inv(&self.value, &mut res.value, F::W),
274 4 => quartic_inv(&self.value, &mut res.value, F::W),
275 5 => res = quintic_inv(self),
276 8 => octic_inv(&self.value, &mut res.value, F::W),
277 _ => res = self.pseudo_inv(),
278 }
279
280 Some(res)
281 }
282
283 #[inline]
284 fn add_slices(slice_1: &mut [Self], slice_2: &[Self]) {
285 unsafe {
289 let base_slice_1 = as_base_slice_mut(slice_1);
290 let base_slice_2 = as_base_slice(slice_2);
291
292 F::add_slices(base_slice_1, base_slice_2);
293 }
294 }
295
296 #[inline]
297 fn order() -> BigUint {
298 F::order().pow(D as u32)
299 }
300}
301
302impl<F, const D: usize> Display for BinomialExtensionField<F, D>
303where
304 F: BinomiallyExtendable<D>,
305{
306 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
307 if self.is_zero() {
308 write!(f, "0")
309 } else {
310 let str = self
311 .value
312 .iter()
313 .enumerate()
314 .filter(|(_, x)| !x.is_zero())
315 .map(|(i, x)| match (i, x.is_one()) {
316 (0, _) => format!("{x}"),
317 (1, true) => "X".to_string(),
318 (1, false) => format!("{x} X"),
319 (_, true) => format!("X^{i}"),
320 (_, false) => format!("{x} X^{i}"),
321 })
322 .join(" + ");
323 write!(f, "{str}")
324 }
325 }
326}
327
328impl<F, A, const D: usize> Neg for BinomialExtensionField<F, D, A>
329where
330 F: BinomiallyExtendable<D>,
331 A: Algebra<F>,
332{
333 type Output = Self;
334
335 #[inline]
336 fn neg(self) -> Self {
337 Self::new(self.value.map(A::neg))
338 }
339}
340
341impl<F, A, const D: usize> Add for BinomialExtensionField<F, D, A>
342where
343 F: BinomiallyExtendable<D>,
344 A: ExtensionAlgebra<F, D, Binomial<F>>,
345{
346 type Output = Self;
347
348 #[inline]
349 fn add(self, rhs: Self) -> Self {
350 let value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_add(&self.value, &rhs.value);
351 Self::new(value)
352 }
353}
354
355impl<F, A, const D: usize> Add<A> for BinomialExtensionField<F, D, A>
356where
357 F: BinomiallyExtendable<D>,
358 A: Algebra<F>,
359{
360 type Output = Self;
361
362 #[inline]
363 fn add(mut self, rhs: A) -> Self {
364 self.value[0] += rhs;
365 self
366 }
367}
368
369impl<F, A, const D: usize> AddAssign for BinomialExtensionField<F, D, A>
370where
371 F: BinomiallyExtendable<D>,
372 A: ExtensionAlgebra<F, D, Binomial<F>>,
373{
374 #[inline]
375 fn add_assign(&mut self, rhs: Self) {
376 self.value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_add(&self.value, &rhs.value);
377 }
378}
379
380impl<F, A, const D: usize> AddAssign<A> for BinomialExtensionField<F, D, A>
381where
382 F: BinomiallyExtendable<D>,
383 A: Algebra<F>,
384{
385 #[inline]
386 fn add_assign(&mut self, rhs: A) {
387 self.value[0] += rhs;
388 }
389}
390
391impl<F, A, const D: usize> Sum for BinomialExtensionField<F, D, A>
392where
393 F: BinomiallyExtendable<D>,
394 A: ExtensionAlgebra<F, D, Binomial<F>> + Copy,
395{
396 #[inline]
397 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
398 iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO)
399 }
400}
401
402impl<F, A, const D: usize> Sub for BinomialExtensionField<F, D, A>
403where
404 F: BinomiallyExtendable<D>,
405 A: ExtensionAlgebra<F, D, Binomial<F>>,
406{
407 type Output = Self;
408
409 #[inline]
410 fn sub(self, rhs: Self) -> Self {
411 let value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_sub(&self.value, &rhs.value);
412 Self::new(value)
413 }
414}
415
416impl<F, A, const D: usize> Sub<A> for BinomialExtensionField<F, D, A>
417where
418 F: BinomiallyExtendable<D>,
419 A: Algebra<F>,
420{
421 type Output = Self;
422
423 #[inline]
424 fn sub(self, rhs: A) -> Self {
425 let mut res = self.value;
426 res[0] -= rhs;
427 Self::new(res)
428 }
429}
430
431impl<F, A, const D: usize> SubAssign for BinomialExtensionField<F, D, A>
432where
433 F: BinomiallyExtendable<D>,
434 A: ExtensionAlgebra<F, D, Binomial<F>>,
435{
436 #[inline]
437 fn sub_assign(&mut self, rhs: Self) {
438 self.value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_sub(&self.value, &rhs.value);
439 }
440}
441
442impl<F, A, const D: usize> SubAssign<A> for BinomialExtensionField<F, D, A>
443where
444 F: BinomiallyExtendable<D>,
445 A: Algebra<F>,
446{
447 #[inline]
448 fn sub_assign(&mut self, rhs: A) {
449 self.value[0] -= rhs;
450 }
451}
452
453impl<F, A, const D: usize> Mul for BinomialExtensionField<F, D, A>
454where
455 F: BinomiallyExtendable<D>,
456 A: ExtensionAlgebra<F, D, Binomial<F>>,
457{
458 type Output = Self;
459
460 #[inline]
461 fn mul(self, rhs: Self) -> Self {
462 let a = self.value;
463 let b = rhs.value;
464 let mut res = Self::default();
465
466 <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_mul(&a, &b, &mut res.value);
470
471 res
472 }
473}
474
475impl<F, A, const D: usize> Mul<A> for BinomialExtensionField<F, D, A>
476where
477 F: BinomiallyExtendable<D>,
478 A: ExtensionAlgebra<F, D, Binomial<F>>,
479{
480 type Output = Self;
481
482 #[inline]
483 fn mul(self, rhs: A) -> Self {
484 Self::new(<A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_base_mul(
485 self.value, rhs,
486 ))
487 }
488}
489
490impl<F, A, const D: usize> MulAssign for BinomialExtensionField<F, D, A>
491where
492 F: BinomiallyExtendable<D>,
493 A: ExtensionAlgebra<F, D, Binomial<F>>,
494{
495 #[inline]
496 fn mul_assign(&mut self, rhs: Self) {
497 *self = self.clone() * rhs;
498 }
499}
500
501impl<F, A, const D: usize> MulAssign<A> for BinomialExtensionField<F, D, A>
502where
503 F: BinomiallyExtendable<D>,
504 A: ExtensionAlgebra<F, D, Binomial<F>>,
505{
506 #[inline]
507 fn mul_assign(&mut self, rhs: A) {
508 *self = self.clone() * rhs;
509 }
510}
511
512impl<F, A, const D: usize> Product for BinomialExtensionField<F, D, A>
513where
514 F: BinomiallyExtendable<D>,
515 A: ExtensionAlgebra<F, D, Binomial<F>> + Copy,
516{
517 #[inline]
518 fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
519 iter.reduce(|acc, x| acc * x).unwrap_or(Self::ONE)
520 }
521}
522
523impl<F, const D: usize> Div for BinomialExtensionField<F, D>
524where
525 F: BinomiallyExtendable<D>,
526{
527 type Output = Self;
528
529 #[allow(clippy::suspicious_arithmetic_impl)]
530 #[inline]
531 fn div(self, rhs: Self) -> Self::Output {
532 self * rhs.inverse()
533 }
534}
535
536impl<F, const D: usize> DivAssign for BinomialExtensionField<F, D>
537where
538 F: BinomiallyExtendable<D>,
539{
540 #[inline]
541 fn div_assign(&mut self, rhs: Self) {
542 *self = *self / rhs;
543 }
544}
545
546impl<F: Field + HasTwoAdicBinomialExtension<D>, const D: usize> TwoAdicField
547 for BinomialExtensionField<F, D>
548{
549 const TWO_ADICITY: usize = F::EXT_TWO_ADICITY;
550
551 #[inline]
552 fn two_adic_generator(bits: usize) -> Self {
553 Self::new(F::ext_two_adic_generator(bits))
554 }
555}
556
557#[inline]
559pub fn vector_add<R: PrimeCharacteristicRing + Add<R2, Output = R>, R2: Dup, const D: usize>(
560 a: &[R; D],
561 b: &[R2; D],
562) -> [R; D] {
563 array::from_fn(|i| a[i].dup() + b[i].dup())
564}
565
566#[inline]
568pub fn vector_sub<R: PrimeCharacteristicRing + Sub<R2, Output = R>, R2: Dup, const D: usize>(
569 a: &[R; D],
570 b: &[R2; D],
571) -> [R; D] {
572 array::from_fn(|i| a[i].dup() - b[i].dup())
573}
574
575#[inline]
577pub fn binomial_mul<F: Field, R: Algebra<F> + Algebra<R2>, R2: Algebra<F>, const D: usize>(
578 a: &[R; D],
579 b: &[R2; D],
580 res: &mut [R; D],
581 w: F,
582) {
583 match D {
584 2 => quadratic_mul(a, b, res, w),
585 3 => cubic_mul(a, b, res, w),
586 4 => quartic_mul(a, b, res, w),
587 5 => quintic_mul(a, b, res, w),
588 8 => octic_mul(a, b, res, w),
589 _ => {
590 for (i, a_i) in a.iter().enumerate() {
591 for (j, b_j) in b.iter().enumerate() {
592 if i + j >= D {
593 res[i + j - D] += a_i.dup() * w * b_j.dup();
594 } else {
595 res[i + j] += a_i.dup() * b_j.dup();
596 }
597 }
598 }
599 }
600 }
601}
602
603#[inline]
607pub fn binomial_square<F: Field, R: Algebra<F>, const D: usize>(
608 a: &[R; D],
609 res: &mut [R; D],
610 w: F,
611) {
612 match D {
613 2 => {
614 let a1_w = a[1].dup() * w;
615 res[0] = R::dot_product(a[..].try_into().unwrap(), &[a[0].dup(), a1_w]);
616 res[1] = a[0].dup() * a[1].double();
617 }
618 3 => cubic_square(a, res, w),
619 4 => quartic_square(a, res, w),
620 5 => quintic_square(a, res, w),
621 8 => octic_square(a, res, w),
622 _ => binomial_mul::<F, R, R, D>(a, a, res, w),
623 }
624}
625
626#[inline]
640fn quadratic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
641where
642 F: Field,
643 R: Algebra<F> + Algebra<R2>,
644 R2: Algebra<F>,
645{
646 let b1_w = b[1].dup() * w;
647
648 res[0] = R::dot_product(a[..].try_into().unwrap(), &[b[0].dup().into(), b1_w.into()]);
650
651 res[1] = R::dot_product(
653 &[a[0].dup(), a[1].dup()],
654 &[b[1].dup().into(), b[0].dup().into()],
655 );
656}
657
658#[inline]
660fn quadratic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
661 assert_eq!(D, 2);
662 let neg_a1 = -a[1];
663 let scalar = F::dot_product(&[a[0], neg_a1], &[a[0], w * a[1]]).inverse();
664 res[0] = a[0] * scalar;
665 res[1] = neg_a1 * scalar;
666}
667
668#[inline]
670fn cubic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
671 assert_eq!(D, 3);
672 let a0_square = a[0].square();
673 let a1_square = a[1].square();
674 let a2_w = w * a[2];
675 let a0_a1 = a[0] * a[1];
676
677 let scalar = (a0_square * a[0] + w * a[1] * a1_square + a2_w.square() * a[2]
679 - (F::ONE + F::TWO) * a2_w * a0_a1)
680 .inverse();
681
682 res[0] = scalar * (a0_square - a[1] * a2_w);
684 res[1] = scalar * (a2_w * a[2] - a0_a1);
685 res[2] = scalar * (a1_square - a[0] * a[2]);
686}
687
688#[inline]
690fn cubic_mul<F: Field, R: Algebra<F> + Algebra<R2>, R2: Algebra<F>, const D: usize>(
691 a: &[R; D],
692 b: &[R2; D],
693 res: &mut [R; D],
694 w: F,
695) {
696 assert_eq!(D, 3);
697 let a0_b0 = a[0].dup() * b[0].dup();
701 let a1_b1 = a[1].dup() * b[1].dup();
702 let a2_b2 = a[2].dup() * b[2].dup();
703
704 res[0] = a0_b0.dup()
705 + ((a[1].dup() + a[2].dup()) * (b[1].dup() + b[2].dup()) - a1_b1.dup() - a2_b2.dup()) * w;
706 res[1] = (a[0].dup() + a[1].dup()) * (b[0].dup() + b[1].dup()) - a0_b0.dup() - a1_b1.dup()
707 + a2_b2.dup() * w;
708 res[2] = (a[0].dup() + a[2].dup()) * (b[0].dup() + b[2].dup()) - a0_b0 - a2_b2 + a1_b1;
709}
710
711#[inline]
713fn cubic_square<F: Field, R: Algebra<F>, const D: usize>(a: &[R; D], res: &mut [R; D], w: F) {
714 assert_eq!(D, 3);
715
716 let w_a2 = a[2].dup() * w;
717
718 res[0] = a[0].square() + (a[1].dup() * w_a2.dup()).double();
719 res[1] = w_a2 * a[2].dup() + (a[0].dup() * a[1].dup()).double();
720 res[2] = a[1].square() + (a[0].dup() * a[2].dup()).double();
721}
722
723#[inline]
728pub fn quartic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
729where
730 F: Field,
731 R: Algebra<F> + Algebra<R2>,
732 R2: Algebra<F>,
733{
734 assert_eq!(D, 4);
735 let b_r_rev: [R; 5] = [
736 b[3].dup().into(),
737 b[2].dup().into(),
738 b[1].dup().into(),
739 b[0].dup().into(),
740 w.into(),
741 ];
742
743 let w_coeff_0 =
745 R::dot_product::<3>(a[1..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
746 res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[3..].try_into().unwrap());
747
748 let w_coeff_1 =
750 R::dot_product::<2>(a[2..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
751 res[1] = R::dot_product(
752 &[a[0].dup(), a[1].dup(), w_coeff_1],
753 b_r_rev[2..].try_into().unwrap(),
754 );
755
756 let b3_w = b[3].dup() * w;
758 res[2] = R::dot_product::<4>(
759 a[..4].try_into().unwrap(),
760 &[
761 b_r_rev[1].dup(),
762 b_r_rev[2].dup(),
763 b_r_rev[3].dup(),
764 b3_w.into(),
765 ],
766 );
767
768 res[3] = R::dot_product::<4>(a[..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
770}
771
772#[inline]
774fn quartic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
775 assert_eq!(D, 4);
776
777 let neg_a1 = -a[1];
789 let a3_w = a[3] * w;
790 let norm_0 = F::dot_product(&[a[0], a[2], neg_a1.double()], &[a[0], a[2] * w, a3_w]);
791 let norm_1 = F::dot_product(&[a[0], a[1], -a[3]], &[a[2].double(), neg_a1, a3_w]);
792
793 let mut inv = [F::ZERO; 2];
795 quadratic_inv(&[norm_0, norm_1], &mut inv, w);
796
797 let mut out_evn = [F::ZERO; 2];
802 let mut out_odd = [F::ZERO; 2];
803 quadratic_mul(&[a[0], a[2]], &inv, &mut out_evn, w);
804 quadratic_mul(&[a[1], a[3]], &inv, &mut out_odd, w);
805
806 res[0] = out_evn[0];
807 res[1] = -out_odd[0];
808 res[2] = out_evn[1];
809 res[3] = -out_odd[1];
810}
811
812#[inline]
817fn quartic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
818where
819 F: Field,
820 R: Algebra<F>,
821{
822 assert_eq!(D, 4);
823
824 let two_a0 = a[0].double();
825 let two_a1 = a[1].double();
826 let two_a2 = a[2].double();
827 let a2_w = a[2].dup() * w;
828 let a3_w = a[3].dup() * w;
829
830 res[0] = R::dot_product(
832 &[a[0].dup(), a2_w, two_a1],
833 &[a[0].dup(), a[2].dup(), a3_w.dup()],
834 );
835
836 res[1] = R::dot_product(&[two_a0.dup(), two_a2.dup()], &[a[1].dup(), a3_w.dup()]);
838
839 res[2] = R::dot_product(
841 &[a[1].dup(), a3_w, two_a0.dup()],
842 &[a[1].dup(), a[3].dup(), a[2].dup()],
843 );
844
845 res[3] = R::dot_product(&[two_a0, two_a2], &[a[3].dup(), a[1].dup()]);
847}
848
849pub fn quintic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
854where
855 F: Field,
856 R: Algebra<F> + Algebra<R2>,
857 R2: Algebra<F>,
858{
859 assert_eq!(D, 5);
860 let b_r_rev: [R; 6] = [
861 b[4].dup().into(),
862 b[3].dup().into(),
863 b[2].dup().into(),
864 b[1].dup().into(),
865 b[0].dup().into(),
866 w.into(),
867 ];
868
869 let w_coeff_0 =
871 R::dot_product::<4>(a[1..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
872 res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[4..].try_into().unwrap());
873
874 let w_coeff_1 =
876 R::dot_product::<3>(a[2..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
877 res[1] = R::dot_product(
878 &[a[0].dup(), a[1].dup(), w_coeff_1],
879 b_r_rev[3..].try_into().unwrap(),
880 );
881
882 let w_coeff_2 =
884 R::dot_product::<2>(a[3..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
885 res[2] = R::dot_product(
886 &[a[0].dup(), a[1].dup(), a[2].dup(), w_coeff_2],
887 b_r_rev[2..].try_into().unwrap(),
888 );
889
890 let b4_w = b[4].dup() * w;
892 res[3] = R::dot_product::<5>(
893 a[..5].try_into().unwrap(),
894 &[
895 b_r_rev[1].dup(),
896 b_r_rev[2].dup(),
897 b_r_rev[3].dup(),
898 b_r_rev[4].dup(),
899 b4_w.into(),
900 ],
901 );
902
903 res[4] = R::dot_product::<5>(a[..].try_into().unwrap(), b_r_rev[..5].try_into().unwrap());
905}
906
907#[inline]
912fn quintic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
913where
914 F: Field,
915 R: Algebra<F>,
916{
917 assert_eq!(D, 5);
918
919 let two_a0 = a[0].double();
920 let two_a1 = a[1].double();
921 let two_a2 = a[2].double();
922 let two_a3 = a[3].double();
923 let w_a3 = a[3].dup() * w;
924 let w_a4 = a[4].dup() * w;
925
926 res[0] = R::dot_product(
928 &[a[0].dup(), w_a4.dup(), w_a3.dup()],
929 &[a[0].dup(), two_a1.dup(), two_a2.dup()],
930 );
931
932 res[1] = R::dot_product(
934 &[w_a3, two_a0.dup(), w_a4.dup()],
935 &[a[3].dup(), a[1].dup(), two_a2],
936 );
937
938 res[2] = R::dot_product(
940 &[a[1].dup(), two_a0.dup(), w_a4.dup()],
941 &[a[1].dup(), a[2].dup(), two_a3],
942 );
943
944 res[3] = R::dot_product(
946 &[w_a4, two_a0.dup(), two_a1.dup()],
947 &[a[4].dup(), a[3].dup(), a[2].dup()],
948 );
949
950 res[4] = R::dot_product(
952 &[a[2].dup(), two_a0, two_a1],
953 &[a[2].dup(), a[4].dup(), a[3].dup()],
954 );
955}
956
957#[inline]
962fn octic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
963where
964 F: Field,
965 R: Algebra<F>,
966{
967 assert_eq!(D, 8);
968
969 let a0_2 = a[0].double();
970 let a1_2 = a[1].double();
971 let a2_2 = a[2].double();
972 let a3_2 = a[3].double();
973 let w_a4 = a[4].dup() * w;
974 let w_a5 = a[5].dup() * w;
975 let w_a6 = a[6].dup() * w;
976 let w_a7 = a[7].dup() * w;
977 let w_a5_2 = w_a5.double();
978 let w_a6_2 = w_a6.double();
979 let w_a7_2 = w_a7.double();
980
981 res[0] = R::dot_product(
983 &[a[0].dup(), a[1].dup(), a[2].dup(), a[3].dup(), a[4].dup()],
984 &[a[0].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2.dup(), w_a4],
985 );
986
987 res[1] = R::dot_product(
989 &[a0_2.dup(), a[2].dup(), a[3].dup(), a[4].dup()],
990 &[a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2],
991 );
992
993 res[2] = R::dot_product(
995 &[a0_2.dup(), a[1].dup(), a[3].dup(), a[4].dup(), a[5].dup()],
996 &[a[2].dup(), a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5],
997 );
998
999 res[3] = R::dot_product(
1001 &[a0_2.dup(), a1_2.dup(), a[4].dup(), a[5].dup()],
1002 &[a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6_2],
1003 );
1004
1005 res[4] = R::dot_product(
1007 &[a0_2.dup(), a1_2.dup(), a[2].dup(), a[5].dup(), a[6].dup()],
1008 &[a[4].dup(), a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6],
1009 );
1010
1011 res[5] = R::dot_product(
1013 &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[6].dup()],
1014 &[a[5].dup(), a[4].dup(), a[3].dup(), w_a7_2],
1015 );
1016
1017 res[6] = R::dot_product(
1019 &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[3].dup(), a[7].dup()],
1020 &[a[6].dup(), a[5].dup(), a[4].dup(), a[3].dup(), w_a7],
1021 );
1022
1023 res[7] = R::dot_product(
1025 &[a0_2, a1_2, a2_2, a3_2],
1026 &[a[7].dup(), a[6].dup(), a[5].dup(), a[4].dup()],
1027 );
1028}
1029
1030#[inline]
1032fn quintic_inv<F: BinomiallyExtendable<D>, const D: usize>(
1033 a: &BinomialExtensionField<F, D>,
1034) -> BinomialExtensionField<F, D> {
1035 let a_exp_q = a.frobenius();
1037 let a_exp_q_plus_q_sq = (*a * a_exp_q).frobenius();
1038 let prod_conj = a_exp_q_plus_q_sq * a_exp_q_plus_q_sq.repeated_frobenius(2);
1039
1040 let a_vals = a.value;
1043 let mut b = prod_conj.value;
1044 b.reverse();
1045
1046 let w_coeff = F::dot_product::<4>(a.value[1..].try_into().unwrap(), b[..4].try_into().unwrap());
1047 let norm = F::dot_product::<2>(&[a_vals[0], F::W], &[b[4], w_coeff]);
1048 debug_assert_eq!(BinomialExtensionField::<F, D>::from(norm), *a * prod_conj);
1049
1050 prod_conj * norm.inverse()
1051}
1052
1053#[inline]
1062fn compute_coefficient<
1063 F,
1064 R,
1065 const D: usize,
1066 const D_PLUS_1: usize,
1067 const N: usize,
1068 const D_PLUS_1_MIN_N: usize,
1069>(
1070 a: &[R; D],
1071 b_rev: &[R; D_PLUS_1],
1072) -> R
1073where
1074 F: Field,
1075 R: Algebra<F>,
1076{
1077 let w_coeff = R::dot_product::<N>(
1078 a[(D - N)..].try_into().unwrap(),
1079 b_rev[..N].try_into().unwrap(),
1080 );
1081 let mut scratch: [R; D_PLUS_1_MIN_N] = array::from_fn(|i| a[i].dup());
1082 scratch[D_PLUS_1_MIN_N - 1] = w_coeff;
1083 R::dot_product(&scratch, b_rev[N..].try_into().unwrap())
1084}
1085
1086#[inline]
1091pub fn octic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
1092where
1093 F: Field,
1094 R: Algebra<F> + Algebra<R2>,
1095 R2: Algebra<F>,
1096{
1097 assert_eq!(D, 8);
1098 let a: &[R; 8] = a[..].try_into().unwrap();
1099 let mut b_r_rev: [R; 9] = [
1100 b[7].dup().into(),
1101 b[6].dup().into(),
1102 b[5].dup().into(),
1103 b[4].dup().into(),
1104 b[3].dup().into(),
1105 b[2].dup().into(),
1106 b[1].dup().into(),
1107 b[0].dup().into(),
1108 w.into(),
1109 ];
1110
1111 res[0] = compute_coefficient::<F, R, 8, 9, 7, 2>(a, &b_r_rev);
1113
1114 res[1] = compute_coefficient::<F, R, 8, 9, 6, 3>(a, &b_r_rev);
1116
1117 res[2] = compute_coefficient::<F, R, 8, 9, 5, 4>(a, &b_r_rev);
1119
1120 res[3] = compute_coefficient::<F, R, 8, 9, 4, 5>(a, &b_r_rev);
1122
1123 res[4] = compute_coefficient::<F, R, 8, 9, 3, 6>(a, &b_r_rev);
1125
1126 res[5] = compute_coefficient::<F, R, 8, 9, 2, 7>(a, &b_r_rev);
1128
1129 b_r_rev[8] *= b[7].dup();
1131 res[6] = R::dot_product::<8>(a, b_r_rev[1..].try_into().unwrap());
1132
1133 res[7] = R::dot_product::<8>(a, b_r_rev[..8].try_into().unwrap());
1135}
1136
1137#[inline]
1139fn octic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
1140 assert_eq!(D, 8);
1141
1142 let evns = [a[0], a[2], a[4], a[6]];
1157 let odds = [a[1], a[3], a[5], a[7]];
1158 let mut evns_sq = [F::ZERO; 4];
1159 let mut odds_sq = [F::ZERO; 4];
1160 quartic_square(&evns, &mut evns_sq, w);
1161 quartic_square(&odds, &mut odds_sq, w);
1162 let norm = [
1164 evns_sq[0] - w * odds_sq[3],
1165 evns_sq[1] - odds_sq[0],
1166 evns_sq[2] - odds_sq[1],
1167 evns_sq[3] - odds_sq[2],
1168 ];
1169
1170 let mut norm_inv = [F::ZERO; 4];
1172 quartic_inv(&norm, &mut norm_inv, w);
1173
1174 let mut out_evn = [F::ZERO; 4];
1180 let mut out_odd = [F::ZERO; 4];
1181 quartic_mul(&evns, &norm_inv, &mut out_evn, w);
1182 quartic_mul(&odds, &norm_inv, &mut out_odd, w);
1183
1184 res[0] = out_evn[0];
1185 res[1] = -out_odd[0];
1186 res[2] = out_evn[1];
1187 res[3] = -out_odd[1];
1188 res[4] = out_evn[2];
1189 res[5] = -out_odd[2];
1190 res[6] = out_evn[3];
1191 res[7] = -out_odd[3];
1192}