ark_ff/fields/models/
fp2.rs

1use ark_std::Zero;
2
3use super::quadratic_extension::*;
4use crate::{fields::PrimeField, CyclotomicMultSubgroup};
5use core::{marker::PhantomData, ops::Not};
6
7/// Trait that specifies constants and methods for defining degree-two extension fields.
8pub trait Fp2Config: 'static + Send + Sync + Sized {
9    /// Base prime field underlying this extension.
10    type Fp: PrimeField;
11
12    /// Quadratic non-residue in [`Self::Fp`] used to construct the extension
13    /// field. That is, `NONRESIDUE` is such that the quadratic polynomial
14    /// `f(X) = X^2 - Self::NONRESIDUE` in Fp\[X\] is irreducible in `Self::Fp`.
15    const NONRESIDUE: Self::Fp;
16
17    /// Coefficients for the Frobenius automorphism.
18    const FROBENIUS_COEFF_FP2_C1: &'static [Self::Fp];
19
20    /// Return `fe * Self::NONRESIDUE`.
21    /// Intended for specialization when [`Self::NONRESIDUE`] has a special
22    /// structure that can speed up multiplication
23    #[inline(always)]
24    fn mul_fp_by_nonresidue_in_place(fe: &mut Self::Fp) -> &mut Self::Fp {
25        *fe *= Self::NONRESIDUE;
26        fe
27    }
28
29    /// A specializable method for setting `y = x + NONRESIDUE * y`.
30    /// This allows for optimizations when the non-residue is
31    /// canonically negative in the field.
32    #[inline(always)]
33    fn mul_fp_by_nonresidue_and_add(y: &mut Self::Fp, x: &Self::Fp) {
34        Self::mul_fp_by_nonresidue_in_place(y);
35        *y += x;
36    }
37
38    /// A specializable method for computing x + mul_fp_by_nonresidue(y) + y
39    /// This allows for optimizations when the non-residue is not -1.
40    #[inline(always)]
41    fn mul_fp_by_nonresidue_plus_one_and_add(y: &mut Self::Fp, x: &Self::Fp) {
42        let old_y = *y;
43        Self::mul_fp_by_nonresidue_and_add(y, x);
44        *y += old_y;
45    }
46
47    /// A specializable method for computing x - mul_fp_by_nonresidue(y)
48    /// This allows for optimizations when the non-residue is
49    /// canonically negative in the field.
50    #[inline(always)]
51    fn sub_and_mul_fp_by_nonresidue(y: &mut Self::Fp, x: &Self::Fp) {
52        *y = *x - Self::mul_fp_by_nonresidue_in_place(y);
53    }
54}
55
56/// Wrapper for [`Fp2Config`], allowing combination of the [`Fp2Config`] and [`QuadExtConfig`] traits.
57pub struct Fp2ConfigWrapper<P: Fp2Config>(PhantomData<P>);
58
59impl<P: Fp2Config> QuadExtConfig for Fp2ConfigWrapper<P> {
60    type BasePrimeField = P::Fp;
61    type BaseField = P::Fp;
62    type FrobCoeff = P::Fp;
63
64    const DEGREE_OVER_BASE_PRIME_FIELD: usize = 2;
65
66    const NONRESIDUE: Self::BaseField = P::NONRESIDUE;
67
68    const FROBENIUS_COEFF_C1: &'static [Self::FrobCoeff] = P::FROBENIUS_COEFF_FP2_C1;
69
70    #[inline(always)]
71    fn mul_base_field_by_nonresidue_in_place(fe: &mut Self::BaseField) -> &mut Self::BaseField {
72        P::mul_fp_by_nonresidue_in_place(fe)
73    }
74
75    #[inline(always)]
76    fn mul_base_field_by_nonresidue_and_add(y: &mut Self::BaseField, x: &Self::BaseField) {
77        P::mul_fp_by_nonresidue_and_add(y, x)
78    }
79
80    #[inline(always)]
81    fn mul_base_field_by_nonresidue_plus_one_and_add(y: &mut Self::BaseField, x: &Self::BaseField) {
82        P::mul_fp_by_nonresidue_plus_one_and_add(y, x)
83    }
84
85    #[inline(always)]
86    fn sub_and_mul_base_field_by_nonresidue(y: &mut Self::BaseField, x: &Self::BaseField) {
87        P::sub_and_mul_fp_by_nonresidue(y, x)
88    }
89
90    fn mul_base_field_by_frob_coeff(fe: &mut Self::BaseField, power: usize) {
91        *fe *= &Self::FROBENIUS_COEFF_C1[power % Self::DEGREE_OVER_BASE_PRIME_FIELD];
92    }
93}
94
95/// Alias for instances of quadratic extension fields. Helpful for omitting verbose
96/// instantiations involving `Fp2ConfigWrapper`.
97pub type Fp2<P> = QuadExtField<Fp2ConfigWrapper<P>>;
98
99impl<P: Fp2Config> Fp2<P> {
100    /// In-place multiply both coefficients `c0` and `c1` of `self`
101    /// by an element from [`Fp`](`Fp2Config::Fp`).
102    ///
103    /// # Examples
104    ///
105    /// ```
106    /// # use ark_std::test_rng;
107    /// # use ark_test_curves::bls12_381::{Fq as Fp, Fq2 as Fp2};
108    /// # use ark_std::UniformRand;
109    /// let c0: Fp = Fp::rand(&mut test_rng());
110    /// let c1: Fp = Fp::rand(&mut test_rng());
111    /// let mut ext_element: Fp2 = Fp2::new(c0, c1);
112    ///
113    /// let base_field_element: Fp = Fp::rand(&mut test_rng());
114    /// ext_element.mul_assign_by_fp(&base_field_element);
115    ///
116    /// assert_eq!(ext_element.c0, c0 * base_field_element);
117    /// assert_eq!(ext_element.c1, c1 * base_field_element);
118    /// ```
119    pub fn mul_assign_by_fp(&mut self, other: &P::Fp) {
120        self.c0 *= other;
121        self.c1 *= other;
122    }
123}
124
125impl<P: Fp2Config> CyclotomicMultSubgroup for Fp2<P> {
126    const INVERSE_IS_FAST: bool = true;
127    fn cyclotomic_inverse_in_place(&mut self) -> Option<&mut Self> {
128        // As the multiplicative subgroup is of order p^2 - 1, the
129        // only non-trivial cyclotomic subgroup is of order p+1
130        // Therefore, for any element in the cyclotomic subgroup, we have that `x^(p+1) = 1`.
131        // Recall that `x^(p+1)` in a quadratic extension field is equal
132        // to the norm in the base field, so we have that
133        // `x * x.conjugate() = 1`. By uniqueness of inverses,
134        // for this subgroup, x.inverse() = x.conjugate()
135
136        self.is_zero().not().then(|| {
137            self.conjugate_in_place();
138            self
139        })
140    }
141}