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
27pub trait CubicExtConfig: 'static + Send + Sync + Sized {
29 type BasePrimeField: PrimeField;
31 type BaseField: Field<BasePrimeField = Self::BasePrimeField>;
37 type FrobCoeff: Field;
40
41 const SQRT_PRECOMP: Option<SqrtPrecomputation<CubicExtField<Self>>>;
43
44 const DEGREE_OVER_BASE_PRIME_FIELD: usize;
46
47 const NONRESIDUE: Self::BaseField;
49
50 const FROBENIUS_COEFF_C1: &'static [Self::FrobCoeff];
52 const FROBENIUS_COEFF_C2: &'static [Self::FrobCoeff];
53
54 #[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 #[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 fn mul_base_field_by_frob_coeff(
73 c1: &mut Self::BaseField,
74 c2: &mut Self::BaseField,
75 power: usize,
76 );
77}
78
79#[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 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 pub fn norm(&self) -> P::BaseField {
133 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 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 self.c0 = s3;
272 P::mul_base_field_by_nonresidue_in_place(&mut self.c0);
273 self.c0 += &s0;
274
275 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 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 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; 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
340impl<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 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 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 let mut rng = test_rng();
712 let a: Fq3 = rng.gen();
713 let _ = a.norm();
714
715 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 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 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}