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 <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_square(&self.value, &mut res.value);
173 res
174 }
175
176 #[inline]
177 fn mul_2exp_u64(&self, exp: u64) -> Self {
178 Self::new(array::from_fn(|i| self.value[i].mul_2exp_u64(exp)))
181 }
182
183 #[inline]
184 fn div_2exp_u64(&self, exp: u64) -> Self {
185 Self::new(array::from_fn(|i| self.value[i].div_2exp_u64(exp)))
188 }
189
190 #[inline]
191 fn zero_vec(len: usize) -> Vec<Self> {
192 unsafe { reconstitute_from_base(F::zero_vec(len * D)) }
194 }
195}
196
197impl<F: BinomiallyExtendable<D>, const D: usize> Algebra<F> for BinomialExtensionField<F, D> {}
198
199impl<F: BinomiallyExtendable<D>, const D: usize> RawDataSerializable
200 for BinomialExtensionField<F, D>
201{
202 const NUM_BYTES: usize = F::NUM_BYTES * D;
203
204 #[inline]
205 fn into_bytes(self) -> impl IntoIterator<Item = u8> {
206 self.value.into_iter().flat_map(|x| x.into_bytes())
207 }
208
209 #[inline]
210 fn into_byte_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u8> {
211 F::into_byte_stream(input.into_iter().flat_map(|x| x.value))
212 }
213
214 #[inline]
215 fn into_u32_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u32> {
216 F::into_u32_stream(input.into_iter().flat_map(|x| x.value))
217 }
218
219 #[inline]
220 fn into_u64_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u64> {
221 F::into_u64_stream(input.into_iter().flat_map(|x| x.value))
222 }
223
224 #[inline]
225 fn into_parallel_byte_streams<const N: usize>(
226 input: impl IntoIterator<Item = [Self; N]>,
227 ) -> impl IntoIterator<Item = [u8; N]> {
228 F::into_parallel_byte_streams(
229 input
230 .into_iter()
231 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
232 )
233 }
234
235 #[inline]
236 fn into_parallel_u32_streams<const N: usize>(
237 input: impl IntoIterator<Item = [Self; N]>,
238 ) -> impl IntoIterator<Item = [u32; N]> {
239 F::into_parallel_u32_streams(
240 input
241 .into_iter()
242 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
243 )
244 }
245
246 #[inline]
247 fn into_parallel_u64_streams<const N: usize>(
248 input: impl IntoIterator<Item = [Self; N]>,
249 ) -> impl IntoIterator<Item = [u64; N]> {
250 F::into_parallel_u64_streams(
251 input
252 .into_iter()
253 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
254 )
255 }
256}
257
258impl<F: BinomiallyExtendable<D>, const D: usize> Field for BinomialExtensionField<F, D> {
259 type Packing = Self;
260
261 const GENERATOR: Self = Self::new(F::EXT_GENERATOR);
262
263 fn try_inverse(&self) -> Option<Self> {
264 if self.is_zero() {
265 return None;
266 }
267
268 let mut res = Self::default();
269
270 match D {
271 2 => quadratic_inv(&self.value, &mut res.value, F::W),
272 3 => cubic_inv(&self.value, &mut res.value, F::W),
273 4 => quartic_inv(&self.value, &mut res.value, F::W),
274 5 => res = quintic_inv(self),
275 8 => octic_inv(&self.value, &mut res.value, F::W),
276 _ => res = self.pseudo_inv(),
277 }
278
279 Some(res)
280 }
281
282 #[inline]
283 fn add_slices(slice_1: &mut [Self], slice_2: &[Self]) {
284 unsafe {
288 let base_slice_1 = as_base_slice_mut(slice_1);
289 let base_slice_2 = as_base_slice(slice_2);
290
291 F::add_slices(base_slice_1, base_slice_2);
292 }
293 }
294
295 #[inline]
296 fn order() -> BigUint {
297 F::order().pow(D as u32)
298 }
299}
300
301impl<F, const D: usize> Display for BinomialExtensionField<F, D>
302where
303 F: BinomiallyExtendable<D>,
304{
305 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
306 if self.is_zero() {
307 write!(f, "0")
308 } else {
309 let str = self
310 .value
311 .iter()
312 .enumerate()
313 .filter(|(_, x)| !x.is_zero())
314 .map(|(i, x)| match (i, x.is_one()) {
315 (0, _) => format!("{x}"),
316 (1, true) => "X".to_string(),
317 (1, false) => format!("{x} X"),
318 (_, true) => format!("X^{i}"),
319 (_, false) => format!("{x} X^{i}"),
320 })
321 .join(" + ");
322 write!(f, "{str}")
323 }
324 }
325}
326
327impl<F, A, const D: usize> Neg for BinomialExtensionField<F, D, A>
328where
329 F: BinomiallyExtendable<D>,
330 A: Algebra<F>,
331{
332 type Output = Self;
333
334 #[inline]
335 fn neg(self) -> Self {
336 Self::new(self.value.map(A::neg))
337 }
338}
339
340impl<F, A, const D: usize> Add for BinomialExtensionField<F, D, A>
341where
342 F: BinomiallyExtendable<D>,
343 A: ExtensionAlgebra<F, D, Binomial<F>>,
344{
345 type Output = Self;
346
347 #[inline]
348 fn add(self, rhs: Self) -> Self {
349 let value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_add(&self.value, &rhs.value);
350 Self::new(value)
351 }
352}
353
354impl<F, A, const D: usize> Add<A> for BinomialExtensionField<F, D, A>
355where
356 F: BinomiallyExtendable<D>,
357 A: Algebra<F>,
358{
359 type Output = Self;
360
361 #[inline]
362 fn add(mut self, rhs: A) -> Self {
363 self.value[0] += rhs;
364 self
365 }
366}
367
368impl<F, A, const D: usize> AddAssign for BinomialExtensionField<F, D, A>
369where
370 F: BinomiallyExtendable<D>,
371 A: ExtensionAlgebra<F, D, Binomial<F>>,
372{
373 #[inline]
374 fn add_assign(&mut self, rhs: Self) {
375 self.value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_add(&self.value, &rhs.value);
376 }
377}
378
379impl<F, A, const D: usize> AddAssign<A> for BinomialExtensionField<F, D, A>
380where
381 F: BinomiallyExtendable<D>,
382 A: Algebra<F>,
383{
384 #[inline]
385 fn add_assign(&mut self, rhs: A) {
386 self.value[0] += rhs;
387 }
388}
389
390impl<F, A, const D: usize> Sum for BinomialExtensionField<F, D, A>
391where
392 F: BinomiallyExtendable<D>,
393 A: ExtensionAlgebra<F, D, Binomial<F>> + Copy,
394{
395 #[inline]
396 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
397 iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO)
398 }
399}
400
401impl<F, A, const D: usize> Sub for BinomialExtensionField<F, D, A>
402where
403 F: BinomiallyExtendable<D>,
404 A: ExtensionAlgebra<F, D, Binomial<F>>,
405{
406 type Output = Self;
407
408 #[inline]
409 fn sub(self, rhs: Self) -> Self {
410 let value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_sub(&self.value, &rhs.value);
411 Self::new(value)
412 }
413}
414
415impl<F, A, const D: usize> Sub<A> for BinomialExtensionField<F, D, A>
416where
417 F: BinomiallyExtendable<D>,
418 A: Algebra<F>,
419{
420 type Output = Self;
421
422 #[inline]
423 fn sub(self, rhs: A) -> Self {
424 let mut res = self.value;
425 res[0] -= rhs;
426 Self::new(res)
427 }
428}
429
430impl<F, A, const D: usize> SubAssign for BinomialExtensionField<F, D, A>
431where
432 F: BinomiallyExtendable<D>,
433 A: ExtensionAlgebra<F, D, Binomial<F>>,
434{
435 #[inline]
436 fn sub_assign(&mut self, rhs: Self) {
437 self.value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_sub(&self.value, &rhs.value);
438 }
439}
440
441impl<F, A, const D: usize> SubAssign<A> for BinomialExtensionField<F, D, A>
442where
443 F: BinomiallyExtendable<D>,
444 A: Algebra<F>,
445{
446 #[inline]
447 fn sub_assign(&mut self, rhs: A) {
448 self.value[0] -= rhs;
449 }
450}
451
452impl<F, A, const D: usize> Mul for BinomialExtensionField<F, D, A>
453where
454 F: BinomiallyExtendable<D>,
455 A: ExtensionAlgebra<F, D, Binomial<F>>,
456{
457 type Output = Self;
458
459 #[inline]
460 fn mul(self, rhs: Self) -> Self {
461 let a = self.value;
462 let b = rhs.value;
463 let mut res = Self::default();
464
465 <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_mul(&a, &b, &mut res.value);
469
470 res
471 }
472}
473
474impl<F, A, const D: usize> Mul<A> for BinomialExtensionField<F, D, A>
475where
476 F: BinomiallyExtendable<D>,
477 A: ExtensionAlgebra<F, D, Binomial<F>>,
478{
479 type Output = Self;
480
481 #[inline]
482 fn mul(self, rhs: A) -> Self {
483 Self::new(<A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_base_mul(
484 self.value, rhs,
485 ))
486 }
487}
488
489impl<F, A, const D: usize> MulAssign for BinomialExtensionField<F, D, A>
490where
491 F: BinomiallyExtendable<D>,
492 A: ExtensionAlgebra<F, D, Binomial<F>>,
493{
494 #[inline]
495 fn mul_assign(&mut self, rhs: Self) {
496 *self = self.clone() * rhs;
497 }
498}
499
500impl<F, A, const D: usize> MulAssign<A> for BinomialExtensionField<F, D, A>
501where
502 F: BinomiallyExtendable<D>,
503 A: ExtensionAlgebra<F, D, Binomial<F>>,
504{
505 #[inline]
506 fn mul_assign(&mut self, rhs: A) {
507 *self = self.clone() * rhs;
508 }
509}
510
511impl<F, A, const D: usize> Product for BinomialExtensionField<F, D, A>
512where
513 F: BinomiallyExtendable<D>,
514 A: ExtensionAlgebra<F, D, Binomial<F>> + Copy,
515{
516 #[inline]
517 fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
518 iter.reduce(|acc, x| acc * x).unwrap_or(Self::ONE)
519 }
520}
521
522impl<F, const D: usize> Div for BinomialExtensionField<F, D>
523where
524 F: BinomiallyExtendable<D>,
525{
526 type Output = Self;
527
528 #[allow(clippy::suspicious_arithmetic_impl)]
529 #[inline]
530 fn div(self, rhs: Self) -> Self::Output {
531 self * rhs.inverse()
532 }
533}
534
535impl<F, const D: usize> DivAssign for BinomialExtensionField<F, D>
536where
537 F: BinomiallyExtendable<D>,
538{
539 #[inline]
540 fn div_assign(&mut self, rhs: Self) {
541 *self = *self / rhs;
542 }
543}
544
545impl<F: Field + HasTwoAdicBinomialExtension<D>, const D: usize> TwoAdicField
546 for BinomialExtensionField<F, D>
547{
548 const TWO_ADICITY: usize = F::EXT_TWO_ADICITY;
549
550 #[inline]
551 fn two_adic_generator(bits: usize) -> Self {
552 Self::new(F::ext_two_adic_generator(bits))
553 }
554}
555
556#[inline]
558pub fn vector_add<R: PrimeCharacteristicRing + Add<R2, Output = R>, R2: Dup, const D: usize>(
559 a: &[R; D],
560 b: &[R2; D],
561) -> [R; D] {
562 array::from_fn(|i| a[i].dup() + b[i].dup())
563}
564
565#[inline]
567pub fn vector_sub<R: PrimeCharacteristicRing + Sub<R2, Output = R>, R2: Dup, const D: usize>(
568 a: &[R; D],
569 b: &[R2; D],
570) -> [R; D] {
571 array::from_fn(|i| a[i].dup() - b[i].dup())
572}
573
574#[inline]
576pub fn binomial_mul<F: Field, R: Algebra<F> + Algebra<R2>, R2: Algebra<F>, const D: usize>(
577 a: &[R; D],
578 b: &[R2; D],
579 res: &mut [R; D],
580 w: F,
581) {
582 match D {
583 2 => quadratic_mul(a, b, res, w),
584 3 => cubic_mul(a, b, res, w),
585 4 => quartic_mul(a, b, res, w),
586 5 => quintic_mul(a, b, res, w),
587 8 => octic_mul(a, b, res, w),
588 _ => {
589 for (i, a_i) in a.iter().enumerate() {
590 for (j, b_j) in b.iter().enumerate() {
591 if i + j >= D {
592 res[i + j - D] += a_i.dup() * w * b_j.dup();
593 } else {
594 res[i + j] += a_i.dup() * b_j.dup();
595 }
596 }
597 }
598 }
599 }
600}
601
602#[inline]
606pub fn binomial_square<F: Field, R: Algebra<F>, const D: usize>(
607 a: &[R; D],
608 res: &mut [R; D],
609 w: F,
610) {
611 match D {
612 2 => {
613 let a1_w = a[1].dup() * w;
614 res[0] = R::dot_product(a[..].try_into().unwrap(), &[a[0].dup(), a1_w]);
615 res[1] = a[0].dup() * a[1].double();
616 }
617 3 => cubic_square(a, res, w),
618 4 => quartic_square(a, res, w),
619 5 => quintic_square(a, res, w),
620 8 => octic_square(a, res, w),
621 _ => binomial_mul::<F, R, R, D>(a, a, res, w),
622 }
623}
624
625#[inline]
639fn quadratic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
640where
641 F: Field,
642 R: Algebra<F> + Algebra<R2>,
643 R2: Algebra<F>,
644{
645 let b1_w = b[1].dup() * w;
646
647 res[0] = R::dot_product(a[..].try_into().unwrap(), &[b[0].dup().into(), b1_w.into()]);
649
650 res[1] = R::dot_product(
652 &[a[0].dup(), a[1].dup()],
653 &[b[1].dup().into(), b[0].dup().into()],
654 );
655}
656
657#[inline]
659fn quadratic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
660 assert_eq!(D, 2);
661 let neg_a1 = -a[1];
662 let scalar = F::dot_product(&[a[0], neg_a1], &[a[0], w * a[1]]).inverse();
663 res[0] = a[0] * scalar;
664 res[1] = neg_a1 * scalar;
665}
666
667#[inline]
669fn cubic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
670 assert_eq!(D, 3);
671 let a0_square = a[0].square();
672 let a1_square = a[1].square();
673 let a2_w = w * a[2];
674 let a0_a1 = a[0] * a[1];
675
676 let scalar = (a0_square * a[0] + w * a[1] * a1_square + a2_w.square() * a[2]
678 - (F::ONE + F::TWO) * a2_w * a0_a1)
679 .inverse();
680
681 res[0] = scalar * (a0_square - a[1] * a2_w);
683 res[1] = scalar * (a2_w * a[2] - a0_a1);
684 res[2] = scalar * (a1_square - a[0] * a[2]);
685}
686
687#[inline]
689fn cubic_mul<F: Field, R: Algebra<F> + Algebra<R2>, R2: Algebra<F>, const D: usize>(
690 a: &[R; D],
691 b: &[R2; D],
692 res: &mut [R; D],
693 w: F,
694) {
695 assert_eq!(D, 3);
696 let a0_b0 = a[0].dup() * b[0].dup();
700 let a1_b1 = a[1].dup() * b[1].dup();
701 let a2_b2 = a[2].dup() * b[2].dup();
702
703 res[0] = a0_b0.dup()
704 + ((a[1].dup() + a[2].dup()) * (b[1].dup() + b[2].dup()) - a1_b1.dup() - a2_b2.dup()) * w;
705 res[1] = (a[0].dup() + a[1].dup()) * (b[0].dup() + b[1].dup()) - a0_b0.dup() - a1_b1.dup()
706 + a2_b2.dup() * w;
707 res[2] = (a[0].dup() + a[2].dup()) * (b[0].dup() + b[2].dup()) - a0_b0 - a2_b2 + a1_b1;
708}
709
710#[inline]
712fn cubic_square<F: Field, R: Algebra<F>, const D: usize>(a: &[R; D], res: &mut [R; D], w: F) {
713 assert_eq!(D, 3);
714
715 let w_a2 = a[2].dup() * w;
716
717 res[0] = a[0].square() + (a[1].dup() * w_a2.dup()).double();
718 res[1] = w_a2 * a[2].dup() + (a[0].dup() * a[1].dup()).double();
719 res[2] = a[1].square() + (a[0].dup() * a[2].dup()).double();
720}
721
722#[inline]
727pub fn quartic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
728where
729 F: Field,
730 R: Algebra<F> + Algebra<R2>,
731 R2: Algebra<F>,
732{
733 assert_eq!(D, 4);
734 let b_r_rev: [R; 5] = [
735 b[3].dup().into(),
736 b[2].dup().into(),
737 b[1].dup().into(),
738 b[0].dup().into(),
739 w.into(),
740 ];
741
742 let w_coeff_0 =
744 R::dot_product::<3>(a[1..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
745 res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[3..].try_into().unwrap());
746
747 let w_coeff_1 =
749 R::dot_product::<2>(a[2..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
750 res[1] = R::dot_product(
751 &[a[0].dup(), a[1].dup(), w_coeff_1],
752 b_r_rev[2..].try_into().unwrap(),
753 );
754
755 let b3_w = b[3].dup() * w;
757 res[2] = R::dot_product::<4>(
758 a[..4].try_into().unwrap(),
759 &[
760 b_r_rev[1].dup(),
761 b_r_rev[2].dup(),
762 b_r_rev[3].dup(),
763 b3_w.into(),
764 ],
765 );
766
767 res[3] = R::dot_product::<4>(a[..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
769}
770
771#[inline]
773fn quartic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
774 assert_eq!(D, 4);
775
776 let neg_a1 = -a[1];
788 let a3_w = a[3] * w;
789 let norm_0 = F::dot_product(&[a[0], a[2], neg_a1.double()], &[a[0], a[2] * w, a3_w]);
790 let norm_1 = F::dot_product(&[a[0], a[1], -a[3]], &[a[2].double(), neg_a1, a3_w]);
791
792 let mut inv = [F::ZERO; 2];
794 quadratic_inv(&[norm_0, norm_1], &mut inv, w);
795
796 let mut out_evn = [F::ZERO; 2];
801 let mut out_odd = [F::ZERO; 2];
802 quadratic_mul(&[a[0], a[2]], &inv, &mut out_evn, w);
803 quadratic_mul(&[a[1], a[3]], &inv, &mut out_odd, w);
804
805 res[0] = out_evn[0];
806 res[1] = -out_odd[0];
807 res[2] = out_evn[1];
808 res[3] = -out_odd[1];
809}
810
811#[inline]
816fn quartic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
817where
818 F: Field,
819 R: Algebra<F>,
820{
821 assert_eq!(D, 4);
822
823 let two_a0 = a[0].double();
824 let two_a1 = a[1].double();
825 let two_a2 = a[2].double();
826 let a2_w = a[2].dup() * w;
827 let a3_w = a[3].dup() * w;
828
829 res[0] = R::dot_product(
831 &[a[0].dup(), a2_w, two_a1],
832 &[a[0].dup(), a[2].dup(), a3_w.dup()],
833 );
834
835 res[1] = R::dot_product(&[two_a0.dup(), two_a2.dup()], &[a[1].dup(), a3_w.dup()]);
837
838 res[2] = R::dot_product(
840 &[a[1].dup(), a3_w, two_a0.dup()],
841 &[a[1].dup(), a[3].dup(), a[2].dup()],
842 );
843
844 res[3] = R::dot_product(&[two_a0, two_a2], &[a[3].dup(), a[1].dup()]);
846}
847
848pub fn quintic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
853where
854 F: Field,
855 R: Algebra<F> + Algebra<R2>,
856 R2: Algebra<F>,
857{
858 assert_eq!(D, 5);
859 let b_r_rev: [R; 6] = [
860 b[4].dup().into(),
861 b[3].dup().into(),
862 b[2].dup().into(),
863 b[1].dup().into(),
864 b[0].dup().into(),
865 w.into(),
866 ];
867
868 let w_coeff_0 =
870 R::dot_product::<4>(a[1..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
871 res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[4..].try_into().unwrap());
872
873 let w_coeff_1 =
875 R::dot_product::<3>(a[2..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
876 res[1] = R::dot_product(
877 &[a[0].dup(), a[1].dup(), w_coeff_1],
878 b_r_rev[3..].try_into().unwrap(),
879 );
880
881 let w_coeff_2 =
883 R::dot_product::<2>(a[3..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
884 res[2] = R::dot_product(
885 &[a[0].dup(), a[1].dup(), a[2].dup(), w_coeff_2],
886 b_r_rev[2..].try_into().unwrap(),
887 );
888
889 let b4_w = b[4].dup() * w;
891 res[3] = R::dot_product::<5>(
892 a[..5].try_into().unwrap(),
893 &[
894 b_r_rev[1].dup(),
895 b_r_rev[2].dup(),
896 b_r_rev[3].dup(),
897 b_r_rev[4].dup(),
898 b4_w.into(),
899 ],
900 );
901
902 res[4] = R::dot_product::<5>(a[..].try_into().unwrap(), b_r_rev[..5].try_into().unwrap());
904}
905
906#[inline]
911fn quintic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
912where
913 F: Field,
914 R: Algebra<F>,
915{
916 assert_eq!(D, 5);
917
918 let two_a0 = a[0].double();
919 let two_a1 = a[1].double();
920 let two_a2 = a[2].double();
921 let two_a3 = a[3].double();
922 let w_a3 = a[3].dup() * w;
923 let w_a4 = a[4].dup() * w;
924
925 res[0] = R::dot_product(
927 &[a[0].dup(), w_a4.dup(), w_a3.dup()],
928 &[a[0].dup(), two_a1.dup(), two_a2.dup()],
929 );
930
931 res[1] = R::dot_product(
933 &[w_a3, two_a0.dup(), w_a4.dup()],
934 &[a[3].dup(), a[1].dup(), two_a2],
935 );
936
937 res[2] = R::dot_product(
939 &[a[1].dup(), two_a0.dup(), w_a4.dup()],
940 &[a[1].dup(), a[2].dup(), two_a3],
941 );
942
943 res[3] = R::dot_product(
945 &[w_a4, two_a0.dup(), two_a1.dup()],
946 &[a[4].dup(), a[3].dup(), a[2].dup()],
947 );
948
949 res[4] = R::dot_product(
951 &[a[2].dup(), two_a0, two_a1],
952 &[a[2].dup(), a[4].dup(), a[3].dup()],
953 );
954}
955
956#[inline]
961fn octic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
962where
963 F: Field,
964 R: Algebra<F>,
965{
966 assert_eq!(D, 8);
967
968 let a0_2 = a[0].double();
969 let a1_2 = a[1].double();
970 let a2_2 = a[2].double();
971 let a3_2 = a[3].double();
972 let w_a4 = a[4].dup() * w;
973 let w_a5 = a[5].dup() * w;
974 let w_a6 = a[6].dup() * w;
975 let w_a7 = a[7].dup() * w;
976 let w_a5_2 = w_a5.double();
977 let w_a6_2 = w_a6.double();
978 let w_a7_2 = w_a7.double();
979
980 res[0] = R::dot_product(
982 &[a[0].dup(), a[1].dup(), a[2].dup(), a[3].dup(), a[4].dup()],
983 &[a[0].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2.dup(), w_a4],
984 );
985
986 res[1] = R::dot_product(
988 &[a0_2.dup(), a[2].dup(), a[3].dup(), a[4].dup()],
989 &[a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2],
990 );
991
992 res[2] = R::dot_product(
994 &[a0_2.dup(), a[1].dup(), a[3].dup(), a[4].dup(), a[5].dup()],
995 &[a[2].dup(), a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5],
996 );
997
998 res[3] = R::dot_product(
1000 &[a0_2.dup(), a1_2.dup(), a[4].dup(), a[5].dup()],
1001 &[a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6_2],
1002 );
1003
1004 res[4] = R::dot_product(
1006 &[a0_2.dup(), a1_2.dup(), a[2].dup(), a[5].dup(), a[6].dup()],
1007 &[a[4].dup(), a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6],
1008 );
1009
1010 res[5] = R::dot_product(
1012 &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[6].dup()],
1013 &[a[5].dup(), a[4].dup(), a[3].dup(), w_a7_2],
1014 );
1015
1016 res[6] = R::dot_product(
1018 &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[3].dup(), a[7].dup()],
1019 &[a[6].dup(), a[5].dup(), a[4].dup(), a[3].dup(), w_a7],
1020 );
1021
1022 res[7] = R::dot_product(
1024 &[a0_2, a1_2, a2_2, a3_2],
1025 &[a[7].dup(), a[6].dup(), a[5].dup(), a[4].dup()],
1026 );
1027}
1028
1029#[inline]
1031fn quintic_inv<F: BinomiallyExtendable<D>, const D: usize>(
1032 a: &BinomialExtensionField<F, D>,
1033) -> BinomialExtensionField<F, D> {
1034 let a_exp_q = a.frobenius();
1036 let a_exp_q_plus_q_sq = (*a * a_exp_q).frobenius();
1037 let prod_conj = a_exp_q_plus_q_sq * a_exp_q_plus_q_sq.repeated_frobenius(2);
1038
1039 let a_vals = a.value;
1042 let mut b = prod_conj.value;
1043 b.reverse();
1044
1045 let w_coeff = F::dot_product::<4>(a.value[1..].try_into().unwrap(), b[..4].try_into().unwrap());
1046 let norm = F::dot_product::<2>(&[a_vals[0], F::W], &[b[4], w_coeff]);
1047 debug_assert_eq!(BinomialExtensionField::<F, D>::from(norm), *a * prod_conj);
1048
1049 prod_conj * norm.inverse()
1050}
1051
1052#[inline]
1061fn compute_coefficient<
1062 F,
1063 R,
1064 const D: usize,
1065 const D_PLUS_1: usize,
1066 const N: usize,
1067 const D_PLUS_1_MIN_N: usize,
1068>(
1069 a: &[R; D],
1070 b_rev: &[R; D_PLUS_1],
1071) -> R
1072where
1073 F: Field,
1074 R: Algebra<F>,
1075{
1076 let w_coeff = R::dot_product::<N>(
1077 a[(D - N)..].try_into().unwrap(),
1078 b_rev[..N].try_into().unwrap(),
1079 );
1080 let mut scratch: [R; D_PLUS_1_MIN_N] = array::from_fn(|i| a[i].dup());
1081 scratch[D_PLUS_1_MIN_N - 1] = w_coeff;
1082 R::dot_product(&scratch, b_rev[N..].try_into().unwrap())
1083}
1084
1085#[inline]
1090pub fn octic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
1091where
1092 F: Field,
1093 R: Algebra<F> + Algebra<R2>,
1094 R2: Algebra<F>,
1095{
1096 assert_eq!(D, 8);
1097 let a: &[R; 8] = a[..].try_into().unwrap();
1098 let mut b_r_rev: [R; 9] = [
1099 b[7].dup().into(),
1100 b[6].dup().into(),
1101 b[5].dup().into(),
1102 b[4].dup().into(),
1103 b[3].dup().into(),
1104 b[2].dup().into(),
1105 b[1].dup().into(),
1106 b[0].dup().into(),
1107 w.into(),
1108 ];
1109
1110 res[0] = compute_coefficient::<F, R, 8, 9, 7, 2>(a, &b_r_rev);
1112
1113 res[1] = compute_coefficient::<F, R, 8, 9, 6, 3>(a, &b_r_rev);
1115
1116 res[2] = compute_coefficient::<F, R, 8, 9, 5, 4>(a, &b_r_rev);
1118
1119 res[3] = compute_coefficient::<F, R, 8, 9, 4, 5>(a, &b_r_rev);
1121
1122 res[4] = compute_coefficient::<F, R, 8, 9, 3, 6>(a, &b_r_rev);
1124
1125 res[5] = compute_coefficient::<F, R, 8, 9, 2, 7>(a, &b_r_rev);
1127
1128 b_r_rev[8] *= b[7].dup();
1130 res[6] = R::dot_product::<8>(a, b_r_rev[1..].try_into().unwrap());
1131
1132 res[7] = R::dot_product::<8>(a, b_r_rev[..8].try_into().unwrap());
1134}
1135
1136#[inline]
1138fn octic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
1139 assert_eq!(D, 8);
1140
1141 let evns = [a[0], a[2], a[4], a[6]];
1156 let odds = [a[1], a[3], a[5], a[7]];
1157 let mut evns_sq = [F::ZERO; 4];
1158 let mut odds_sq = [F::ZERO; 4];
1159 quartic_square(&evns, &mut evns_sq, w);
1160 quartic_square(&odds, &mut odds_sq, w);
1161 let norm = [
1163 evns_sq[0] - w * odds_sq[3],
1164 evns_sq[1] - odds_sq[0],
1165 evns_sq[2] - odds_sq[1],
1166 evns_sq[3] - odds_sq[2],
1167 ];
1168
1169 let mut norm_inv = [F::ZERO; 4];
1171 quartic_inv(&norm, &mut norm_inv, w);
1172
1173 let mut out_evn = [F::ZERO; 4];
1179 let mut out_odd = [F::ZERO; 4];
1180 quartic_mul(&evns, &norm_inv, &mut out_evn, w);
1181 quartic_mul(&odds, &norm_inv, &mut out_odd, w);
1182
1183 res[0] = out_evn[0];
1184 res[1] = -out_odd[0];
1185 res[2] = out_evn[1];
1186 res[3] = -out_odd[1];
1187 res[4] = out_evn[2];
1188 res[5] = -out_odd[2];
1189 res[6] = out_evn[3];
1190 res[7] = -out_odd[3];
1191}