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