ark_ff/fields/models/
cubic_extension.rs

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