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