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