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