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: BinomiallyExtendable<D>, const D: usize> crate::AlgebraIdentity<F>
27 for BinomialExtensionField<F, D>
28{
29 fn algebra_id() -> Vec<u8> {
30 F::binomial_algebra_id()
31 }
32}
33
34impl<F: Copy, const D: usize> BinomialExtensionField<F, D, F> {
35 #[inline]
42 pub const fn new_array<const N: usize>(input: [[F; D]; N]) -> [Self; N] {
43 const { assert!(N > 0) }
44 let mut output = [Self::new(input[0]); N];
45 let mut i = 1;
46 while i < N {
47 output[i] = Self::new(input[i]);
48 i += 1;
49 }
50 output
51 }
52}
53
54impl<F: BinomiallyExtendable<D>, const D: usize> ExtensionField<F>
55 for BinomialExtensionField<F, D>
56{
57 type ExtensionPacking = PackedBinomialExtensionField<F, F::Packing, D>;
58
59 #[inline]
60 fn is_in_basefield(&self) -> bool {
61 self.value[1..].iter().all(F::is_zero)
62 }
63
64 #[inline]
65 fn as_base(&self) -> Option<F> {
66 <Self as ExtensionField<F>>::is_in_basefield(self).then(|| self.value[0])
67 }
68}
69
70impl<F: BinomiallyExtendable<D>, const D: usize> HasFrobenius<F> for BinomialExtensionField<F, D> {
71 #[inline]
73 fn frobenius(&self) -> Self {
74 let mut res = Self::ZERO;
76 for (i, z) in F::DTH_ROOT.powers().take(D).enumerate() {
77 res.value[i] = self.value[i] * z;
78 }
79
80 res
81 }
82
83 #[inline]
88 fn repeated_frobenius(&self, count: usize) -> Self {
89 if count == 0 {
90 return *self;
91 } else if count >= D {
92 return self.repeated_frobenius(count % D);
95 }
96
97 let z0 = F::DTH_ROOT.exp_u64(count as u64);
99
100 let mut res = Self::ZERO;
101 for (i, z) in z0.powers().take(D).enumerate() {
102 res.value[i] = self.value[i] * z;
103 }
104
105 res
106 }
107
108 #[inline]
114 fn pseudo_inv(&self) -> Self {
115 let mut prod_conj = self.frobenius();
129 for _ in 2..D {
130 prod_conj = (prod_conj * *self).frobenius();
131 }
132
133 let a = self.value;
136 let b = prod_conj.value;
137 let mut w_coeff = F::ZERO;
138 for i in 1..D {
143 w_coeff += a[i] * b[D - i];
144 }
145 let norm = F::dot_product(&[a[0], F::W], &[b[0], w_coeff]);
146 debug_assert_eq!(Self::from(norm), *self * prod_conj);
147
148 prod_conj * norm.inverse()
149 }
150}
151
152impl<F, A, const D: usize> PrimeCharacteristicRing for BinomialExtensionField<F, D, A>
153where
154 F: BinomiallyExtendable<D>,
155 A: ExtensionAlgebra<F, D, Binomial<F>> + Copy,
156{
157 type PrimeSubfield = <A as PrimeCharacteristicRing>::PrimeSubfield;
158
159 const ZERO: Self = Self::new([A::ZERO; D]);
160
161 const ONE: Self = Self::new(field_to_array(A::ONE));
162
163 const TWO: Self = Self::new(field_to_array(A::TWO));
164
165 const NEG_ONE: Self = Self::new(field_to_array(A::NEG_ONE));
166
167 #[inline]
168 fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
169 <A as PrimeCharacteristicRing>::from_prime_subfield(f).into()
170 }
171
172 #[inline]
173 fn halve(&self) -> Self {
174 Self::new(array::from_fn(|i| self.value[i].halve()))
175 }
176
177 #[inline(always)]
178 fn square(&self) -> Self {
179 let mut res = Self::default();
180 <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_square(&self.value, &mut res.value);
181 res
182 }
183
184 #[inline]
185 fn mul_2exp_u64(&self, exp: u64) -> Self {
186 Self::new(array::from_fn(|i| self.value[i].mul_2exp_u64(exp)))
189 }
190
191 #[inline]
192 fn div_2exp_u64(&self, exp: u64) -> Self {
193 Self::new(array::from_fn(|i| self.value[i].div_2exp_u64(exp)))
196 }
197
198 #[inline]
199 fn zero_vec(len: usize) -> Vec<Self> {
200 unsafe { reconstitute_from_base(F::zero_vec(len * D)) }
202 }
203}
204
205impl<F: BinomiallyExtendable<D>, const D: usize> Algebra<F> for BinomialExtensionField<F, D> {}
206
207impl<F: BinomiallyExtendable<D>, const D: usize> RawDataSerializable
208 for BinomialExtensionField<F, D>
209{
210 const NUM_BYTES: usize = F::NUM_BYTES * D;
211
212 #[inline]
213 fn into_bytes(self) -> impl IntoIterator<Item = u8> {
214 self.value.into_iter().flat_map(|x| x.into_bytes())
215 }
216
217 #[inline]
218 fn into_byte_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u8> {
219 F::into_byte_stream(input.into_iter().flat_map(|x| x.value))
220 }
221
222 #[inline]
223 fn into_u32_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u32> {
224 F::into_u32_stream(input.into_iter().flat_map(|x| x.value))
225 }
226
227 #[inline]
228 fn into_u64_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u64> {
229 F::into_u64_stream(input.into_iter().flat_map(|x| x.value))
230 }
231
232 #[inline]
233 fn into_parallel_byte_streams<const N: usize>(
234 input: impl IntoIterator<Item = [Self; N]>,
235 ) -> impl IntoIterator<Item = [u8; N]> {
236 F::into_parallel_byte_streams(
237 input
238 .into_iter()
239 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
240 )
241 }
242
243 #[inline]
244 fn into_parallel_u32_streams<const N: usize>(
245 input: impl IntoIterator<Item = [Self; N]>,
246 ) -> impl IntoIterator<Item = [u32; N]> {
247 F::into_parallel_u32_streams(
248 input
249 .into_iter()
250 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
251 )
252 }
253
254 #[inline]
255 fn into_parallel_u64_streams<const N: usize>(
256 input: impl IntoIterator<Item = [Self; N]>,
257 ) -> impl IntoIterator<Item = [u64; N]> {
258 F::into_parallel_u64_streams(
259 input
260 .into_iter()
261 .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
262 )
263 }
264}
265
266impl<F: BinomiallyExtendable<D>, const D: usize> Field for BinomialExtensionField<F, D> {
267 type Packing = Self;
268
269 const GENERATOR: Self = Self::new(F::EXT_GENERATOR);
270
271 fn try_inverse(&self) -> Option<Self> {
272 if self.is_zero() {
273 return None;
274 }
275
276 let mut res = Self::default();
277
278 match D {
279 2 => quadratic_inv(&self.value, &mut res.value, F::W),
280 3 => cubic_inv(&self.value, &mut res.value, F::W),
281 4 => quartic_inv(&self.value, &mut res.value, F::W),
282 5 => res = quintic_inv(self),
283 8 => octic_inv(&self.value, &mut res.value, F::W),
284 _ => res = self.pseudo_inv(),
285 }
286
287 Some(res)
288 }
289
290 #[inline]
291 fn add_slices(slice_1: &mut [Self], slice_2: &[Self]) {
292 unsafe {
296 let base_slice_1 = as_base_slice_mut(slice_1);
297 let base_slice_2 = as_base_slice(slice_2);
298
299 F::add_slices(base_slice_1, base_slice_2);
300 }
301 }
302
303 #[inline]
304 fn order() -> BigUint {
305 F::order().pow(D as u32)
306 }
307}
308
309impl<F, const D: usize> Display for BinomialExtensionField<F, D>
310where
311 F: BinomiallyExtendable<D>,
312{
313 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
314 if self.is_zero() {
315 write!(f, "0")
316 } else {
317 let str = self
318 .value
319 .iter()
320 .enumerate()
321 .filter(|(_, x)| !x.is_zero())
322 .map(|(i, x)| match (i, x.is_one()) {
323 (0, _) => format!("{x}"),
324 (1, true) => "X".to_string(),
325 (1, false) => format!("{x} X"),
326 (_, true) => format!("X^{i}"),
327 (_, false) => format!("{x} X^{i}"),
328 })
329 .join(" + ");
330 write!(f, "{str}")
331 }
332 }
333}
334
335impl<F, A, const D: usize> Neg for BinomialExtensionField<F, D, A>
336where
337 F: BinomiallyExtendable<D>,
338 A: Algebra<F>,
339{
340 type Output = Self;
341
342 #[inline]
343 fn neg(self) -> Self {
344 Self::new(self.value.map(A::neg))
345 }
346}
347
348impl<F, A, const D: usize> Add for BinomialExtensionField<F, D, A>
349where
350 F: BinomiallyExtendable<D>,
351 A: ExtensionAlgebra<F, D, Binomial<F>>,
352{
353 type Output = Self;
354
355 #[inline]
356 fn add(self, rhs: Self) -> Self {
357 let value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_add(&self.value, &rhs.value);
358 Self::new(value)
359 }
360}
361
362impl<F, A, const D: usize> Add<A> for BinomialExtensionField<F, D, A>
363where
364 F: BinomiallyExtendable<D>,
365 A: Algebra<F>,
366{
367 type Output = Self;
368
369 #[inline]
370 fn add(mut self, rhs: A) -> Self {
371 self.value[0] += rhs;
372 self
373 }
374}
375
376impl<F, A, const D: usize> AddAssign for BinomialExtensionField<F, D, A>
377where
378 F: BinomiallyExtendable<D>,
379 A: ExtensionAlgebra<F, D, Binomial<F>>,
380{
381 #[inline]
382 fn add_assign(&mut self, rhs: Self) {
383 self.value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_add(&self.value, &rhs.value);
384 }
385}
386
387impl<F, A, const D: usize> AddAssign<A> for BinomialExtensionField<F, D, A>
388where
389 F: BinomiallyExtendable<D>,
390 A: Algebra<F>,
391{
392 #[inline]
393 fn add_assign(&mut self, rhs: A) {
394 self.value[0] += rhs;
395 }
396}
397
398impl<F, A, const D: usize> Sum for BinomialExtensionField<F, D, A>
399where
400 F: BinomiallyExtendable<D>,
401 A: ExtensionAlgebra<F, D, Binomial<F>> + Copy,
402{
403 #[inline]
404 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
405 iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO)
406 }
407}
408
409impl<F, A, const D: usize> Sub for BinomialExtensionField<F, D, A>
410where
411 F: BinomiallyExtendable<D>,
412 A: ExtensionAlgebra<F, D, Binomial<F>>,
413{
414 type Output = Self;
415
416 #[inline]
417 fn sub(self, rhs: Self) -> Self {
418 let value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_sub(&self.value, &rhs.value);
419 Self::new(value)
420 }
421}
422
423impl<F, A, const D: usize> Sub<A> for BinomialExtensionField<F, D, A>
424where
425 F: BinomiallyExtendable<D>,
426 A: Algebra<F>,
427{
428 type Output = Self;
429
430 #[inline]
431 fn sub(self, rhs: A) -> Self {
432 let mut res = self.value;
433 res[0] -= rhs;
434 Self::new(res)
435 }
436}
437
438impl<F, A, const D: usize> SubAssign for BinomialExtensionField<F, D, A>
439where
440 F: BinomiallyExtendable<D>,
441 A: ExtensionAlgebra<F, D, Binomial<F>>,
442{
443 #[inline]
444 fn sub_assign(&mut self, rhs: Self) {
445 self.value = <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_sub(&self.value, &rhs.value);
446 }
447}
448
449impl<F, A, const D: usize> SubAssign<A> for BinomialExtensionField<F, D, A>
450where
451 F: BinomiallyExtendable<D>,
452 A: Algebra<F>,
453{
454 #[inline]
455 fn sub_assign(&mut self, rhs: A) {
456 self.value[0] -= rhs;
457 }
458}
459
460impl<F, A, const D: usize> Mul for BinomialExtensionField<F, D, A>
461where
462 F: BinomiallyExtendable<D>,
463 A: ExtensionAlgebra<F, D, Binomial<F>>,
464{
465 type Output = Self;
466
467 #[inline]
468 fn mul(self, rhs: Self) -> Self {
469 let a = self.value;
470 let b = rhs.value;
471 let mut res = Self::default();
472
473 <A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_mul(&a, &b, &mut res.value);
474
475 res
476 }
477}
478
479impl<F, A, const D: usize> Mul<A> for BinomialExtensionField<F, D, A>
480where
481 F: BinomiallyExtendable<D>,
482 A: ExtensionAlgebra<F, D, Binomial<F>>,
483{
484 type Output = Self;
485
486 #[inline]
487 fn mul(self, rhs: A) -> Self {
488 Self::new(<A as ExtensionAlgebra<F, D, Binomial<F>>>::ext_base_mul(
489 self.value, rhs,
490 ))
491 }
492}
493
494impl<F, A, const D: usize> MulAssign for BinomialExtensionField<F, D, A>
495where
496 F: BinomiallyExtendable<D>,
497 A: ExtensionAlgebra<F, D, Binomial<F>>,
498{
499 #[inline]
500 fn mul_assign(&mut self, rhs: Self) {
501 *self = self.clone() * rhs;
502 }
503}
504
505impl<F, A, const D: usize> MulAssign<A> for BinomialExtensionField<F, D, A>
506where
507 F: BinomiallyExtendable<D>,
508 A: ExtensionAlgebra<F, D, Binomial<F>>,
509{
510 #[inline]
511 fn mul_assign(&mut self, rhs: A) {
512 *self = self.clone() * rhs;
513 }
514}
515
516impl<F, A, const D: usize> Product for BinomialExtensionField<F, D, A>
517where
518 F: BinomiallyExtendable<D>,
519 A: ExtensionAlgebra<F, D, Binomial<F>> + Copy,
520{
521 #[inline]
522 fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
523 iter.reduce(|acc, x| acc * x).unwrap_or(Self::ONE)
524 }
525}
526
527impl<F, const D: usize> Div for BinomialExtensionField<F, D>
528where
529 F: BinomiallyExtendable<D>,
530{
531 type Output = Self;
532
533 #[allow(clippy::suspicious_arithmetic_impl)]
534 #[inline]
535 fn div(self, rhs: Self) -> Self::Output {
536 self * rhs.inverse()
537 }
538}
539
540impl<F, const D: usize> DivAssign for BinomialExtensionField<F, D>
541where
542 F: BinomiallyExtendable<D>,
543{
544 #[inline]
545 fn div_assign(&mut self, rhs: Self) {
546 *self = *self / rhs;
547 }
548}
549
550impl<F: Field + HasTwoAdicBinomialExtension<D>, const D: usize> TwoAdicField
551 for BinomialExtensionField<F, D>
552{
553 const TWO_ADICITY: usize = F::EXT_TWO_ADICITY;
554
555 #[inline]
556 fn two_adic_generator(bits: usize) -> Self {
557 Self::new(F::ext_two_adic_generator(bits))
558 }
559}
560
561#[inline]
563pub fn vector_add<R: PrimeCharacteristicRing + Add<R2, Output = R>, R2: Dup, const D: usize>(
564 a: &[R; D],
565 b: &[R2; D],
566) -> [R; D] {
567 array::from_fn(|i| a[i].dup() + b[i].dup())
568}
569
570#[inline]
572pub fn vector_sub<R: PrimeCharacteristicRing + Sub<R2, Output = R>, R2: Dup, const D: usize>(
573 a: &[R; D],
574 b: &[R2; D],
575) -> [R; D] {
576 array::from_fn(|i| a[i].dup() - b[i].dup())
577}
578
579#[inline]
581pub fn binomial_mul<F: Field, R: Algebra<F> + Algebra<R2>, R2: Algebra<F>, const D: usize>(
582 a: &[R; D],
583 b: &[R2; D],
584 res: &mut [R; D],
585 w: F,
586) {
587 match D {
588 2 => quadratic_mul(a, b, res, w),
589 3 => cubic_mul(a, b, res, w),
590 4 => quartic_mul(a, b, res, w),
591 5 => quintic_mul(a, b, res, w),
592 8 => octic_mul(a, b, res, w),
593 _ => {
594 for (i, a_i) in a.iter().enumerate() {
595 for (j, b_j) in b.iter().enumerate() {
596 if i + j >= D {
597 res[i + j - D] += a_i.dup() * w * b_j.dup();
598 } else {
599 res[i + j] += a_i.dup() * b_j.dup();
600 }
601 }
602 }
603 }
604 }
605}
606
607#[inline]
611pub fn binomial_square<F: Field, R: Algebra<F>, const D: usize>(
612 a: &[R; D],
613 res: &mut [R; D],
614 w: F,
615) {
616 match D {
617 2 => {
618 let [c0, c1] = R::quadratic_extension_square(a[..].try_into().unwrap(), w);
619 res[0] = c0;
620 res[1] = c1;
621 }
622 3 => cubic_square(a, res, w),
623 4 => quartic_square(a, res, w),
624 5 => quintic_square(a, res, w),
625 8 => octic_square(a, res, w),
626 _ => binomial_mul::<F, R, R, D>(a, a, res, w),
627 }
628}
629
630#[inline]
644fn quadratic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
645where
646 F: Field,
647 R: Algebra<F> + Algebra<R2>,
648 R2: Algebra<F>,
649{
650 let b1_w = b[1].dup() * w;
651
652 res[0] = R::dot_product(a[..].try_into().unwrap(), &[b[0].dup().into(), b1_w.into()]);
654
655 res[1] = R::dot_product(
657 &[a[0].dup(), a[1].dup()],
658 &[b[1].dup().into(), b[0].dup().into()],
659 );
660}
661
662#[inline]
664fn quadratic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
665 assert_eq!(D, 2);
666 let neg_a1 = -a[1];
667 let scalar = F::dot_product(&[a[0], neg_a1], &[a[0], w * a[1]]).inverse();
668 res[0] = a[0] * scalar;
669 res[1] = neg_a1 * scalar;
670}
671
672#[inline]
674fn cubic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
675 assert_eq!(D, 3);
676 let a0_square = a[0].square();
677 let a1_square = a[1].square();
678 let a2_w = w * a[2];
679 let a0_a1 = a[0] * a[1];
680
681 let scalar = (a0_square * a[0] + w * a[1] * a1_square + a2_w.square() * a[2]
683 - (F::ONE + F::TWO) * a2_w * a0_a1)
684 .inverse();
685
686 res[0] = scalar * (a0_square - a[1] * a2_w);
688 res[1] = scalar * (a2_w * a[2] - a0_a1);
689 res[2] = scalar * (a1_square - a[0] * a[2]);
690}
691
692#[inline]
694fn cubic_mul<F: Field, R: Algebra<F> + Algebra<R2>, R2: Algebra<F>, const D: usize>(
695 a: &[R; D],
696 b: &[R2; D],
697 res: &mut [R; D],
698 w: F,
699) {
700 assert_eq!(D, 3);
701 let a0_b0 = a[0].dup() * b[0].dup();
705 let a1_b1 = a[1].dup() * b[1].dup();
706 let a2_b2 = a[2].dup() * b[2].dup();
707
708 res[0] = a0_b0.dup()
709 + ((a[1].dup() + a[2].dup()) * (b[1].dup() + b[2].dup()) - a1_b1.dup() - a2_b2.dup()) * w;
710 res[1] = (a[0].dup() + a[1].dup()) * (b[0].dup() + b[1].dup()) - a0_b0.dup() - a1_b1.dup()
711 + a2_b2.dup() * w;
712 res[2] = (a[0].dup() + a[2].dup()) * (b[0].dup() + b[2].dup()) - a0_b0 - a2_b2 + a1_b1;
713}
714
715#[inline]
717fn cubic_square<F: Field, R: Algebra<F>, const D: usize>(a: &[R; D], res: &mut [R; D], w: F) {
718 assert_eq!(D, 3);
719
720 let w_a2 = a[2].dup() * w;
721
722 res[0] = a[0].square() + (a[1].dup() * w_a2.dup()).double();
723 res[1] = w_a2 * a[2].dup() + (a[0].dup() * a[1].dup()).double();
724 res[2] = a[1].square() + (a[0].dup() * a[2].dup()).double();
725}
726
727#[inline]
732pub fn quartic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
733where
734 F: Field,
735 R: Algebra<F> + Algebra<R2>,
736 R2: Algebra<F>,
737{
738 assert_eq!(D, 4);
739 let b_r_rev: [R; 5] = [
740 b[3].dup().into(),
741 b[2].dup().into(),
742 b[1].dup().into(),
743 b[0].dup().into(),
744 w.into(),
745 ];
746
747 let w_coeff_0 =
749 R::dot_product::<3>(a[1..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
750 res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[3..].try_into().unwrap());
751
752 let w_coeff_1 =
754 R::dot_product::<2>(a[2..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
755 res[1] = R::dot_product(
756 &[a[0].dup(), a[1].dup(), w_coeff_1],
757 b_r_rev[2..].try_into().unwrap(),
758 );
759
760 let b3_w = b[3].dup() * w;
762 res[2] = R::dot_product::<4>(
763 a[..4].try_into().unwrap(),
764 &[
765 b_r_rev[1].dup(),
766 b_r_rev[2].dup(),
767 b_r_rev[3].dup(),
768 b3_w.into(),
769 ],
770 );
771
772 res[3] = R::dot_product::<4>(a[..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
774}
775
776#[inline]
778fn quartic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
779 assert_eq!(D, 4);
780
781 let neg_a1 = -a[1];
793 let a3_w = a[3] * w;
794 let norm_0 = F::dot_product(&[a[0], a[2], neg_a1.double()], &[a[0], a[2] * w, a3_w]);
795 let norm_1 = F::dot_product(&[a[0], a[1], -a[3]], &[a[2].double(), neg_a1, a3_w]);
796
797 let mut inv = [F::ZERO; 2];
799 quadratic_inv(&[norm_0, norm_1], &mut inv, w);
800
801 let mut out_evn = [F::ZERO; 2];
806 let mut out_odd = [F::ZERO; 2];
807 quadratic_mul(&[a[0], a[2]], &inv, &mut out_evn, w);
808 quadratic_mul(&[a[1], a[3]], &inv, &mut out_odd, w);
809
810 res[0] = out_evn[0];
811 res[1] = -out_odd[0];
812 res[2] = out_evn[1];
813 res[3] = -out_odd[1];
814}
815
816#[inline]
821fn quartic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
822where
823 F: Field,
824 R: Algebra<F>,
825{
826 assert_eq!(D, 4);
827
828 let two_a0 = a[0].double();
829 let two_a1 = a[1].double();
830 let two_a2 = a[2].double();
831 let a2_w = a[2].dup() * w;
832 let a3_w = a[3].dup() * w;
833
834 res[0] = R::dot_product(
836 &[a[0].dup(), a2_w, two_a1],
837 &[a[0].dup(), a[2].dup(), a3_w.dup()],
838 );
839
840 res[1] = R::dot_product(&[two_a0.dup(), two_a2.dup()], &[a[1].dup(), a3_w.dup()]);
842
843 res[2] = R::dot_product(
845 &[a[1].dup(), a3_w, two_a0.dup()],
846 &[a[1].dup(), a[3].dup(), a[2].dup()],
847 );
848
849 res[3] = R::dot_product(&[two_a0, two_a2], &[a[3].dup(), a[1].dup()]);
851}
852
853pub fn quintic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
858where
859 F: Field,
860 R: Algebra<F> + Algebra<R2>,
861 R2: Algebra<F>,
862{
863 assert_eq!(D, 5);
864 let b_r_rev: [R; 6] = [
865 b[4].dup().into(),
866 b[3].dup().into(),
867 b[2].dup().into(),
868 b[1].dup().into(),
869 b[0].dup().into(),
870 w.into(),
871 ];
872
873 let w_coeff_0 =
875 R::dot_product::<4>(a[1..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
876 res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[4..].try_into().unwrap());
877
878 let w_coeff_1 =
880 R::dot_product::<3>(a[2..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
881 res[1] = R::dot_product(
882 &[a[0].dup(), a[1].dup(), w_coeff_1],
883 b_r_rev[3..].try_into().unwrap(),
884 );
885
886 let w_coeff_2 =
888 R::dot_product::<2>(a[3..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
889 res[2] = R::dot_product(
890 &[a[0].dup(), a[1].dup(), a[2].dup(), w_coeff_2],
891 b_r_rev[2..].try_into().unwrap(),
892 );
893
894 let b4_w = b[4].dup() * w;
896 res[3] = R::dot_product::<5>(
897 a[..5].try_into().unwrap(),
898 &[
899 b_r_rev[1].dup(),
900 b_r_rev[2].dup(),
901 b_r_rev[3].dup(),
902 b_r_rev[4].dup(),
903 b4_w.into(),
904 ],
905 );
906
907 res[4] = R::dot_product::<5>(a[..].try_into().unwrap(), b_r_rev[..5].try_into().unwrap());
909}
910
911#[inline]
916fn quintic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
917where
918 F: Field,
919 R: Algebra<F>,
920{
921 assert_eq!(D, 5);
922
923 let two_a0 = a[0].double();
924 let two_a1 = a[1].double();
925 let two_a2 = a[2].double();
926 let two_a3 = a[3].double();
927 let w_a3 = a[3].dup() * w;
928 let w_a4 = a[4].dup() * w;
929
930 res[0] = R::dot_product(
932 &[a[0].dup(), w_a4.dup(), w_a3.dup()],
933 &[a[0].dup(), two_a1.dup(), two_a2.dup()],
934 );
935
936 res[1] = R::dot_product(
938 &[w_a3, two_a0.dup(), w_a4.dup()],
939 &[a[3].dup(), a[1].dup(), two_a2],
940 );
941
942 res[2] = R::dot_product(
944 &[a[1].dup(), two_a0.dup(), w_a4.dup()],
945 &[a[1].dup(), a[2].dup(), two_a3],
946 );
947
948 res[3] = R::dot_product(
950 &[w_a4, two_a0.dup(), two_a1.dup()],
951 &[a[4].dup(), a[3].dup(), a[2].dup()],
952 );
953
954 res[4] = R::dot_product(
956 &[a[2].dup(), two_a0, two_a1],
957 &[a[2].dup(), a[4].dup(), a[3].dup()],
958 );
959}
960
961#[inline]
966fn octic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
967where
968 F: Field,
969 R: Algebra<F>,
970{
971 assert_eq!(D, 8);
972
973 let a0_2 = a[0].double();
974 let a1_2 = a[1].double();
975 let a2_2 = a[2].double();
976 let a3_2 = a[3].double();
977 let w_a4 = a[4].dup() * w;
978 let w_a5 = a[5].dup() * w;
979 let w_a6 = a[6].dup() * w;
980 let w_a7 = a[7].dup() * w;
981 let w_a5_2 = w_a5.double();
982 let w_a6_2 = w_a6.double();
983 let w_a7_2 = w_a7.double();
984
985 res[0] = R::dot_product(
987 &[a[0].dup(), a[1].dup(), a[2].dup(), a[3].dup(), a[4].dup()],
988 &[a[0].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2.dup(), w_a4],
989 );
990
991 res[1] = R::dot_product(
993 &[a0_2.dup(), a[2].dup(), a[3].dup(), a[4].dup()],
994 &[a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2],
995 );
996
997 res[2] = R::dot_product(
999 &[a0_2.dup(), a[1].dup(), a[3].dup(), a[4].dup(), a[5].dup()],
1000 &[a[2].dup(), a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5],
1001 );
1002
1003 res[3] = R::dot_product(
1005 &[a0_2.dup(), a1_2.dup(), a[4].dup(), a[5].dup()],
1006 &[a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6_2],
1007 );
1008
1009 res[4] = R::dot_product(
1011 &[a0_2.dup(), a1_2.dup(), a[2].dup(), a[5].dup(), a[6].dup()],
1012 &[a[4].dup(), a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6],
1013 );
1014
1015 res[5] = R::dot_product(
1017 &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[6].dup()],
1018 &[a[5].dup(), a[4].dup(), a[3].dup(), w_a7_2],
1019 );
1020
1021 res[6] = R::dot_product(
1023 &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[3].dup(), a[7].dup()],
1024 &[a[6].dup(), a[5].dup(), a[4].dup(), a[3].dup(), w_a7],
1025 );
1026
1027 res[7] = R::dot_product(
1029 &[a0_2, a1_2, a2_2, a3_2],
1030 &[a[7].dup(), a[6].dup(), a[5].dup(), a[4].dup()],
1031 );
1032}
1033
1034#[inline]
1036fn quintic_inv<F: BinomiallyExtendable<D>, const D: usize>(
1037 a: &BinomialExtensionField<F, D>,
1038) -> BinomialExtensionField<F, D> {
1039 let a_exp_q = a.frobenius();
1041 let a_exp_q_plus_q_sq = (*a * a_exp_q).frobenius();
1042 let prod_conj = a_exp_q_plus_q_sq * a_exp_q_plus_q_sq.repeated_frobenius(2);
1043
1044 let a_vals = a.value;
1047 let mut b = prod_conj.value;
1048 b.reverse();
1049
1050 let w_coeff = F::dot_product::<4>(a.value[1..].try_into().unwrap(), b[..4].try_into().unwrap());
1051 let norm = F::dot_product::<2>(&[a_vals[0], F::W], &[b[4], w_coeff]);
1052 debug_assert_eq!(BinomialExtensionField::<F, D>::from(norm), *a * prod_conj);
1053
1054 prod_conj * norm.inverse()
1055}
1056
1057#[inline]
1066fn compute_coefficient<
1067 F,
1068 R,
1069 const D: usize,
1070 const D_PLUS_1: usize,
1071 const N: usize,
1072 const D_PLUS_1_MIN_N: usize,
1073>(
1074 a: &[R; D],
1075 b_rev: &[R; D_PLUS_1],
1076) -> R
1077where
1078 F: Field,
1079 R: Algebra<F>,
1080{
1081 let w_coeff = R::dot_product::<N>(
1082 a[(D - N)..].try_into().unwrap(),
1083 b_rev[..N].try_into().unwrap(),
1084 );
1085 let mut scratch: [R; D_PLUS_1_MIN_N] = array::from_fn(|i| a[i].dup());
1086 scratch[D_PLUS_1_MIN_N - 1] = w_coeff;
1087 R::dot_product(&scratch, b_rev[N..].try_into().unwrap())
1088}
1089
1090#[inline]
1095pub fn octic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
1096where
1097 F: Field,
1098 R: Algebra<F> + Algebra<R2>,
1099 R2: Algebra<F>,
1100{
1101 assert_eq!(D, 8);
1102 let a: &[R; 8] = a[..].try_into().unwrap();
1103 let mut b_r_rev: [R; 9] = [
1104 b[7].dup().into(),
1105 b[6].dup().into(),
1106 b[5].dup().into(),
1107 b[4].dup().into(),
1108 b[3].dup().into(),
1109 b[2].dup().into(),
1110 b[1].dup().into(),
1111 b[0].dup().into(),
1112 w.into(),
1113 ];
1114
1115 res[0] = compute_coefficient::<F, R, 8, 9, 7, 2>(a, &b_r_rev);
1117
1118 res[1] = compute_coefficient::<F, R, 8, 9, 6, 3>(a, &b_r_rev);
1120
1121 res[2] = compute_coefficient::<F, R, 8, 9, 5, 4>(a, &b_r_rev);
1123
1124 res[3] = compute_coefficient::<F, R, 8, 9, 4, 5>(a, &b_r_rev);
1126
1127 res[4] = compute_coefficient::<F, R, 8, 9, 3, 6>(a, &b_r_rev);
1129
1130 res[5] = compute_coefficient::<F, R, 8, 9, 2, 7>(a, &b_r_rev);
1132
1133 b_r_rev[8] *= b[7].dup();
1135 res[6] = R::dot_product::<8>(a, b_r_rev[1..].try_into().unwrap());
1136
1137 res[7] = R::dot_product::<8>(a, b_r_rev[..8].try_into().unwrap());
1139}
1140
1141#[inline]
1143fn octic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
1144 assert_eq!(D, 8);
1145
1146 let evns = [a[0], a[2], a[4], a[6]];
1161 let odds = [a[1], a[3], a[5], a[7]];
1162 let mut evns_sq = [F::ZERO; 4];
1163 let mut odds_sq = [F::ZERO; 4];
1164 quartic_square(&evns, &mut evns_sq, w);
1165 quartic_square(&odds, &mut odds_sq, w);
1166 let norm = [
1168 evns_sq[0] - w * odds_sq[3],
1169 evns_sq[1] - odds_sq[0],
1170 evns_sq[2] - odds_sq[1],
1171 evns_sq[3] - odds_sq[2],
1172 ];
1173
1174 let mut norm_inv = [F::ZERO; 4];
1176 quartic_inv(&norm, &mut norm_inv, w);
1177
1178 let mut out_evn = [F::ZERO; 4];
1184 let mut out_odd = [F::ZERO; 4];
1185 quartic_mul(&evns, &norm_inv, &mut out_evn, w);
1186 quartic_mul(&odds, &norm_inv, &mut out_odd, w);
1187
1188 res[0] = out_evn[0];
1189 res[1] = -out_odd[0];
1190 res[2] = out_evn[1];
1191 res[3] = -out_odd[1];
1192 res[4] = out_evn[2];
1193 res[5] = -out_odd[2];
1194 res[6] = out_evn[3];
1195 res[7] = -out_odd[3];
1196}