Skip to main content

ark_ff/fields/models/
cubic_extension.rs

1use crate::{
2    fields::{Field, PrimeField},
3    AdditiveGroup, FftField, LegendreSymbol, One, SqrtPrecomputation, ToConstraintField,
4    UniformRand, Zero,
5};
6use ark_serialize::{
7    CanonicalDeserialize, CanonicalDeserializeWithFlags, CanonicalSerialize,
8    CanonicalSerializeWithFlags, Compress, EmptyFlags, Flags, SerializationError,
9};
10use ark_std::{
11    cmp::*,
12    fmt,
13    io::{Read, Write},
14    iter::*,
15    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
16    rand::{
17        distributions::{Distribution, Standard},
18        Rng,
19    },
20    vec::*,
21};
22use zeroize::Zeroize;
23
24/// Defines a Cubic extension field from a cubic non-residue.
25pub trait CubicExtConfig: 'static + Send + Sync + Sized {
26    /// The prime field that this cubic extension is eventually an extension of.
27    type BasePrimeField: PrimeField;
28    /// The base field that this field is a cubic extension of.
29    ///
30    /// Note: while for simple instances of cubic extensions such as `Fp3`
31    /// we might see `BaseField == BasePrimeField`, it won't always hold true.
32    /// E.g. for an extension tower: `BasePrimeField == Fp`, but `BaseField == Fp2`.
33    type BaseField: Field<BasePrimeField = Self::BasePrimeField>;
34    /// The type of the coefficients for an efficient implementation of the
35    /// Frobenius endomorphism.
36    type FrobCoeff: Field;
37
38    /// Determines the algorithm for computing square roots.
39    const SQRT_PRECOMP: Option<SqrtPrecomputation<CubicExtField<Self>>>;
40
41    /// The degree of the extension over the base prime field.
42    const DEGREE_OVER_BASE_PRIME_FIELD: usize;
43
44    /// The cubic non-residue used to construct the extension.
45    const NONRESIDUE: Self::BaseField;
46
47    /// Coefficients for the Frobenius automorphism.
48    const FROBENIUS_COEFF_C1: &[Self::FrobCoeff];
49    const FROBENIUS_COEFF_C2: &[Self::FrobCoeff];
50
51    /// A specializable method for multiplying an element of the base field by
52    /// the quadratic non-residue. This is used in multiplication and squaring.
53    #[inline(always)]
54    fn mul_base_field_by_nonresidue_in_place(fe: &mut Self::BaseField) -> &mut Self::BaseField {
55        *fe *= &Self::NONRESIDUE;
56        fe
57    }
58
59    /// A defaulted method for multiplying an element of the base field by
60    /// the quadratic non-residue. This is used in multiplication and squaring.
61    #[inline(always)]
62    fn mul_base_field_by_nonresidue(mut fe: Self::BaseField) -> Self::BaseField {
63        Self::mul_base_field_by_nonresidue_in_place(&mut fe);
64        fe
65    }
66
67    /// A specializable method for multiplying an element of the base field by
68    /// the appropriate Frobenius coefficient.
69    fn mul_base_field_by_frob_coeff(
70        c1: &mut Self::BaseField,
71        c2: &mut Self::BaseField,
72        power: usize,
73    );
74}
75
76/// An element of a cubic extension field F_p\[X\]/(X^3 - P::NONRESIDUE) is
77/// represented as c0 + c1 * X + c2 * X^2, for c0, c1, c2 in `P::BaseField`.
78#[derive(educe::Educe, CanonicalDeserialize)]
79#[educe(Default, Hash, Clone, Copy, Debug, PartialEq, Eq)]
80pub struct CubicExtField<P: CubicExtConfig> {
81    pub c0: P::BaseField,
82    pub c1: P::BaseField,
83    pub c2: P::BaseField,
84}
85
86impl<P: CubicExtConfig> CubicExtField<P> {
87    /// Create a new field element from coefficients `c0`, `c1` and `c2`
88    /// so that the result is of the form `c0 + c1 * X + c2 * X^2`.
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// # use ark_std::test_rng;
94    /// # use ark_test_curves::bls12_381::{Fq2 as Fp2, Fq6 as Fp6};
95    /// # use ark_test_curves::bls12_381::Fq6Config;
96    /// # use ark_std::UniformRand;
97    /// # use ark_ff::models::fp6_3over2::Fp6ConfigWrapper;
98    /// use ark_ff::models::cubic_extension::CubicExtField;
99    ///
100    /// let c0: Fp2 = Fp2::rand(&mut test_rng());
101    /// let c1: Fp2 = Fp2::rand(&mut test_rng());
102    /// let c2: Fp2 = Fp2::rand(&mut test_rng());
103    /// # type Config = Fp6ConfigWrapper<Fq6Config>;
104    /// // `Fp6` a degree-3 extension over `Fp2`.
105    /// let c: CubicExtField<Config> = Fp6::new(c0, c1, c2);
106    /// ```
107    pub const fn new(c0: P::BaseField, c1: P::BaseField, c2: P::BaseField) -> Self {
108        Self { c0, c1, c2 }
109    }
110
111    pub fn mul_assign_by_base_field(&mut self, value: &P::BaseField) {
112        self.c0 *= value;
113        self.c1 *= value;
114        self.c2 *= value;
115    }
116
117    /// Calculate the norm of an element with respect to the base field
118    /// `P::BaseField`. The norm maps an element `a` in the extension field
119    /// `Fq^m` to an element in the BaseField `Fq`.
120    /// `Norm(a) = a * a^q * a^(q^2)`
121    pub fn norm(&self) -> P::BaseField {
122        // w.r.t to BaseField, we need the 0th, 1st & 2nd powers of `q`
123        // Since Frobenius coefficients on the towered extensions are
124        // indexed w.r.t. to BasePrimeField, we need to calculate the correct index.
125        let index_multiplier = P::BaseField::extension_degree() as usize;
126        let mut self_to_p = *self;
127        self_to_p.frobenius_map_in_place(index_multiplier);
128        let mut self_to_p2 = *self;
129        self_to_p2.frobenius_map_in_place(2 * index_multiplier);
130        self_to_p *= &(self_to_p2 * self);
131        assert!(self_to_p.c1.is_zero() && self_to_p.c2.is_zero());
132        self_to_p.c0
133    }
134}
135
136impl<P: CubicExtConfig> Zero for CubicExtField<P> {
137    fn zero() -> Self {
138        Self::new(P::BaseField::ZERO, P::BaseField::ZERO, P::BaseField::ZERO)
139    }
140
141    fn is_zero(&self) -> bool {
142        self.c0.is_zero() && self.c1.is_zero() && self.c2.is_zero()
143    }
144}
145
146impl<P: CubicExtConfig> One for CubicExtField<P> {
147    fn one() -> Self {
148        Self::new(P::BaseField::ONE, P::BaseField::ZERO, P::BaseField::ZERO)
149    }
150
151    fn is_one(&self) -> bool {
152        self.c0.is_one() && self.c1.is_zero() && self.c2.is_zero()
153    }
154}
155
156impl<P: CubicExtConfig> AdditiveGroup for CubicExtField<P> {
157    type Scalar = Self;
158
159    const ZERO: Self = Self::new(P::BaseField::ZERO, P::BaseField::ZERO, P::BaseField::ZERO);
160
161    fn double(&self) -> Self {
162        let mut result = *self;
163        result.double_in_place();
164        result
165    }
166
167    fn double_in_place(&mut self) -> &mut Self {
168        self.c0.double_in_place();
169        self.c1.double_in_place();
170        self.c2.double_in_place();
171        self
172    }
173
174    fn neg_in_place(&mut self) -> &mut Self {
175        self.c0.neg_in_place();
176        self.c1.neg_in_place();
177        self.c2.neg_in_place();
178        self
179    }
180}
181
182impl<P: CubicExtConfig> Field for CubicExtField<P> {
183    type BasePrimeField = P::BasePrimeField;
184
185    const SQRT_PRECOMP: Option<SqrtPrecomputation<Self>> = P::SQRT_PRECOMP;
186
187    const ONE: Self = Self::new(P::BaseField::ONE, P::BaseField::ZERO, P::BaseField::ZERO);
188
189    const NEG_ONE: Self = Self::new(
190        P::BaseField::NEG_ONE,
191        P::BaseField::ZERO,
192        P::BaseField::ZERO,
193    );
194
195    fn extension_degree() -> u64 {
196        3 * P::BaseField::extension_degree()
197    }
198
199    fn from_base_prime_field(elem: Self::BasePrimeField) -> Self {
200        let fe = P::BaseField::from_base_prime_field(elem);
201        Self::new(fe, P::BaseField::ZERO, P::BaseField::ZERO)
202    }
203
204    fn to_base_prime_field_elements(&self) -> impl Iterator<Item = Self::BasePrimeField> {
205        self.c0
206            .to_base_prime_field_elements()
207            .chain(self.c1.to_base_prime_field_elements())
208            .chain(self.c2.to_base_prime_field_elements())
209    }
210
211    fn from_base_prime_field_elems(
212        elems: impl IntoIterator<Item = Self::BasePrimeField>,
213    ) -> Option<Self> {
214        let mut iter = elems.into_iter();
215        let d = P::BaseField::extension_degree() as usize;
216
217        let a = P::BaseField::from_base_prime_field_elems(iter.by_ref().take(d))?;
218        let b = P::BaseField::from_base_prime_field_elems(iter.by_ref().take(d))?;
219        let c = P::BaseField::from_base_prime_field_elems(iter.by_ref().take(d))?;
220
221        iter.next().is_none().then(|| Self::new(a, b, c))
222    }
223
224    #[inline]
225    fn from_random_bytes_with_flags<F: Flags>(bytes: &[u8]) -> Option<(Self, F)> {
226        let split_at = bytes.len() / 3;
227        if let Some(c0) = P::BaseField::from_random_bytes(&bytes[..split_at]) {
228            if let Some(c1) = P::BaseField::from_random_bytes(&bytes[split_at..2 * split_at]) {
229                if let Some((c2, flags)) =
230                    P::BaseField::from_random_bytes_with_flags(&bytes[2 * split_at..])
231                {
232                    return Some((CubicExtField::new(c0, c1, c2), flags));
233                }
234            }
235        }
236        None
237    }
238
239    #[inline]
240    fn from_random_bytes(bytes: &[u8]) -> Option<Self> {
241        Self::from_random_bytes_with_flags::<EmptyFlags>(bytes).map(|f| f.0)
242    }
243
244    fn square(&self) -> Self {
245        let mut result = *self;
246        result.square_in_place();
247        result
248    }
249
250    fn square_in_place(&mut self) -> &mut Self {
251        // Devegili OhEig Scott Dahab --- Multiplication and Squaring on
252        // AbstractPairing-Friendly
253        // Fields.pdf; Section 4 (CH-SQR2)
254        let a = self.c0;
255        let b = self.c1;
256        let c = self.c2;
257
258        let s0 = a.square();
259        let ab = a * &b;
260        let s1 = ab.double();
261        let s2 = (a - &b + &c).square();
262        let bc = b * &c;
263        let s3 = bc.double();
264        let s4 = c.square();
265
266        // c0 = s0 + s3 * NON_RESIDUE
267        self.c0 = s3;
268        P::mul_base_field_by_nonresidue_in_place(&mut self.c0);
269        self.c0 += &s0;
270
271        // c1 = s1 + s4 * NON_RESIDUE
272        self.c1 = s4;
273        P::mul_base_field_by_nonresidue_in_place(&mut self.c1);
274        self.c1 += &s1;
275
276        self.c2 = s1 + &s2 + &s3 - &s0 - &s4;
277        self
278    }
279
280    /// Returns the Legendre symbol.
281    fn legendre(&self) -> LegendreSymbol {
282        self.norm().legendre()
283    }
284
285    fn inverse(&self) -> Option<Self> {
286        if self.is_zero() {
287            None
288        } else {
289            // From "High-Speed Software Implementation of the Optimal Ate AbstractPairing
290            // over
291            // Barreto-Naehrig Curves"; Algorithm 17
292            let t0 = self.c0.square();
293            let t1 = self.c1.square();
294            let t2 = self.c2.square();
295            let t3 = self.c0 * &self.c1;
296            let t4 = self.c0 * &self.c2;
297            let t5 = self.c1 * &self.c2;
298            let n5 = P::mul_base_field_by_nonresidue(t5);
299
300            let s0 = t0 - &n5;
301            let s1 = P::mul_base_field_by_nonresidue(t2) - &t3;
302            let s2 = t1 - &t4; // typo in paper referenced above. should be "-" as per Scott, but is "*"
303
304            let a1 = self.c2 * &s1;
305            let a2 = self.c1 * &s2;
306            let mut a3 = a1 + &a2;
307            a3 = P::mul_base_field_by_nonresidue(a3);
308            let t6 = (self.c0 * &s0 + &a3).inverse().unwrap();
309
310            let c0 = t6 * &s0;
311            let c1 = t6 * &s1;
312            let c2 = t6 * &s2;
313
314            Some(Self::new(c0, c1, c2))
315        }
316    }
317
318    fn inverse_in_place(&mut self) -> Option<&mut Self> {
319        self.inverse().map(|inverse| {
320            *self = inverse;
321            self
322        })
323    }
324
325    fn frobenius_map_in_place(&mut self, power: usize) {
326        self.c0.frobenius_map_in_place(power);
327        self.c1.frobenius_map_in_place(power);
328        self.c2.frobenius_map_in_place(power);
329
330        P::mul_base_field_by_frob_coeff(&mut self.c1, &mut self.c2, power);
331    }
332
333    fn mul_by_base_prime_field(&self, elem: &Self::BasePrimeField) -> Self {
334        let mut result = *self;
335        result.c0 = result.c0.mul_by_base_prime_field(elem);
336        result.c1 = result.c1.mul_by_base_prime_field(elem);
337        result.c2 = result.c2.mul_by_base_prime_field(elem);
338        result
339    }
340}
341
342/// `CubicExtField` elements are ordered lexicographically.
343impl<P: CubicExtConfig> Ord for CubicExtField<P> {
344    #[inline(always)]
345    fn cmp(&self, other: &Self) -> Ordering {
346        self.c2
347            .cmp(&other.c2)
348            .then_with(|| self.c1.cmp(&other.c1))
349            .then_with(|| self.c0.cmp(&other.c0))
350    }
351}
352
353impl<P: CubicExtConfig> PartialOrd for CubicExtField<P> {
354    #[inline(always)]
355    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
356        Some(self.cmp(other))
357    }
358}
359
360impl<P: CubicExtConfig> Zeroize for CubicExtField<P> {
361    // The phantom data does not contain element-specific data
362    // and thus does not need to be zeroized.
363    fn zeroize(&mut self) {
364        self.c0.zeroize();
365        self.c1.zeroize();
366        self.c2.zeroize();
367    }
368}
369
370impl<P: CubicExtConfig> From<u128> for CubicExtField<P> {
371    fn from(other: u128) -> Self {
372        Self::new(other.into(), P::BaseField::ZERO, P::BaseField::ZERO)
373    }
374}
375
376impl<P: CubicExtConfig> From<i128> for CubicExtField<P> {
377    #[inline]
378    fn from(val: i128) -> Self {
379        let abs = Self::from(val.unsigned_abs());
380        if val.is_positive() {
381            abs
382        } else {
383            -abs
384        }
385    }
386}
387
388impl<P: CubicExtConfig> From<u64> for CubicExtField<P> {
389    fn from(other: u64) -> Self {
390        Self::new(other.into(), P::BaseField::ZERO, P::BaseField::ZERO)
391    }
392}
393
394impl<P: CubicExtConfig> From<i64> for CubicExtField<P> {
395    #[inline]
396    fn from(val: i64) -> Self {
397        let abs = Self::from(val.unsigned_abs());
398        if val.is_positive() {
399            abs
400        } else {
401            -abs
402        }
403    }
404}
405
406impl<P: CubicExtConfig> From<u32> for CubicExtField<P> {
407    fn from(other: u32) -> Self {
408        Self::new(other.into(), P::BaseField::ZERO, P::BaseField::ZERO)
409    }
410}
411
412impl<P: CubicExtConfig> From<i32> for CubicExtField<P> {
413    #[inline]
414    fn from(val: i32) -> Self {
415        let abs = Self::from(val.unsigned_abs());
416        if val.is_positive() {
417            abs
418        } else {
419            -abs
420        }
421    }
422}
423
424impl<P: CubicExtConfig> From<u16> for CubicExtField<P> {
425    fn from(other: u16) -> Self {
426        Self::new(other.into(), P::BaseField::ZERO, P::BaseField::ZERO)
427    }
428}
429
430impl<P: CubicExtConfig> From<i16> for CubicExtField<P> {
431    #[inline]
432    fn from(val: i16) -> Self {
433        let abs = Self::from(val.unsigned_abs());
434        if val.is_positive() {
435            abs
436        } else {
437            -abs
438        }
439    }
440}
441
442impl<P: CubicExtConfig> From<u8> for CubicExtField<P> {
443    fn from(other: u8) -> Self {
444        Self::new(other.into(), P::BaseField::ZERO, P::BaseField::ZERO)
445    }
446}
447
448impl<P: CubicExtConfig> From<i8> for CubicExtField<P> {
449    #[inline]
450    fn from(val: i8) -> Self {
451        let abs = Self::from(val.unsigned_abs());
452        if val.is_positive() {
453            abs
454        } else {
455            -abs
456        }
457    }
458}
459
460impl<P: CubicExtConfig> From<bool> for CubicExtField<P> {
461    #[allow(clippy::unconditional_recursion)]
462    fn from(other: bool) -> Self {
463        other.into()
464    }
465}
466
467impl<P: CubicExtConfig> Neg for CubicExtField<P> {
468    type Output = Self;
469    #[inline]
470    fn neg(mut self) -> Self {
471        self.c0.neg_in_place();
472        self.c1.neg_in_place();
473        self.c2.neg_in_place();
474        self
475    }
476}
477
478impl<P: CubicExtConfig> Distribution<CubicExtField<P>> for Standard {
479    #[inline]
480    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> CubicExtField<P> {
481        CubicExtField::new(
482            UniformRand::rand(rng),
483            UniformRand::rand(rng),
484            UniformRand::rand(rng),
485        )
486    }
487}
488
489impl<P: CubicExtConfig> Add<&CubicExtField<P>> for CubicExtField<P> {
490    type Output = Self;
491
492    #[inline]
493    fn add(mut self, other: &Self) -> Self {
494        self += other;
495        self
496    }
497}
498
499impl<P: CubicExtConfig> Sub<&CubicExtField<P>> for CubicExtField<P> {
500    type Output = Self;
501
502    #[inline]
503    fn sub(mut self, other: &Self) -> Self {
504        self -= other;
505        self
506    }
507}
508
509impl<P: CubicExtConfig> Mul<&CubicExtField<P>> for CubicExtField<P> {
510    type Output = Self;
511
512    #[inline]
513    fn mul(mut self, other: &Self) -> Self {
514        self *= other;
515        self
516    }
517}
518
519impl<P: CubicExtConfig> Div<&CubicExtField<P>> for CubicExtField<P> {
520    type Output = Self;
521
522    #[inline]
523    #[allow(clippy::suspicious_arithmetic_impl)]
524    fn div(mut self, other: &Self) -> Self {
525        self *= &other.inverse().unwrap();
526        self
527    }
528}
529
530impl_additive_ops_from_ref!(CubicExtField, CubicExtConfig);
531impl_multiplicative_ops_from_ref!(CubicExtField, CubicExtConfig);
532impl<P: CubicExtConfig> AddAssign<&Self> for CubicExtField<P> {
533    #[inline]
534    fn add_assign(&mut self, other: &Self) {
535        self.c0 += &other.c0;
536        self.c1 += &other.c1;
537        self.c2 += &other.c2;
538    }
539}
540
541impl<P: CubicExtConfig> SubAssign<&Self> for CubicExtField<P> {
542    #[inline]
543    fn sub_assign(&mut self, other: &Self) {
544        self.c0 -= &other.c0;
545        self.c1 -= &other.c1;
546        self.c2 -= &other.c2;
547    }
548}
549
550impl<P: CubicExtConfig> MulAssign<&Self> for CubicExtField<P> {
551    #[inline]
552    fn mul_assign(&mut self, other: &Self) {
553        // Devegili OhEig Scott Dahab --- Multiplication and Squaring on
554        // AbstractPairing-Friendly
555        // Fields.pdf; Section 4 (Karatsuba)
556
557        let a = other.c0;
558        let b = other.c1;
559        let c = other.c2;
560
561        let d = self.c0;
562        let e = self.c1;
563        let f = self.c2;
564
565        let ad = d * &a;
566        let be = e * &b;
567        let cf = f * &c;
568
569        let x = (e + &f) * &(b + &c) - &be - &cf;
570        let y = (d + &e) * &(a + &b) - &ad - &be;
571        let z = (d + &f) * &(a + &c) - &ad + &be - &cf;
572
573        self.c0 = ad + &P::mul_base_field_by_nonresidue(x);
574        self.c1 = y + &P::mul_base_field_by_nonresidue(cf);
575        self.c2 = z;
576    }
577}
578
579impl<P: CubicExtConfig> DivAssign<&Self> for CubicExtField<P> {
580    #[inline]
581    fn div_assign(&mut self, other: &Self) {
582        *self *= &other.inverse().unwrap();
583    }
584}
585
586impl<P: CubicExtConfig> fmt::Display for CubicExtField<P> {
587    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
588        write!(f, "CubicExtField({}, {}, {})", self.c0, self.c1, self.c2)
589    }
590}
591
592impl<P: CubicExtConfig> CanonicalSerializeWithFlags for CubicExtField<P> {
593    #[inline]
594    fn serialize_with_flags<W: Write, F: Flags>(
595        &self,
596        mut writer: W,
597        flags: F,
598    ) -> Result<(), SerializationError> {
599        self.c0.serialize_compressed(&mut writer)?;
600        self.c1.serialize_compressed(&mut writer)?;
601        self.c2.serialize_with_flags(&mut writer, flags)?;
602        Ok(())
603    }
604
605    #[inline]
606    fn serialized_size_with_flags<F: Flags>(&self) -> usize {
607        self.c0.compressed_size()
608            + self.c1.compressed_size()
609            + self.c2.serialized_size_with_flags::<F>()
610    }
611}
612
613impl<P: CubicExtConfig> CanonicalSerialize for CubicExtField<P> {
614    #[inline]
615    fn serialize_with_mode<W: Write>(
616        &self,
617        writer: W,
618        _compress: Compress,
619    ) -> Result<(), SerializationError> {
620        self.serialize_with_flags(writer, EmptyFlags)
621    }
622
623    #[inline]
624    fn serialized_size(&self, _compress: Compress) -> usize {
625        self.serialized_size_with_flags::<EmptyFlags>()
626    }
627}
628
629impl<P: CubicExtConfig> CanonicalDeserializeWithFlags for CubicExtField<P> {
630    #[inline]
631    fn deserialize_with_flags<R: Read, F: Flags>(
632        mut reader: R,
633    ) -> Result<(Self, F), SerializationError> {
634        let c0 = CanonicalDeserialize::deserialize_compressed(&mut reader)?;
635        let c1 = CanonicalDeserialize::deserialize_compressed(&mut reader)?;
636        let (c2, flags) = CanonicalDeserializeWithFlags::deserialize_with_flags(&mut reader)?;
637        Ok((Self::new(c0, c1, c2), flags))
638    }
639}
640
641impl<P: CubicExtConfig> ToConstraintField<P::BasePrimeField> for CubicExtField<P>
642where
643    P::BaseField: ToConstraintField<P::BasePrimeField>,
644{
645    fn to_field_elements(&self) -> Option<Vec<P::BasePrimeField>> {
646        let mut res = self.c0.to_field_elements()?;
647        res.extend(self.c1.to_field_elements()?);
648        res.extend(self.c2.to_field_elements()?);
649        Some(res)
650    }
651}
652
653impl<P: CubicExtConfig> FftField for CubicExtField<P>
654where
655    P::BaseField: FftField,
656{
657    const GENERATOR: Self = Self::new(
658        P::BaseField::GENERATOR,
659        P::BaseField::ZERO,
660        P::BaseField::ZERO,
661    );
662    const TWO_ADICITY: u32 = P::BaseField::TWO_ADICITY;
663    const TWO_ADIC_ROOT_OF_UNITY: Self = Self::new(
664        P::BaseField::TWO_ADIC_ROOT_OF_UNITY,
665        P::BaseField::ZERO,
666        P::BaseField::ZERO,
667    );
668    const SMALL_SUBGROUP_BASE: Option<u32> = P::BaseField::SMALL_SUBGROUP_BASE;
669    const SMALL_SUBGROUP_BASE_ADICITY: Option<u32> = P::BaseField::SMALL_SUBGROUP_BASE_ADICITY;
670    const LARGE_SUBGROUP_ROOT_OF_UNITY: Option<Self> =
671        if let Some(x) = P::BaseField::LARGE_SUBGROUP_ROOT_OF_UNITY {
672            Some(Self::new(x, P::BaseField::ZERO, P::BaseField::ZERO))
673        } else {
674            None
675        };
676}
677
678#[cfg(test)]
679mod cube_ext_tests {
680    use super::*;
681    use ark_std::{test_rng, vec};
682    use ark_test_curves::{
683        ark_ff::Field,
684        bls12_381::{Fq, Fq2, Fq6},
685        mnt6_753::Fq3,
686    };
687
688    #[test]
689    fn test_norm_for_towers() {
690        // First, test the simple fp3
691        let mut rng = test_rng();
692        let a: Fq3 = rng.gen();
693        let _ = a.norm();
694
695        // then also the tower 3_over_2, norm should work
696        let a: Fq6 = rng.gen();
697        let _ = a.norm();
698    }
699
700    #[test]
701    fn test_from_base_prime_field_elements() {
702        let ext_degree = Fq6::extension_degree() as usize;
703        // Test on slice lengths that aren't equal to the extension degree
704        let max_num_elems_to_test = 10;
705        for d in 0..max_num_elems_to_test {
706            if d == ext_degree {
707                continue;
708            }
709            let mut random_coeffs = Vec::new();
710            for _ in 0..d {
711                random_coeffs.push(Fq::rand(&mut test_rng()));
712            }
713            let res = Fq6::from_base_prime_field_elems(random_coeffs);
714            assert_eq!(res, None);
715        }
716        // Test on slice lengths that are equal to the extension degree
717        // We test consistency against Fq2::new
718        let number_of_tests = 10;
719        for _ in 0..number_of_tests {
720            let mut random_coeffs = Vec::new();
721            for _ in 0..ext_degree {
722                random_coeffs.push(Fq::rand(&mut test_rng()));
723            }
724
725            let expected_0 = Fq2::new(random_coeffs[0], random_coeffs[1]);
726            let expected_1 = Fq2::new(random_coeffs[2], random_coeffs[3]);
727            let expected_2 = Fq2::new(random_coeffs[3], random_coeffs[4]);
728            let expected = Fq6::new(expected_0, expected_1, expected_2);
729
730            let actual = Fq6::from_base_prime_field_elems(random_coeffs).unwrap();
731            assert_eq!(actual, expected);
732        }
733    }
734
735    #[test]
736    fn test_from_base_prime_field_element() {
737        let ext_degree = Fq6::extension_degree() as usize;
738        let max_num_elems_to_test = 10;
739        for _ in 0..max_num_elems_to_test {
740            let mut random_coeffs = vec![Fq::zero(); ext_degree];
741            let random_coeff = Fq::rand(&mut test_rng());
742            let res = Fq6::from_base_prime_field(random_coeff);
743            random_coeffs[0] = random_coeff;
744            assert_eq!(
745                res,
746                Fq6::from_base_prime_field_elems(random_coeffs).unwrap()
747            );
748        }
749    }
750
751    #[test]
752    fn test_cubic_ext_field_cmp_equal_elements() {
753        // Generate random coefficients
754        let mut rng = test_rng();
755        let c0 = Fq2::rand(&mut rng);
756        let c1 = Fq2::rand(&mut rng);
757        let c2 = Fq2::rand(&mut rng);
758
759        // Create two identical Fq6 elements
760        let element1 = Fq6::new(c0, c1, c2);
761        let element2 = Fq6::new(c0, c1, c2);
762
763        // The elements should be equal
764        assert_eq!(element1.cmp(&element2), Ordering::Equal);
765    }
766
767    #[test]
768    fn test_cubic_ext_field_cmp_less_than_elements() {
769        // Generate random coefficients
770        let mut rng = test_rng();
771        let c0 = Fq2::rand(&mut rng);
772        let c1 = Fq2::rand(&mut rng);
773        let c2 = Fq2::rand(&mut rng);
774
775        // Create two Fq6 elements, where element1 is less than element2
776        let element1 = Fq6::new(c0, c1, c2);
777        let element2 = Fq6::new(c0, c1, c2 + Fq2::one()); // Increment c2 to ensure element2 is greater
778
779        // element1 should be less than element2
780        assert_eq!(element1.cmp(&element2), Ordering::Less);
781    }
782
783    #[test]
784    fn test_cubic_ext_field_cmp_greater_than_elements() {
785        // Generate random coefficients
786        let mut rng = test_rng();
787        let c0 = Fq2::rand(&mut rng);
788        let c1 = Fq2::rand(&mut rng);
789        let c2 = Fq2::rand(&mut rng);
790
791        // Create two Fq6 elements, where element1 is greater than element2
792        let element1 = Fq6::new(c0, c1, c2 + Fq2::one()); // Increment c2 to ensure element1 is greater
793        let element2 = Fq6::new(c0, c1, c2);
794
795        // element1 should be greater than element2
796        assert_eq!(element1.cmp(&element2), Ordering::Greater);
797    }
798
799    #[test]
800    fn test_cubic_ext_field_cmp_with_different_c1() {
801        // Generate random coefficients
802        let mut rng = test_rng();
803        let c0 = Fq2::rand(&mut rng);
804        let c1 = Fq2::rand(&mut rng);
805        let c2 = Fq2::rand(&mut rng);
806
807        // Create two Fq6 elements with different c1 coefficients
808        let element1 = Fq6::new(c0, c1, c2);
809        let element2 = Fq6::new(c0, c1 + Fq2::one(), c2); // Increment c1 to ensure element2 is greater
810
811        // element1 should be less than element2 due to c1 comparison
812        assert_eq!(element1.cmp(&element2), Ordering::Less);
813    }
814
815    #[test]
816    fn test_cubic_ext_field_cmp_with_different_c0() {
817        // Generate random coefficients
818        let mut rng = test_rng();
819        let c0 = Fq2::rand(&mut rng);
820        let c1 = Fq2::rand(&mut rng);
821        let c2 = Fq2::rand(&mut rng);
822
823        // Create two Fq6 elements with different c0 coefficients
824        let element1 = Fq6::new(c0, c1, c2);
825        let element2 = Fq6::new(c0 + Fq2::one(), c1, c2); // Increment c0 to ensure element2 is greater
826
827        // element1 should be less than element2 due to c0 comparison
828        assert_eq!(element1.cmp(&element2), Ordering::Less);
829    }
830}