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