Skip to main content

p3_field/extension/
binomial_extension.rs

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)] // Needed to make various casts safe.
27#[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    /// Create an extension field element from an array of base elements.
39    ///
40    /// Any array is accepted. No reduction is required since
41    /// base elements are already valid field elements.
42    #[inline]
43    pub const fn new(value: [A; D]) -> Self {
44        const { assert!(D > 1) }
45        Self {
46            value,
47            _phantom: PhantomData,
48        }
49    }
50}
51
52impl<F: Copy, const D: usize> BinomialExtensionField<F, D, F> {
53    /// Convert a `[[F; D]; N]` array to an array of extension field elements.
54    ///
55    /// Const version of `input.map(BinomialExtensionField::new)`.
56    ///
57    /// # Panics
58    /// Panics if `N == 0`.
59    #[inline]
60    pub const fn new_array<const N: usize>(input: [[F; D]; N]) -> [Self; N] {
61        const { assert!(N > 0) }
62        let mut output = [Self::new(input[0]); N];
63        let mut i = 1;
64        while i < N {
65            output[i] = Self::new(input[i]);
66            i += 1;
67        }
68        output
69    }
70}
71
72impl<F: Field, A: Algebra<F>, const D: usize> Default for BinomialExtensionField<F, D, A> {
73    fn default() -> Self {
74        Self::new(array::from_fn(|_| A::ZERO))
75    }
76}
77
78impl<F: Field, A: Algebra<F>, const D: usize> From<A> for BinomialExtensionField<F, D, A> {
79    fn from(x: A) -> Self {
80        Self::new(field_to_array(x))
81    }
82}
83
84impl<F, A, const D: usize> From<[A; D]> for BinomialExtensionField<F, D, A> {
85    #[inline]
86    fn from(x: [A; D]) -> Self {
87        Self {
88            value: x,
89            _phantom: PhantomData,
90        }
91    }
92}
93
94impl<F: BinomiallyExtendable<D>, const D: usize> Packable for BinomialExtensionField<F, D> {}
95
96impl<F: BinomiallyExtendable<D>, A: Algebra<F>, const D: usize> BasedVectorSpace<A>
97    for BinomialExtensionField<F, D, A>
98{
99    const DIMENSION: usize = D;
100
101    #[inline]
102    fn as_basis_coefficients_slice(&self) -> &[A] {
103        &self.value
104    }
105
106    #[inline]
107    fn from_basis_coefficients_fn<Fn: FnMut(usize) -> A>(f: Fn) -> Self {
108        Self::new(array::from_fn(f))
109    }
110
111    #[inline]
112    fn from_basis_coefficients_iter<I: ExactSizeIterator<Item = A>>(mut iter: I) -> Option<Self> {
113        (iter.len() == D).then(|| Self::new(array::from_fn(|_| iter.next().unwrap()))) // The unwrap is safe as we just checked the length of iter.
114    }
115
116    #[inline]
117    fn flatten_to_base(vec: Vec<Self>) -> Vec<A> {
118        unsafe {
119            // Safety:
120            // As `Self` is a `repr(transparent)`, it is stored identically in memory to `[A; D]`
121            flatten_to_base::<A, Self>(vec)
122        }
123    }
124
125    #[inline]
126    fn reconstitute_from_base(vec: Vec<A>) -> Vec<Self> {
127        unsafe {
128            // Safety:
129            // As `Self` is a `repr(transparent)`, it is stored identically in memory to `[A; D]`
130            reconstitute_from_base::<A, Self>(vec)
131        }
132    }
133}
134
135impl<F: BinomiallyExtendable<D>, const D: usize> ExtensionField<F>
136    for BinomialExtensionField<F, D>
137{
138    type ExtensionPacking = PackedBinomialExtensionField<F, F::Packing, D>;
139
140    #[inline]
141    fn is_in_basefield(&self) -> bool {
142        self.value[1..].iter().all(F::is_zero)
143    }
144
145    #[inline]
146    fn as_base(&self) -> Option<F> {
147        <Self as ExtensionField<F>>::is_in_basefield(self).then(|| self.value[0])
148    }
149}
150
151impl<F: BinomiallyExtendable<D>, const D: usize> HasFrobenius<F> for BinomialExtensionField<F, D> {
152    /// FrobeniusField automorphisms: x -> x^n, where n is the order of BaseField.
153    #[inline]
154    fn frobenius(&self) -> Self {
155        // Slightly faster than self.repeated_frobenius(1)
156        let mut res = Self::ZERO;
157        for (i, z) in F::DTH_ROOT.powers().take(D).enumerate() {
158            res.value[i] = self.value[i] * z;
159        }
160
161        res
162    }
163
164    /// Repeated Frobenius automorphisms: x -> x^(n^count).
165    ///
166    /// Follows precomputation suggestion in Section 11.3.3 of the
167    /// Handbook of Elliptic and Hyperelliptic Curve Cryptography.
168    #[inline]
169    fn repeated_frobenius(&self, count: usize) -> Self {
170        if count == 0 {
171            return *self;
172        } else if count >= D {
173            // x |-> x^(n^D) is the identity, so x^(n^count) ==
174            // x^(n^(count % D))
175            return self.repeated_frobenius(count % D);
176        }
177
178        // z0 = DTH_ROOT^count = W^(k * count) where k = floor((n-1)/D)
179        let z0 = F::DTH_ROOT.exp_u64(count as u64);
180
181        let mut res = Self::ZERO;
182        for (i, z) in z0.powers().take(D).enumerate() {
183            res.value[i] = self.value[i] * z;
184        }
185
186        res
187    }
188
189    /// Compute the pseudo inverse of a given element making use of the Frobenius automorphism.
190    ///
191    /// Returns `0` if `self == 0`, and `1/self` otherwise.
192    ///
193    /// Algorithm 11.3.4 in Handbook of Elliptic and Hyperelliptic Curve Cryptography.
194    #[inline]
195    fn pseudo_inv(&self) -> Self {
196        // Writing 'a' for self and `q` for the order of the base field, our goal is to compute `a^{-1}`.
197        //
198        // Note that we can write `-1 = (q^{D - 1} + ... + q) - (q^{D - 1} + ... + q + 1)`.
199        // This is a useful decomposition as powers of q can be efficiently computed using the frobenius
200        // automorphism and `Norm(a) = a^{(q^{D - 1} + ... + q + 1)}` is guaranteed to lie in the base field.
201        // This means that `Norm(a)^{-1}` can be computed using base field operations.
202        //
203        // Hence this implementation first computes `ProdConj(a) = a^{q^{D - 1} + ... + q}` using frobenius automorphisms.
204        // From this, it computes `Norm(a) = a * ProdConj(a)` and returns `ProdConj(a) * Norm(a)^{-1} = a^{-1}`.
205
206        // This loop requires a linear number of multiplications and Frobenius automorphisms.
207        // If D is known, it is possible to do this in a logarithmic number. See quintic_inv
208        // for an example of this.
209        let mut prod_conj = self.frobenius();
210        for _ in 2..D {
211            prod_conj = (prod_conj * *self).frobenius();
212        }
213
214        // norm = a * prod_conj is in the base field, so only compute that
215        // coefficient rather than the full product.
216        let a = self.value;
217        let b = prod_conj.value;
218        let mut w_coeff = F::ZERO;
219        // This should really be a dot product but
220        // const generics doesn't let this happen:
221        // b.reverse();
222        // let mut g = F::dot_product::<{D - 1}>(a[1..].try_into().unwrap(), b[..D - 1].try_into().unwrap());
223        for i in 1..D {
224            w_coeff += a[i] * b[D - i];
225        }
226        let norm = F::dot_product(&[a[0], F::W], &[b[0], w_coeff]);
227        debug_assert_eq!(Self::from(norm), *self * prod_conj);
228
229        prod_conj * norm.inverse()
230    }
231}
232
233impl<F, A, const D: usize> PrimeCharacteristicRing for BinomialExtensionField<F, D, A>
234where
235    F: BinomiallyExtendable<D>,
236    A: BinomiallyExtendableAlgebra<F, D> + Copy,
237{
238    type PrimeSubfield = <A as PrimeCharacteristicRing>::PrimeSubfield;
239
240    const ZERO: Self = Self::new([A::ZERO; D]);
241
242    const ONE: Self = Self::new(field_to_array(A::ONE));
243
244    const TWO: Self = Self::new(field_to_array(A::TWO));
245
246    const NEG_ONE: Self = Self::new(field_to_array(A::NEG_ONE));
247
248    #[inline]
249    fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
250        <A as PrimeCharacteristicRing>::from_prime_subfield(f).into()
251    }
252
253    #[inline]
254    fn halve(&self) -> Self {
255        Self::new(array::from_fn(|i| self.value[i].halve()))
256    }
257
258    #[inline(always)]
259    fn square(&self) -> Self {
260        let mut res = Self::default();
261        let w = F::W;
262        binomial_square(&self.value, &mut res.value, w);
263        res
264    }
265
266    #[inline]
267    fn mul_2exp_u64(&self, exp: u64) -> Self {
268        // Depending on the field, this might be a little slower than
269        // the default implementation if the compiler doesn't realize `F::TWO.exp_u64(exp)` is a constant.
270        Self::new(array::from_fn(|i| self.value[i].mul_2exp_u64(exp)))
271    }
272
273    #[inline]
274    fn div_2exp_u64(&self, exp: u64) -> Self {
275        // Depending on the field, this might be a little slower than
276        // the default implementation if the compiler doesn't realize `F::ONE.halve().exp_u64(exp)` is a constant.
277        Self::new(array::from_fn(|i| self.value[i].div_2exp_u64(exp)))
278    }
279
280    #[inline]
281    fn zero_vec(len: usize) -> Vec<Self> {
282        // SAFETY: this is a repr(transparent) wrapper around an array.
283        unsafe { reconstitute_from_base(F::zero_vec(len * D)) }
284    }
285}
286
287impl<F: BinomiallyExtendable<D>, const D: usize> Algebra<F> for BinomialExtensionField<F, D> {}
288
289impl<F: BinomiallyExtendable<D>, const D: usize> RawDataSerializable
290    for BinomialExtensionField<F, D>
291{
292    const NUM_BYTES: usize = F::NUM_BYTES * D;
293
294    #[inline]
295    fn into_bytes(self) -> impl IntoIterator<Item = u8> {
296        self.value.into_iter().flat_map(|x| x.into_bytes())
297    }
298
299    #[inline]
300    fn into_byte_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u8> {
301        F::into_byte_stream(input.into_iter().flat_map(|x| x.value))
302    }
303
304    #[inline]
305    fn into_u32_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u32> {
306        F::into_u32_stream(input.into_iter().flat_map(|x| x.value))
307    }
308
309    #[inline]
310    fn into_u64_stream(input: impl IntoIterator<Item = Self>) -> impl IntoIterator<Item = u64> {
311        F::into_u64_stream(input.into_iter().flat_map(|x| x.value))
312    }
313
314    #[inline]
315    fn into_parallel_byte_streams<const N: usize>(
316        input: impl IntoIterator<Item = [Self; N]>,
317    ) -> impl IntoIterator<Item = [u8; N]> {
318        F::into_parallel_byte_streams(
319            input
320                .into_iter()
321                .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
322        )
323    }
324
325    #[inline]
326    fn into_parallel_u32_streams<const N: usize>(
327        input: impl IntoIterator<Item = [Self; N]>,
328    ) -> impl IntoIterator<Item = [u32; N]> {
329        F::into_parallel_u32_streams(
330            input
331                .into_iter()
332                .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
333        )
334    }
335
336    #[inline]
337    fn into_parallel_u64_streams<const N: usize>(
338        input: impl IntoIterator<Item = [Self; N]>,
339    ) -> impl IntoIterator<Item = [u64; N]> {
340        F::into_parallel_u64_streams(
341            input
342                .into_iter()
343                .flat_map(|x| (0..D).map(move |i| array::from_fn(|j| x[j].value[i]))),
344        )
345    }
346}
347
348impl<F: BinomiallyExtendable<D>, const D: usize> Field for BinomialExtensionField<F, D> {
349    type Packing = Self;
350
351    const GENERATOR: Self = Self::new(F::EXT_GENERATOR);
352
353    fn try_inverse(&self) -> Option<Self> {
354        if self.is_zero() {
355            return None;
356        }
357
358        let mut res = Self::default();
359
360        match D {
361            2 => quadratic_inv(&self.value, &mut res.value, F::W),
362            3 => cubic_inv(&self.value, &mut res.value, F::W),
363            4 => quartic_inv(&self.value, &mut res.value, F::W),
364            5 => res = quintic_inv(self),
365            8 => octic_inv(&self.value, &mut res.value, F::W),
366            _ => res = self.pseudo_inv(),
367        }
368
369        Some(res)
370    }
371
372    #[inline]
373    fn add_slices(slice_1: &mut [Self], slice_2: &[Self]) {
374        // By construction, Self is repr(transparent) over [F; D].
375        // Additionally, addition is F-linear. Hence we can cast
376        // everything to F and use F's add_slices.
377        unsafe {
378            let base_slice_1 = as_base_slice_mut(slice_1);
379            let base_slice_2 = as_base_slice(slice_2);
380
381            F::add_slices(base_slice_1, base_slice_2);
382        }
383    }
384
385    #[inline]
386    fn order() -> BigUint {
387        F::order().pow(D as u32)
388    }
389}
390
391impl<F, const D: usize> Display for BinomialExtensionField<F, D>
392where
393    F: BinomiallyExtendable<D>,
394{
395    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
396        if self.is_zero() {
397            write!(f, "0")
398        } else {
399            let str = self
400                .value
401                .iter()
402                .enumerate()
403                .filter(|(_, x)| !x.is_zero())
404                .map(|(i, x)| match (i, x.is_one()) {
405                    (0, _) => format!("{x}"),
406                    (1, true) => "X".to_string(),
407                    (1, false) => format!("{x} X"),
408                    (_, true) => format!("X^{i}"),
409                    (_, false) => format!("{x} X^{i}"),
410                })
411                .join(" + ");
412            write!(f, "{str}")
413        }
414    }
415}
416
417impl<F, A, const D: usize> Neg for BinomialExtensionField<F, D, A>
418where
419    F: BinomiallyExtendable<D>,
420    A: Algebra<F>,
421{
422    type Output = Self;
423
424    #[inline]
425    fn neg(self) -> Self {
426        Self::new(self.value.map(A::neg))
427    }
428}
429
430impl<F, A, const D: usize> Add for BinomialExtensionField<F, D, A>
431where
432    F: BinomiallyExtendable<D>,
433    A: BinomiallyExtendableAlgebra<F, D>,
434{
435    type Output = Self;
436
437    #[inline]
438    fn add(self, rhs: Self) -> Self {
439        let value = A::binomial_add(&self.value, &rhs.value);
440        Self::new(value)
441    }
442}
443
444impl<F, A, const D: usize> Add<A> for BinomialExtensionField<F, D, A>
445where
446    F: BinomiallyExtendable<D>,
447    A: Algebra<F>,
448{
449    type Output = Self;
450
451    #[inline]
452    fn add(mut self, rhs: A) -> Self {
453        self.value[0] += rhs;
454        self
455    }
456}
457
458impl<F, A, const D: usize> AddAssign for BinomialExtensionField<F, D, A>
459where
460    F: BinomiallyExtendable<D>,
461    A: BinomiallyExtendableAlgebra<F, D>,
462{
463    #[inline]
464    fn add_assign(&mut self, rhs: Self) {
465        self.value = A::binomial_add(&self.value, &rhs.value);
466    }
467}
468
469impl<F, A, const D: usize> AddAssign<A> for BinomialExtensionField<F, D, A>
470where
471    F: BinomiallyExtendable<D>,
472    A: Algebra<F>,
473{
474    #[inline]
475    fn add_assign(&mut self, rhs: A) {
476        self.value[0] += rhs;
477    }
478}
479
480impl<F, A, const D: usize> Sum for BinomialExtensionField<F, D, A>
481where
482    F: BinomiallyExtendable<D>,
483    A: BinomiallyExtendableAlgebra<F, D> + Copy,
484{
485    #[inline]
486    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
487        iter.reduce(|acc, x| acc + x).unwrap_or(Self::ZERO)
488    }
489}
490
491impl<F, A, const D: usize> Sub for BinomialExtensionField<F, D, A>
492where
493    F: BinomiallyExtendable<D>,
494    A: BinomiallyExtendableAlgebra<F, D>,
495{
496    type Output = Self;
497
498    #[inline]
499    fn sub(self, rhs: Self) -> Self {
500        let value = A::binomial_sub(&self.value, &rhs.value);
501        Self::new(value)
502    }
503}
504
505impl<F, A, const D: usize> Sub<A> for BinomialExtensionField<F, D, A>
506where
507    F: BinomiallyExtendable<D>,
508    A: Algebra<F>,
509{
510    type Output = Self;
511
512    #[inline]
513    fn sub(self, rhs: A) -> Self {
514        let mut res = self.value;
515        res[0] -= rhs;
516        Self::new(res)
517    }
518}
519
520impl<F, A, const D: usize> SubAssign for BinomialExtensionField<F, D, A>
521where
522    F: BinomiallyExtendable<D>,
523    A: BinomiallyExtendableAlgebra<F, D>,
524{
525    #[inline]
526    fn sub_assign(&mut self, rhs: Self) {
527        self.value = A::binomial_sub(&self.value, &rhs.value);
528    }
529}
530
531impl<F, A, const D: usize> SubAssign<A> for BinomialExtensionField<F, D, A>
532where
533    F: BinomiallyExtendable<D>,
534    A: Algebra<F>,
535{
536    #[inline]
537    fn sub_assign(&mut self, rhs: A) {
538        self.value[0] -= rhs;
539    }
540}
541
542impl<F, A, const D: usize> Mul for BinomialExtensionField<F, D, A>
543where
544    F: BinomiallyExtendable<D>,
545    A: BinomiallyExtendableAlgebra<F, D>,
546{
547    type Output = Self;
548
549    #[inline]
550    fn mul(self, rhs: Self) -> Self {
551        let a = self.value;
552        let b = rhs.value;
553        let mut res = Self::default();
554        let w = F::W;
555
556        A::binomial_mul(&a, &b, &mut res.value, w);
557
558        res
559    }
560}
561
562impl<F, A, const D: usize> Mul<A> for BinomialExtensionField<F, D, A>
563where
564    F: BinomiallyExtendable<D>,
565    A: BinomiallyExtendableAlgebra<F, D>,
566{
567    type Output = Self;
568
569    #[inline]
570    fn mul(self, rhs: A) -> Self {
571        Self::new(A::binomial_base_mul(self.value, rhs))
572    }
573}
574
575impl<F, A, const D: usize> MulAssign for BinomialExtensionField<F, D, A>
576where
577    F: BinomiallyExtendable<D>,
578    A: BinomiallyExtendableAlgebra<F, D>,
579{
580    #[inline]
581    fn mul_assign(&mut self, rhs: Self) {
582        *self = self.clone() * rhs;
583    }
584}
585
586impl<F, A, const D: usize> MulAssign<A> for BinomialExtensionField<F, D, A>
587where
588    F: BinomiallyExtendable<D>,
589    A: BinomiallyExtendableAlgebra<F, D>,
590{
591    #[inline]
592    fn mul_assign(&mut self, rhs: A) {
593        *self = self.clone() * rhs;
594    }
595}
596
597impl<F, A, const D: usize> Product for BinomialExtensionField<F, D, A>
598where
599    F: BinomiallyExtendable<D>,
600    A: BinomiallyExtendableAlgebra<F, D> + Copy,
601{
602    #[inline]
603    fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
604        iter.reduce(|acc, x| acc * x).unwrap_or(Self::ONE)
605    }
606}
607
608impl<F, const D: usize> Div for BinomialExtensionField<F, D>
609where
610    F: BinomiallyExtendable<D>,
611{
612    type Output = Self;
613
614    #[allow(clippy::suspicious_arithmetic_impl)]
615    #[inline]
616    fn div(self, rhs: Self) -> Self::Output {
617        self * rhs.inverse()
618    }
619}
620
621impl<F, const D: usize> DivAssign for BinomialExtensionField<F, D>
622where
623    F: BinomiallyExtendable<D>,
624{
625    #[inline]
626    fn div_assign(&mut self, rhs: Self) {
627        *self = *self / rhs;
628    }
629}
630
631impl<F: BinomiallyExtendable<D>, const D: usize> Distribution<BinomialExtensionField<F, D>>
632    for StandardUniform
633where
634    Self: Distribution<F>,
635{
636    #[inline]
637    fn sample<R: rand::Rng + ?Sized>(&self, rng: &mut R) -> BinomialExtensionField<F, D> {
638        BinomialExtensionField::new(array::from_fn(|_| self.sample(rng)))
639    }
640}
641
642impl<F: Field + HasTwoAdicBinomialExtension<D>, const D: usize> TwoAdicField
643    for BinomialExtensionField<F, D>
644{
645    const TWO_ADICITY: usize = F::EXT_TWO_ADICITY;
646
647    #[inline]
648    fn two_adic_generator(bits: usize) -> Self {
649        Self::new(F::ext_two_adic_generator(bits))
650    }
651}
652
653/// Add two vectors element wise.
654#[inline]
655pub fn vector_add<R: PrimeCharacteristicRing + Add<R2, Output = R>, R2: Dup, const D: usize>(
656    a: &[R; D],
657    b: &[R2; D],
658) -> [R; D] {
659    array::from_fn(|i| a[i].dup() + b[i].dup())
660}
661
662/// Subtract two vectors element wise.
663#[inline]
664pub(crate) fn vector_sub<
665    R: PrimeCharacteristicRing + Sub<R2, Output = R>,
666    R2: Dup,
667    const D: usize,
668>(
669    a: &[R; D],
670    b: &[R2; D],
671) -> [R; D] {
672    array::from_fn(|i| a[i].dup() - b[i].dup())
673}
674
675/// Multiply two vectors representing elements in a binomial extension.
676#[inline]
677pub(super) fn binomial_mul<
678    F: Field,
679    R: Algebra<F> + Algebra<R2>,
680    R2: Algebra<F>,
681    const D: usize,
682>(
683    a: &[R; D],
684    b: &[R2; D],
685    res: &mut [R; D],
686    w: F,
687) {
688    match D {
689        2 => quadratic_mul(a, b, res, w),
690        3 => cubic_mul(a, b, res, w),
691        4 => quartic_mul(a, b, res, w),
692        5 => quintic_mul(a, b, res, w),
693        8 => octic_mul(a, b, res, w),
694        _ =>
695        {
696            #[allow(clippy::needless_range_loop)]
697            for i in 0..D {
698                for j in 0..D {
699                    if i + j >= D {
700                        res[i + j - D] += a[i].dup() * w * b[j].dup();
701                    } else {
702                        res[i + j] += a[i].dup() * b[j].dup();
703                    }
704                }
705            }
706        }
707    }
708}
709
710/// Square a vector representing an element in a binomial extension.
711///
712/// This is optimized for the case that R is a prime field or its packing.
713#[inline]
714pub(super) fn binomial_square<F: Field, R: Algebra<F>, const D: usize>(
715    a: &[R; D],
716    res: &mut [R; D],
717    w: F,
718) {
719    match D {
720        2 => {
721            let a1_w = a[1].dup() * w;
722            res[0] = R::dot_product(a[..].try_into().unwrap(), &[a[0].dup(), a1_w]);
723            res[1] = a[0].dup() * a[1].double();
724        }
725        3 => cubic_square(a, res, w),
726        4 => quartic_square(a, res, w),
727        5 => quintic_square(a, res, w),
728        8 => octic_square(a, res, w),
729        _ => binomial_mul::<F, R, R, D>(a, a, res, w),
730    }
731}
732
733/// Optimized multiplication for quadratic extension field.
734///
735/// Makes use of the in built field dot product code. This is optimized for the case that
736/// R is a prime field or its packing.
737///
738/// ```text
739///     A = a0 + a1·X
740///     B = b0 + b1·X
741/// ```
742/// Where `X` satisfies `X² = w`. Then the product is:
743/// ```text
744///     A·B = a0·b0 + a1·b1·w + (a0·b1 + a1·b0)·X
745/// ```
746#[inline]
747fn quadratic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
748where
749    F: Field,
750    R: Algebra<F> + Algebra<R2>,
751    R2: Algebra<F>,
752{
753    let b1_w = b[1].dup() * w;
754
755    // Compute a0·b0 + a1·b1·w
756    res[0] = R::dot_product(a[..].try_into().unwrap(), &[b[0].dup().into(), b1_w.into()]);
757
758    // Compute a0·b1 + a1·b0
759    res[1] = R::dot_product(
760        &[a[0].dup(), a[1].dup()],
761        &[b[1].dup().into(), b[0].dup().into()],
762    );
763}
764
765///Section 11.3.6b in Handbook of Elliptic and Hyperelliptic Curve Cryptography.
766#[inline]
767fn quadratic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
768    assert_eq!(D, 2);
769    let neg_a1 = -a[1];
770    let scalar = F::dot_product(&[a[0], neg_a1], &[a[0], w * a[1]]).inverse();
771    res[0] = a[0] * scalar;
772    res[1] = neg_a1 * scalar;
773}
774
775/// Section 11.3.6b in Handbook of Elliptic and Hyperelliptic Curve Cryptography.
776#[inline]
777fn cubic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
778    assert_eq!(D, 3);
779    let a0_square = a[0].square();
780    let a1_square = a[1].square();
781    let a2_w = w * a[2];
782    let a0_a1 = a[0] * a[1];
783
784    // scalar = (a0^3+wa1^3+w^2a2^3-3wa0a1a2)^-1
785    let scalar = (a0_square * a[0] + w * a[1] * a1_square + a2_w.square() * a[2]
786        - (F::ONE + F::TWO) * a2_w * a0_a1)
787        .inverse();
788
789    //scalar*[a0^2-wa1a2, wa2^2-a0a1, a1^2-a0a2]
790    res[0] = scalar * (a0_square - a[1] * a2_w);
791    res[1] = scalar * (a2_w * a[2] - a0_a1);
792    res[2] = scalar * (a1_square - a[0] * a[2]);
793}
794
795/// karatsuba multiplication for cubic extension field
796#[inline]
797fn cubic_mul<F: Field, R: Algebra<F> + Algebra<R2>, R2: Algebra<F>, const D: usize>(
798    a: &[R; D],
799    b: &[R2; D],
800    res: &mut [R; D],
801    w: F,
802) {
803    assert_eq!(D, 3);
804    // TODO: Test if we should switch to a naive multiplication approach using dot products.
805    // This is mainly used for a degree 3 extension of Complex<Mersenne31> so this approach might be faster.
806
807    let a0_b0 = a[0].dup() * b[0].dup();
808    let a1_b1 = a[1].dup() * b[1].dup();
809    let a2_b2 = a[2].dup() * b[2].dup();
810
811    res[0] = a0_b0.dup()
812        + ((a[1].dup() + a[2].dup()) * (b[1].dup() + b[2].dup()) - a1_b1.dup() - a2_b2.dup()) * w;
813    res[1] = (a[0].dup() + a[1].dup()) * (b[0].dup() + b[1].dup()) - a0_b0.dup() - a1_b1.dup()
814        + a2_b2.dup() * w;
815    res[2] = (a[0].dup() + a[2].dup()) * (b[0].dup() + b[2].dup()) - a0_b0 - a2_b2 + a1_b1;
816}
817
818/// Section 11.3.6a in Handbook of Elliptic and Hyperelliptic Curve Cryptography.
819#[inline]
820fn cubic_square<F: Field, R: Algebra<F>, const D: usize>(a: &[R; D], res: &mut [R; D], w: F) {
821    assert_eq!(D, 3);
822
823    let w_a2 = a[2].dup() * w;
824
825    res[0] = a[0].square() + (a[1].dup() * w_a2.dup()).double();
826    res[1] = w_a2 * a[2].dup() + (a[0].dup() * a[1].dup()).double();
827    res[2] = a[1].square() + (a[0].dup() * a[2].dup()).double();
828}
829
830/// Multiplication in a quartic binomial extension field.
831///
832/// Makes use of the in built field dot product code. This is optimized for the case that
833/// R is a prime field or its packing.
834#[inline]
835pub fn quartic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
836where
837    F: Field,
838    R: Algebra<F> + Algebra<R2>,
839    R2: Algebra<F>,
840{
841    assert_eq!(D, 4);
842    let b_r_rev: [R; 5] = [
843        b[3].dup().into(),
844        b[2].dup().into(),
845        b[1].dup().into(),
846        b[0].dup().into(),
847        w.into(),
848    ];
849
850    // Constant term = a0*b0 + w(a1*b3 + a2*b2 + a3*b1)
851    let w_coeff_0 =
852        R::dot_product::<3>(a[1..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
853    res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[3..].try_into().unwrap());
854
855    // Linear term = a0*b1 + a1*b0 + w(a2*b3 + a3*b2)
856    let w_coeff_1 =
857        R::dot_product::<2>(a[2..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
858    res[1] = R::dot_product(
859        &[a[0].dup(), a[1].dup(), w_coeff_1],
860        b_r_rev[2..].try_into().unwrap(),
861    );
862
863    // Square term = a0*b2 + a1*b1 + a2*b0 + w(a3*b3)
864    let b3_w = b[3].dup() * w;
865    res[2] = R::dot_product::<4>(
866        a[..4].try_into().unwrap(),
867        &[
868            b_r_rev[1].dup(),
869            b_r_rev[2].dup(),
870            b_r_rev[3].dup(),
871            b3_w.into(),
872        ],
873    );
874
875    // Cubic term = a0*b3 + a1*b2 + a2*b1 + a3*b0
876    res[3] = R::dot_product::<4>(a[..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
877}
878
879/// Compute the inverse of a quartic binomial extension field element.
880#[inline]
881fn quartic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
882    assert_eq!(D, 4);
883
884    // We use the fact that the quartic extension is a tower of quadratic extensions.
885    // We can see this by writing our element as a = a0 + a1·X + a2·X² + a3·X³ = (a0 + a2·X²) + (a1 + a3·X²)·X.
886    // Explicitly our tower looks like F < F[x]/(X²-w) < F[x]/(X⁴-w).
887    // Using this, we can compute the inverse of a in three steps:
888
889    // Compute the norm of our element with respect to F[x]/(X²-w).
890    // This is given by:
891    //      ((a0 + a2·X²) + (a1 + a3·X²)·X) * ((a0 + a2·X²) - (a1 + a3·X²)·X)
892    //          = (a0 + a2·X²)² - (a1 + a3·X²)²
893    //          = (a0² + w·a2² - 2w·a1·a3) + (2·a0·a2 - a1² - w·a3²)·X²
894    //          = norm_0 + norm_1·X² = norm
895    let neg_a1 = -a[1];
896    let a3_w = a[3] * w;
897    let norm_0 = F::dot_product(&[a[0], a[2], neg_a1.double()], &[a[0], a[2] * w, a3_w]);
898    let norm_1 = F::dot_product(&[a[0], a[1], -a[3]], &[a[2].double(), neg_a1, a3_w]);
899
900    // Now we compute the inverse of norm = norm_0 + norm_1·X².
901    let mut inv = [F::ZERO; 2];
902    quadratic_inv(&[norm_0, norm_1], &mut inv, w);
903
904    // Then the inverse of a is given by:
905    //      a⁻¹ = ((a0 + a2·X²) - (a1 + a3·X²)·X)·norm⁻¹
906    //          = (a0 + a2·X²)·norm⁻¹ - (a1 + a3·X²)·norm⁻¹·X
907    // Both of these multiplications can be done in the quadratic extension field.
908    let mut out_evn = [F::ZERO; 2];
909    let mut out_odd = [F::ZERO; 2];
910    quadratic_mul(&[a[0], a[2]], &inv, &mut out_evn, w);
911    quadratic_mul(&[a[1], a[3]], &inv, &mut out_odd, w);
912
913    res[0] = out_evn[0];
914    res[1] = -out_odd[0];
915    res[2] = out_evn[1];
916    res[3] = -out_odd[1];
917}
918
919/// Optimized Square function for quadratic extension field.
920///
921/// Makes use of the in built field dot product code. This is optimized for the case that
922/// R is a prime field or its packing.
923#[inline]
924fn quartic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
925where
926    F: Field,
927    R: Algebra<F>,
928{
929    assert_eq!(D, 4);
930
931    let two_a0 = a[0].double();
932    let two_a1 = a[1].double();
933    let two_a2 = a[2].double();
934    let a2_w = a[2].dup() * w;
935    let a3_w = a[3].dup() * w;
936
937    // Constant term = a0*a0 + w*a2*a2 + 2*w*a1*a3
938    res[0] = R::dot_product(
939        &[a[0].dup(), a2_w, two_a1],
940        &[a[0].dup(), a[2].dup(), a3_w.dup()],
941    );
942
943    // Linear term = 2*a0*a1 + 2*w*a2*a3)
944    res[1] = R::dot_product(&[two_a0.dup(), two_a2.dup()], &[a[1].dup(), a3_w.dup()]);
945
946    // Square term = a1*a1 + w*a3*a3 + 2*a0*a2
947    res[2] = R::dot_product(
948        &[a[1].dup(), a3_w, two_a0.dup()],
949        &[a[1].dup(), a[3].dup(), a[2].dup()],
950    );
951
952    // Cubic term = 2*a0*a3 + 2*a1*a2)
953    res[3] = R::dot_product(&[two_a0, two_a2], &[a[3].dup(), a[1].dup()]);
954}
955
956/// Multiplication in a quintic binomial extension field.
957///
958/// Makes use of the in built field dot product code. This is optimized for the case that
959/// R is a prime field or its packing.
960pub fn quintic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
961where
962    F: Field,
963    R: Algebra<F> + Algebra<R2>,
964    R2: Algebra<F>,
965{
966    assert_eq!(D, 5);
967    let b_r_rev: [R; 6] = [
968        b[4].dup().into(),
969        b[3].dup().into(),
970        b[2].dup().into(),
971        b[1].dup().into(),
972        b[0].dup().into(),
973        w.into(),
974    ];
975
976    // Constant term = a0*b0 + w(a1*b4 + a2*b3 + a3*b2 + a4*b1)
977    let w_coeff_0 =
978        R::dot_product::<4>(a[1..].try_into().unwrap(), b_r_rev[..4].try_into().unwrap());
979    res[0] = R::dot_product(&[a[0].dup(), w_coeff_0], b_r_rev[4..].try_into().unwrap());
980
981    // Linear term = a0*b1 + a1*b0 + w(a2*b4 + a3*b3 + a4*b2)
982    let w_coeff_1 =
983        R::dot_product::<3>(a[2..].try_into().unwrap(), b_r_rev[..3].try_into().unwrap());
984    res[1] = R::dot_product(
985        &[a[0].dup(), a[1].dup(), w_coeff_1],
986        b_r_rev[3..].try_into().unwrap(),
987    );
988
989    // Square term = a0*b2 + a1*b1 + a2*b0 + w(a3*b4 + a4*b3)
990    let w_coeff_2 =
991        R::dot_product::<2>(a[3..].try_into().unwrap(), b_r_rev[..2].try_into().unwrap());
992    res[2] = R::dot_product(
993        &[a[0].dup(), a[1].dup(), a[2].dup(), w_coeff_2],
994        b_r_rev[2..].try_into().unwrap(),
995    );
996
997    // Cubic term = a0*b3 + a1*b2 + a2*b1 + a3*b0 + w*a4*b4
998    let b4_w = b[4].dup() * w;
999    res[3] = R::dot_product::<5>(
1000        a[..5].try_into().unwrap(),
1001        &[
1002            b_r_rev[1].dup(),
1003            b_r_rev[2].dup(),
1004            b_r_rev[3].dup(),
1005            b_r_rev[4].dup(),
1006            b4_w.into(),
1007        ],
1008    );
1009
1010    // Quartic term = a0*b4 + a1*b3 + a2*b2 + a3*b1 + a4*b0
1011    res[4] = R::dot_product::<5>(a[..].try_into().unwrap(), b_r_rev[..5].try_into().unwrap());
1012}
1013
1014/// Optimized Square function for quintic extension field elements.
1015///
1016/// Makes use of the in built field dot product code. This is optimized for the case that
1017/// R is a prime field or its packing.
1018#[inline]
1019fn quintic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
1020where
1021    F: Field,
1022    R: Algebra<F>,
1023{
1024    assert_eq!(D, 5);
1025
1026    let two_a0 = a[0].double();
1027    let two_a1 = a[1].double();
1028    let two_a2 = a[2].double();
1029    let two_a3 = a[3].double();
1030    let w_a3 = a[3].dup() * w;
1031    let w_a4 = a[4].dup() * w;
1032
1033    // Constant term = a0*a0 + 2*w(a1*a4 + a2*a3)
1034    res[0] = R::dot_product(
1035        &[a[0].dup(), w_a4.dup(), w_a3.dup()],
1036        &[a[0].dup(), two_a1.dup(), two_a2.dup()],
1037    );
1038
1039    // Linear term = w*a3*a3 + 2*(a0*a1 + w * a2*a4)
1040    res[1] = R::dot_product(
1041        &[w_a3, two_a0.dup(), w_a4.dup()],
1042        &[a[3].dup(), a[1].dup(), two_a2],
1043    );
1044
1045    // Square term = a1*a1 + 2 * (a0*a2 + w*a3*a4)
1046    res[2] = R::dot_product(
1047        &[a[1].dup(), two_a0.dup(), w_a4.dup()],
1048        &[a[1].dup(), a[2].dup(), two_a3],
1049    );
1050
1051    // Cubic term = w*a4*a4 + 2*(a0*a3 + a1*a2)
1052    res[3] = R::dot_product(
1053        &[w_a4, two_a0.dup(), two_a1.dup()],
1054        &[a[4].dup(), a[3].dup(), a[2].dup()],
1055    );
1056
1057    // Quartic term = a2*a2 + 2*(a0*a4 + a1*a3)
1058    res[4] = R::dot_product(
1059        &[a[2].dup(), two_a0, two_a1],
1060        &[a[2].dup(), a[4].dup(), a[3].dup()],
1061    );
1062}
1063
1064/// Optimized Square function for octic extension field elements.
1065///
1066/// Makes use of the in built field dot product code. This is optimized for the case that
1067/// R is a prime field or its packing.
1068#[inline]
1069fn octic_square<F, R, const D: usize>(a: &[R; D], res: &mut [R; D], w: F)
1070where
1071    F: Field,
1072    R: Algebra<F>,
1073{
1074    assert_eq!(D, 8);
1075
1076    let a0_2 = a[0].double();
1077    let a1_2 = a[1].double();
1078    let a2_2 = a[2].double();
1079    let a3_2 = a[3].double();
1080    let w_a4 = a[4].dup() * w;
1081    let w_a5 = a[5].dup() * w;
1082    let w_a6 = a[6].dup() * w;
1083    let w_a7 = a[7].dup() * w;
1084    let w_a5_2 = w_a5.double();
1085    let w_a6_2 = w_a6.double();
1086    let w_a7_2 = w_a7.double();
1087
1088    // Constant coefficient = a0² + w (2(a1 * a7 + a2 * a6 + a3 * a5) + a4²)
1089    res[0] = R::dot_product(
1090        &[a[0].dup(), a[1].dup(), a[2].dup(), a[3].dup(), a[4].dup()],
1091        &[a[0].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2.dup(), w_a4],
1092    );
1093
1094    // Linear coefficient = 2(a0 * a1 + w(a2 * a7 + a3 * a6 + a4 * a5))
1095    res[1] = R::dot_product(
1096        &[a0_2.dup(), a[2].dup(), a[3].dup(), a[4].dup()],
1097        &[a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5_2],
1098    );
1099
1100    // Square coefficient = 2a0 * a2 + a1² + w(2(a3 * a7 + a4 * a6) + a5²)
1101    res[2] = R::dot_product(
1102        &[a0_2.dup(), a[1].dup(), a[3].dup(), a[4].dup(), a[5].dup()],
1103        &[a[2].dup(), a[1].dup(), w_a7_2.dup(), w_a6_2.dup(), w_a5],
1104    );
1105
1106    // Cube coefficient = 2(a0 * a3 + a1 * a2 + w(a4 * a7 + a5 * a6)
1107    res[3] = R::dot_product(
1108        &[a0_2.dup(), a1_2.dup(), a[4].dup(), a[5].dup()],
1109        &[a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6_2],
1110    );
1111
1112    // Quartic coefficient = 2(a0 * a4 + a1 * a3) + a2² + w(2 * a7 * a5 + a6²)
1113    res[4] = R::dot_product(
1114        &[a0_2.dup(), a1_2.dup(), a[2].dup(), a[5].dup(), a[6].dup()],
1115        &[a[4].dup(), a[3].dup(), a[2].dup(), w_a7_2.dup(), w_a6],
1116    );
1117
1118    // Quintic coefficient = 2 * (a0 * a5 + a1 * a4 + a2 * a3 + w * a6 * a7)
1119    res[5] = R::dot_product(
1120        &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[6].dup()],
1121        &[a[5].dup(), a[4].dup(), a[3].dup(), w_a7_2],
1122    );
1123
1124    // Sextic coefficient = 2(a0 * a6 + a1 * a5 + a2 * a4) + a3² + w * a7²
1125    res[6] = R::dot_product(
1126        &[a0_2.dup(), a1_2.dup(), a2_2.dup(), a[3].dup(), a[7].dup()],
1127        &[a[6].dup(), a[5].dup(), a[4].dup(), a[3].dup(), w_a7],
1128    );
1129
1130    // Final coefficient = 2(a0 * a7 + a1 * a6 + a2 * a5 + a3 * a4)
1131    res[7] = R::dot_product(
1132        &[a0_2, a1_2, a2_2, a3_2],
1133        &[a[7].dup(), a[6].dup(), a[5].dup(), a[4].dup()],
1134    );
1135}
1136
1137/// Compute the inverse of a quintic binomial extension field element.
1138#[inline]
1139fn quintic_inv<F: BinomiallyExtendable<D>, const D: usize>(
1140    a: &BinomialExtensionField<F, D>,
1141) -> BinomialExtensionField<F, D> {
1142    // Writing 'a' for self, we need to compute: `prod_conj = a^{q^4 + q^3 + q^2 + q}`
1143    let a_exp_q = a.frobenius();
1144    let a_exp_q_plus_q_sq = (*a * a_exp_q).frobenius();
1145    let prod_conj = a_exp_q_plus_q_sq * a_exp_q_plus_q_sq.repeated_frobenius(2);
1146
1147    // norm = a * prod_conj is in the base field, so only compute that
1148    // coefficient rather than the full product.
1149    let a_vals = a.value;
1150    let mut b = prod_conj.value;
1151    b.reverse();
1152
1153    let w_coeff = F::dot_product::<4>(a.value[1..].try_into().unwrap(), b[..4].try_into().unwrap());
1154    let norm = F::dot_product::<2>(&[a_vals[0], F::W], &[b[4], w_coeff]);
1155    debug_assert_eq!(BinomialExtensionField::<F, D>::from(norm), *a * prod_conj);
1156
1157    prod_conj * norm.inverse()
1158}
1159
1160/// Compute the (D-N)'th coefficient in the multiplication of two elements in a degree
1161/// D binomial extension field.
1162///
1163/// a_0 * b_{D - N} + ... + a_{D - N} * b_0 + w * (a_{D - N + 1}b_{D - 1} + ... + a_{D - 1}b_{D - N + 1})
1164///
1165/// # Inputs
1166/// - a: An array of coefficients.
1167/// - b: An array of coefficients in reverse order with last element equal to `W`
1168#[inline]
1169fn compute_coefficient<
1170    F,
1171    R,
1172    const D: usize,
1173    const D_PLUS_1: usize,
1174    const N: usize,
1175    const D_PLUS_1_MIN_N: usize,
1176>(
1177    a: &[R; D],
1178    b_rev: &[R; D_PLUS_1],
1179) -> R
1180where
1181    F: Field,
1182    R: Algebra<F>,
1183{
1184    let w_coeff = R::dot_product::<N>(
1185        a[(D - N)..].try_into().unwrap(),
1186        b_rev[..N].try_into().unwrap(),
1187    );
1188    let mut scratch: [R; D_PLUS_1_MIN_N] = array::from_fn(|i| a[i].dup());
1189    scratch[D_PLUS_1_MIN_N - 1] = w_coeff;
1190    R::dot_product(&scratch, b_rev[N..].try_into().unwrap())
1191}
1192
1193/// Multiplication in an octic binomial extension field.
1194///
1195/// Makes use of the in built field dot product code. This is optimized for the case that
1196/// R is a prime field or its packing.
1197#[inline]
1198pub fn octic_mul<F, R, R2, const D: usize>(a: &[R; D], b: &[R2; D], res: &mut [R; D], w: F)
1199where
1200    F: Field,
1201    R: Algebra<F> + Algebra<R2>,
1202    R2: Algebra<F>,
1203{
1204    assert_eq!(D, 8);
1205    let a: &[R; 8] = a[..].try_into().unwrap();
1206    let mut b_r_rev: [R; 9] = [
1207        b[7].dup().into(),
1208        b[6].dup().into(),
1209        b[5].dup().into(),
1210        b[4].dup().into(),
1211        b[3].dup().into(),
1212        b[2].dup().into(),
1213        b[1].dup().into(),
1214        b[0].dup().into(),
1215        w.into(),
1216    ];
1217
1218    // Constant coefficient = a0*b0 + w(a1*b7 + ... + a7*b1)
1219    res[0] = compute_coefficient::<F, R, 8, 9, 7, 2>(a, &b_r_rev);
1220
1221    // Linear coefficient = a0*b1 + a1*b0 + w(a2*b7 + ... + a7*b2)
1222    res[1] = compute_coefficient::<F, R, 8, 9, 6, 3>(a, &b_r_rev);
1223
1224    // Square coefficient = a0*b2 + .. + a2*b0 + w(a3*b7 + ... + a7*b3)
1225    res[2] = compute_coefficient::<F, R, 8, 9, 5, 4>(a, &b_r_rev);
1226
1227    // Cube coefficient = a0*b3 + .. + a3*b0 + w(a4*b7 + ... + a7*b4)
1228    res[3] = compute_coefficient::<F, R, 8, 9, 4, 5>(a, &b_r_rev);
1229
1230    // Quartic coefficient = a0*b4 + ... + a4*b0 + w(a5*b7 + ... + a7*b5)
1231    res[4] = compute_coefficient::<F, R, 8, 9, 3, 6>(a, &b_r_rev);
1232
1233    // Quintic coefficient = a0*b5 + ... + a5*b0 + w(a6*b7 + ... + a7*b6)
1234    res[5] = compute_coefficient::<F, R, 8, 9, 2, 7>(a, &b_r_rev);
1235
1236    // Sextic coefficient = a0*b6 + ... + a6*b0 + w*a7*b7
1237    b_r_rev[8] *= b[7].dup();
1238    res[6] = R::dot_product::<8>(a, b_r_rev[1..].try_into().unwrap());
1239
1240    // Final coefficient = a0*b7 + ... + a7*b0
1241    res[7] = R::dot_product::<8>(a, b_r_rev[..8].try_into().unwrap());
1242}
1243
1244/// Compute the inverse of a octic binomial extension field element.
1245#[inline]
1246fn octic_inv<F: Field, const D: usize>(a: &[F; D], res: &mut [F; D], w: F) {
1247    assert_eq!(D, 8);
1248
1249    // We use the fact that the octic extension is a tower of extensions.
1250    // Explicitly our tower looks like F < F[x]/(X⁴ - w) < F[x]/(X^8 - w).
1251    // Using this, we can compute the inverse of a in three steps:
1252
1253    // Compute the norm of our element with respect to F[x]/(X⁴-w).
1254    // Writing a = a0 + a1·X + a2·X² + a3·X³ + a4·X⁴ + a5·X⁵ + a6·X⁶ + a7·X⁷
1255    //           = (a0 + a2·X² + a4·X⁴ + a6·X⁶) + (a1 + a3·X² + a5·X⁴ + a7·X⁶)·X
1256    //           = evens + odds·X
1257    //
1258    // The norm is given by:
1259    //    norm = (evens + odds·X) * (evens - odds·X)
1260    //          = evens² - odds²·X²
1261    //
1262    // This costs 2 multiplications in the quartic extension field.
1263    let evns = [a[0], a[2], a[4], a[6]];
1264    let odds = [a[1], a[3], a[5], a[7]];
1265    let mut evns_sq = [F::ZERO; 4];
1266    let mut odds_sq = [F::ZERO; 4];
1267    quartic_square(&evns, &mut evns_sq, w);
1268    quartic_square(&odds, &mut odds_sq, w);
1269    // odds_sq is multiplied by X^2 so we need to rotate it and multiply by a factor of w.
1270    let norm = [
1271        evns_sq[0] - w * odds_sq[3],
1272        evns_sq[1] - odds_sq[0],
1273        evns_sq[2] - odds_sq[1],
1274        evns_sq[3] - odds_sq[2],
1275    ];
1276
1277    // Now we compute the inverse of norm inside F[x]/(X⁴ - w). We already have an efficient function for this.
1278    let mut norm_inv = [F::ZERO; 4];
1279    quartic_inv(&norm, &mut norm_inv, w);
1280
1281    // Then the inverse of a is given by:
1282    //      a⁻¹ = (evens - odds·X)·norm⁻¹
1283    //          = evens·norm⁻¹ - odds·norm⁻¹·X
1284    //
1285    // Both of these multiplications can again be done in the quartic extension field.
1286    let mut out_evn = [F::ZERO; 4];
1287    let mut out_odd = [F::ZERO; 4];
1288    quartic_mul(&evns, &norm_inv, &mut out_evn, w);
1289    quartic_mul(&odds, &norm_inv, &mut out_odd, w);
1290
1291    res[0] = out_evn[0];
1292    res[1] = -out_odd[0];
1293    res[2] = out_evn[1];
1294    res[3] = -out_odd[1];
1295    res[4] = out_evn[2];
1296    res[5] = -out_odd[2];
1297    res[6] = out_evn[3];
1298    res[7] = -out_odd[3];
1299}