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
24pub trait CubicExtConfig: 'static + Send + Sync + Sized {
26 type BasePrimeField: PrimeField;
28 type BaseField: Field<BasePrimeField = Self::BasePrimeField>;
34 type FrobCoeff: Field;
37
38 const SQRT_PRECOMP: Option<SqrtPrecomputation<CubicExtField<Self>>>;
40
41 const DEGREE_OVER_BASE_PRIME_FIELD: usize;
43
44 const NONRESIDUE: Self::BaseField;
46
47 const FROBENIUS_COEFF_C1: &[Self::FrobCoeff];
49 const FROBENIUS_COEFF_C2: &[Self::FrobCoeff];
50
51 #[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 #[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 fn mul_base_field_by_frob_coeff(
70 c1: &mut Self::BaseField,
71 c2: &mut Self::BaseField,
72 power: usize,
73 );
74}
75
76#[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 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 pub fn norm(&self) -> P::BaseField {
122 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 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 self.c0 = s3;
268 P::mul_base_field_by_nonresidue_in_place(&mut self.c0);
269 self.c0 += &s0;
270
271 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 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 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; 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
342impl<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 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 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 let mut rng = test_rng();
692 let a: Fq3 = rng.gen();
693 let _ = a.norm();
694
695 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 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 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 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 let element1 = Fq6::new(c0, c1, c2);
761 let element2 = Fq6::new(c0, c1, c2);
762
763 assert_eq!(element1.cmp(&element2), Ordering::Equal);
765 }
766
767 #[test]
768 fn test_cubic_ext_field_cmp_less_than_elements() {
769 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 let element1 = Fq6::new(c0, c1, c2);
777 let element2 = Fq6::new(c0, c1, c2 + Fq2::one()); assert_eq!(element1.cmp(&element2), Ordering::Less);
781 }
782
783 #[test]
784 fn test_cubic_ext_field_cmp_greater_than_elements() {
785 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 let element1 = Fq6::new(c0, c1, c2 + Fq2::one()); let element2 = Fq6::new(c0, c1, c2);
794
795 assert_eq!(element1.cmp(&element2), Ordering::Greater);
797 }
798
799 #[test]
800 fn test_cubic_ext_field_cmp_with_different_c1() {
801 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 let element1 = Fq6::new(c0, c1, c2);
809 let element2 = Fq6::new(c0, c1 + Fq2::one(), c2); assert_eq!(element1.cmp(&element2), Ordering::Less);
813 }
814
815 #[test]
816 fn test_cubic_ext_field_cmp_with_different_c0() {
817 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 let element1 = Fq6::new(c0, c1, c2);
825 let element2 = Fq6::new(c0 + Fq2::one(), c1, c2); assert_eq!(element1.cmp(&element2), Ordering::Less);
829 }
830}