1use crate::{
2 biginteger::BigInteger,
3 fields::{Field, LegendreSymbol, PrimeField},
4 AdditiveGroup, FftField, One, SqrtPrecomputation, ToConstraintField, 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 QuadExtConfig: 'static + Send + Sync + Sized {
26 type BasePrimeField: PrimeField;
28 type BaseField: Field<BasePrimeField = Self::BasePrimeField>;
34 type FrobCoeff: Field;
37
38 const DEGREE_OVER_BASE_PRIME_FIELD: usize;
40
41 const NONRESIDUE: Self::BaseField;
43
44 const FROBENIUS_COEFF_C1: &[Self::FrobCoeff];
46
47 #[inline(always)]
51 fn mul_base_field_by_nonresidue_in_place(fe: &mut Self::BaseField) -> &mut Self::BaseField {
52 *fe *= &Self::NONRESIDUE;
53 fe
54 }
55
56 #[inline(always)]
60 fn mul_base_field_by_nonresidue_and_add(y: &mut Self::BaseField, x: &Self::BaseField) {
61 Self::mul_base_field_by_nonresidue_in_place(y);
62 *y += x;
63 }
64
65 #[inline(always)]
68 fn mul_base_field_by_nonresidue_plus_one_and_add(y: &mut Self::BaseField, x: &Self::BaseField) {
69 let old_y = *y;
70 Self::mul_base_field_by_nonresidue_and_add(y, x);
71 *y += old_y;
72 }
73
74 #[inline(always)]
78 fn sub_and_mul_base_field_by_nonresidue(y: &mut Self::BaseField, x: &Self::BaseField) {
79 Self::mul_base_field_by_nonresidue_in_place(y);
80 let mut result = *x;
81 result -= &*y;
82 *y = result;
83 }
84
85 fn mul_base_field_by_frob_coeff(fe: &mut Self::BaseField, power: usize);
88}
89
90#[derive(educe::Educe, CanonicalDeserialize)]
93#[educe(Default, Hash, Clone, Copy, Debug, PartialEq, Eq)]
94pub struct QuadExtField<P: QuadExtConfig> {
95 pub c0: P::BaseField,
97 pub c1: P::BaseField,
99}
100
101impl<P: QuadExtConfig> QuadExtField<P> {
102 pub const fn new(c0: P::BaseField, c1: P::BaseField) -> Self {
117 Self { c0, c1 }
118 }
119
120 pub fn conjugate_in_place(&mut self) -> &mut Self {
123 self.c1 = -self.c1;
124 self
125 }
126
127 pub fn norm(&self) -> P::BaseField {
150 let mut result = self.c1.square();
152 P::sub_and_mul_base_field_by_nonresidue(&mut result, &self.c0.square());
153 result
154 }
155
156 pub fn mul_assign_by_basefield(&mut self, element: &P::BaseField) {
159 self.c0 *= element;
160 self.c1 *= element;
161 }
162}
163
164impl<P: QuadExtConfig> Zero for QuadExtField<P> {
165 fn zero() -> Self {
166 QuadExtField::new(P::BaseField::zero(), P::BaseField::zero())
167 }
168
169 fn is_zero(&self) -> bool {
170 self.c0.is_zero() && self.c1.is_zero()
171 }
172}
173
174impl<P: QuadExtConfig> One for QuadExtField<P> {
175 fn one() -> Self {
176 QuadExtField::new(P::BaseField::one(), P::BaseField::zero())
177 }
178
179 fn is_one(&self) -> bool {
180 self.c0.is_one() && self.c1.is_zero()
181 }
182}
183
184impl<P: QuadExtConfig> AdditiveGroup for QuadExtField<P> {
185 type Scalar = Self;
186
187 const ZERO: Self = Self::new(P::BaseField::ZERO, P::BaseField::ZERO);
188
189 fn double(&self) -> Self {
190 let mut result = *self;
191 result.double_in_place();
192 result
193 }
194
195 fn double_in_place(&mut self) -> &mut Self {
196 self.c0.double_in_place();
197 self.c1.double_in_place();
198 self
199 }
200
201 fn neg_in_place(&mut self) -> &mut Self {
202 self.c0.neg_in_place();
203 self.c1.neg_in_place();
204 self
205 }
206}
207
208impl<P: QuadExtConfig> Field for QuadExtField<P> {
209 type BasePrimeField = P::BasePrimeField;
210
211 const SQRT_PRECOMP: Option<SqrtPrecomputation<Self>> = None;
212
213 const ONE: Self = Self::new(P::BaseField::ONE, P::BaseField::ZERO);
214
215 const NEG_ONE: Self = Self::new(P::BaseField::NEG_ONE, P::BaseField::ZERO);
216
217 fn extension_degree() -> u64 {
218 2 * P::BaseField::extension_degree()
219 }
220
221 fn from_base_prime_field(elem: Self::BasePrimeField) -> Self {
222 let fe = P::BaseField::from_base_prime_field(elem);
223 Self::new(fe, P::BaseField::ZERO)
224 }
225
226 fn to_base_prime_field_elements(&self) -> impl Iterator<Item = Self::BasePrimeField> {
227 self.c0
228 .to_base_prime_field_elements()
229 .chain(self.c1.to_base_prime_field_elements())
230 }
231
232 fn from_base_prime_field_elems(
233 elems: impl IntoIterator<Item = Self::BasePrimeField>,
234 ) -> Option<Self> {
235 let mut iter = elems.into_iter();
236 let d = P::BaseField::extension_degree() as usize;
237
238 let a = P::BaseField::from_base_prime_field_elems(iter.by_ref().take(d))?;
239 let b = P::BaseField::from_base_prime_field_elems(iter.by_ref().take(d))?;
240
241 iter.next().is_none().then(|| Self::new(a, b))
242 }
243
244 fn square(&self) -> Self {
245 let mut result = *self;
246 result.square_in_place();
247 result
248 }
249
250 #[inline]
251 fn from_random_bytes_with_flags<F: Flags>(bytes: &[u8]) -> Option<(Self, F)> {
252 let split_at = bytes.len() / 2;
253 if let Some(c0) = P::BaseField::from_random_bytes(&bytes[..split_at]) {
254 if let Some((c1, flags)) =
255 P::BaseField::from_random_bytes_with_flags(&bytes[split_at..])
256 {
257 return Some((QuadExtField::new(c0, c1), flags));
258 }
259 }
260 None
261 }
262
263 #[inline]
264 fn from_random_bytes(bytes: &[u8]) -> Option<Self> {
265 Self::from_random_bytes_with_flags::<EmptyFlags>(bytes).map(|f| f.0)
266 }
267
268 fn square_in_place(&mut self) -> &mut Self {
269 if P::NONRESIDUE == P::BaseField::NEG_ONE {
276 let c0_copy = self.c0;
280 let mut v0 = self.c0;
282 v0 -= &self.c1;
283 self.c0 += self.c1;
284 self.c0 *= &v0;
287
288 self.c1.double_in_place();
290 self.c1 *= &c0_copy;
293
294 self
295 } else {
296 let mut v0 = self.c0 - &self.c1;
298 let mut v3 = self.c1;
300 P::sub_and_mul_base_field_by_nonresidue(&mut v3, &self.c0);
301 let v2 = self.c0 * &self.c1;
303
304 v0 *= &v3;
308
309 self.c1 = v2;
311 self.c1.double_in_place();
312 self.c0 = v2;
316 P::mul_base_field_by_nonresidue_plus_one_and_add(&mut self.c0, &v0);
317
318 self
319 }
320 }
321
322 fn inverse(&self) -> Option<Self> {
323 if self.is_zero() {
324 None
325 } else {
326 let v1 = self.c1.square();
329 let mut v0 = v1;
331 P::sub_and_mul_base_field_by_nonresidue(&mut v0, &self.c0.square());
332
333 v0.inverse().map(|v1| {
334 let c0 = self.c0 * &v1;
335 let c1 = -(self.c1 * &v1);
336 Self::new(c0, c1)
337 })
338 }
339 }
340
341 fn inverse_in_place(&mut self) -> Option<&mut Self> {
342 self.inverse().map(|inverse| {
343 *self = inverse;
344 self
345 })
346 }
347
348 fn frobenius_map_in_place(&mut self, power: usize) {
349 self.c0.frobenius_map_in_place(power);
350 self.c1.frobenius_map_in_place(power);
351 P::mul_base_field_by_frob_coeff(&mut self.c1, power);
352 }
353
354 fn legendre(&self) -> LegendreSymbol {
355 self.norm().legendre()
366 }
367
368 fn sqrt(&self) -> Option<Self> {
369 if self.c1.is_zero() {
372 if self.c0.legendre().is_qr() {
378 return self.c0.sqrt().map(|c0| Self::new(c0, P::BaseField::ZERO));
380 } else {
381 return (self.c0.div(P::NONRESIDUE))
383 .sqrt()
384 .map(|res| Self::new(P::BaseField::ZERO, res));
385 }
386 }
387 let alpha = self.norm();
390
391 let mut two_inv = P::BasePrimeField::MODULUS;
394
395 two_inv.add_with_carry(&1u64.into());
396 two_inv.div2();
397
398 let two_inv = P::BasePrimeField::from(two_inv);
399 let two_inv = P::BaseField::from_base_prime_field(two_inv);
400
401 alpha.sqrt().and_then(|alpha| {
402 let mut delta = (alpha + &self.c0) * &two_inv;
403 if delta.legendre().is_qnr() {
404 delta -= α
405 }
406 let c0 = delta.sqrt().expect("Delta must have a square root");
407 let c0_inv = c0.inverse().expect("c0 must have an inverse");
408 let sqrt_cand = Self::new(c0, self.c1 * &two_inv * &c0_inv);
409 if sqrt_cand.square() == *self {
412 Some(sqrt_cand)
413 } else {
414 #[cfg(debug_assertions)]
415 {
416 use crate::fields::LegendreSymbol::*;
417 if self.legendre() != QuadraticNonResidue {
418 panic!(
419 "Input has a square root per its legendre symbol, but it was not found"
420 )
421 }
422 }
423 None
424 }
425 })
426 }
427
428 fn sqrt_in_place(&mut self) -> Option<&mut Self> {
429 (*self).sqrt().map(|sqrt| {
430 *self = sqrt;
431 self
432 })
433 }
434
435 fn mul_by_base_prime_field(&self, elem: &Self::BasePrimeField) -> Self {
436 let mut result = *self;
437 result.c0 = result.c0.mul_by_base_prime_field(elem);
438 result.c1 = result.c1.mul_by_base_prime_field(elem);
439 result
440 }
441}
442
443impl<P: QuadExtConfig> Ord for QuadExtField<P> {
445 #[inline(always)]
446 fn cmp(&self, other: &Self) -> Ordering {
447 match self.c1.cmp(&other.c1) {
448 Ordering::Greater => Ordering::Greater,
449 Ordering::Less => Ordering::Less,
450 Ordering::Equal => self.c0.cmp(&other.c0),
451 }
452 }
453}
454
455impl<P: QuadExtConfig> PartialOrd for QuadExtField<P> {
456 #[inline(always)]
457 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
458 Some(self.cmp(other))
459 }
460}
461
462impl<P: QuadExtConfig> Zeroize for QuadExtField<P> {
463 fn zeroize(&mut self) {
466 self.c0.zeroize();
467 self.c1.zeroize();
468 }
469}
470
471impl<P: QuadExtConfig> From<u128> for QuadExtField<P> {
472 fn from(other: u128) -> Self {
473 Self::new(other.into(), P::BaseField::ZERO)
474 }
475}
476
477impl<P: QuadExtConfig> From<i128> for QuadExtField<P> {
478 #[inline]
479 fn from(val: i128) -> Self {
480 let abs = Self::from(val.unsigned_abs());
481 if val.is_positive() {
482 abs
483 } else {
484 -abs
485 }
486 }
487}
488
489impl<P: QuadExtConfig> From<u64> for QuadExtField<P> {
490 fn from(other: u64) -> Self {
491 Self::new(other.into(), P::BaseField::ZERO)
492 }
493}
494
495impl<P: QuadExtConfig> From<i64> for QuadExtField<P> {
496 #[inline]
497 fn from(val: i64) -> Self {
498 let abs = Self::from(val.unsigned_abs());
499 if val.is_positive() {
500 abs
501 } else {
502 -abs
503 }
504 }
505}
506
507impl<P: QuadExtConfig> From<u32> for QuadExtField<P> {
508 fn from(other: u32) -> Self {
509 Self::new(other.into(), P::BaseField::ZERO)
510 }
511}
512
513impl<P: QuadExtConfig> From<i32> for QuadExtField<P> {
514 #[inline]
515 fn from(val: i32) -> Self {
516 let abs = Self::from(val.unsigned_abs());
517 if val.is_positive() {
518 abs
519 } else {
520 -abs
521 }
522 }
523}
524
525impl<P: QuadExtConfig> From<u16> for QuadExtField<P> {
526 fn from(other: u16) -> Self {
527 Self::new(other.into(), P::BaseField::ZERO)
528 }
529}
530
531impl<P: QuadExtConfig> From<i16> for QuadExtField<P> {
532 #[inline]
533 fn from(val: i16) -> Self {
534 let abs = Self::from(val.unsigned_abs());
535 if val.is_positive() {
536 abs
537 } else {
538 -abs
539 }
540 }
541}
542
543impl<P: QuadExtConfig> From<u8> for QuadExtField<P> {
544 fn from(other: u8) -> Self {
545 Self::new(other.into(), P::BaseField::ZERO)
546 }
547}
548
549impl<P: QuadExtConfig> From<i8> for QuadExtField<P> {
550 #[inline]
551 fn from(val: i8) -> Self {
552 let abs = Self::from(val.unsigned_abs());
553 if val.is_positive() {
554 abs
555 } else {
556 -abs
557 }
558 }
559}
560
561impl<P: QuadExtConfig> From<bool> for QuadExtField<P> {
562 fn from(other: bool) -> Self {
563 Self::new(u8::from(other).into(), P::BaseField::ZERO)
564 }
565}
566
567impl<P: QuadExtConfig> Neg for QuadExtField<P> {
568 type Output = Self;
569 #[inline]
570 fn neg(mut self) -> Self {
571 self.c0.neg_in_place();
572 self.c1.neg_in_place();
573 self
574 }
575}
576
577impl<P: QuadExtConfig> Distribution<QuadExtField<P>> for Standard {
578 #[inline]
579 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> QuadExtField<P> {
580 QuadExtField::new(UniformRand::rand(rng), UniformRand::rand(rng))
581 }
582}
583
584impl<P: QuadExtConfig> Add<&QuadExtField<P>> for QuadExtField<P> {
585 type Output = Self;
586
587 #[inline]
588 fn add(mut self, other: &Self) -> Self {
589 self += other;
590 self
591 }
592}
593
594impl<P: QuadExtConfig> Sub<&QuadExtField<P>> for QuadExtField<P> {
595 type Output = Self;
596
597 #[inline(always)]
598 fn sub(mut self, other: &Self) -> Self {
599 self -= other;
600 self
601 }
602}
603
604impl<P: QuadExtConfig> Mul<&QuadExtField<P>> for QuadExtField<P> {
605 type Output = Self;
606
607 #[inline(always)]
608 fn mul(mut self, other: &Self) -> Self {
609 self *= other;
610 self
611 }
612}
613
614impl<P: QuadExtConfig> Div<&QuadExtField<P>> for QuadExtField<P> {
615 type Output = Self;
616
617 #[inline]
618 #[allow(clippy::suspicious_arithmetic_impl)]
619 fn div(mut self, other: &Self) -> Self {
620 self *= &other.inverse().unwrap();
621 self
622 }
623}
624
625impl<P: QuadExtConfig> AddAssign<&Self> for QuadExtField<P> {
626 #[inline]
627 fn add_assign(&mut self, other: &Self) {
628 self.c0 += &other.c0;
629 self.c1 += &other.c1;
630 }
631}
632
633impl<P: QuadExtConfig> SubAssign<&Self> for QuadExtField<P> {
634 #[inline]
635 fn sub_assign(&mut self, other: &Self) {
636 self.c0 -= &other.c0;
637 self.c1 -= &other.c1;
638 }
639}
640
641impl_additive_ops_from_ref!(QuadExtField, QuadExtConfig);
642impl_multiplicative_ops_from_ref!(QuadExtField, QuadExtConfig);
643
644impl<P: QuadExtConfig> MulAssign<&Self> for QuadExtField<P> {
645 #[inline]
646 fn mul_assign(&mut self, other: &Self) {
647 if Self::extension_degree() == 2 {
648 let c1_input = [self.c0, self.c1];
649 P::mul_base_field_by_nonresidue_in_place(&mut self.c1);
650 *self = Self::new(
651 P::BaseField::sum_of_products(&[self.c0, self.c1], &[other.c0, other.c1]),
652 P::BaseField::sum_of_products(&c1_input, &[other.c1, other.c0]),
653 )
654 } else {
655 let mut v0 = self.c0;
658 v0 *= &other.c0;
659 let mut v1 = self.c1;
660 v1 *= &other.c1;
661
662 self.c1 += &self.c0;
663 self.c1 *= &(other.c0 + &other.c1);
664 self.c1 -= &v0;
665 self.c1 -= &v1;
666 self.c0 = v1;
667 P::mul_base_field_by_nonresidue_and_add(&mut self.c0, &v0);
668 }
669 }
670}
671
672impl<P: QuadExtConfig> DivAssign<&Self> for QuadExtField<P> {
673 #[inline]
674 fn div_assign(&mut self, other: &Self) {
675 *self *= &other.inverse().unwrap();
676 }
677}
678
679impl<P: QuadExtConfig> fmt::Display for QuadExtField<P> {
680 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
681 write!(f, "QuadExtField({} + {} * u)", self.c0, self.c1)
682 }
683}
684
685impl<P: QuadExtConfig> CanonicalSerializeWithFlags for QuadExtField<P> {
686 #[inline]
687 fn serialize_with_flags<W: Write, F: Flags>(
688 &self,
689 mut writer: W,
690 flags: F,
691 ) -> Result<(), SerializationError> {
692 self.c0.serialize_compressed(&mut writer)?;
693 self.c1.serialize_with_flags(&mut writer, flags)?;
694 Ok(())
695 }
696
697 #[inline]
698 fn serialized_size_with_flags<F: Flags>(&self) -> usize {
699 self.c0.compressed_size() + self.c1.serialized_size_with_flags::<F>()
700 }
701}
702
703impl<P: QuadExtConfig> CanonicalSerialize for QuadExtField<P> {
704 #[inline]
705 fn serialize_with_mode<W: Write>(
706 &self,
707 writer: W,
708 _compress: Compress,
709 ) -> Result<(), SerializationError> {
710 self.serialize_with_flags(writer, EmptyFlags)
711 }
712
713 #[inline]
714 fn serialized_size(&self, _compress: Compress) -> usize {
715 self.serialized_size_with_flags::<EmptyFlags>()
716 }
717}
718
719impl<P: QuadExtConfig> CanonicalDeserializeWithFlags for QuadExtField<P> {
720 #[inline]
721 fn deserialize_with_flags<R: Read, F: Flags>(
722 mut reader: R,
723 ) -> Result<(Self, F), SerializationError> {
724 let c0 = CanonicalDeserialize::deserialize_compressed(&mut reader)?;
725 let (c1, flags) = CanonicalDeserializeWithFlags::deserialize_with_flags(&mut reader)?;
726 Ok((QuadExtField::new(c0, c1), flags))
727 }
728}
729
730impl<P: QuadExtConfig> ToConstraintField<P::BasePrimeField> for QuadExtField<P>
731where
732 P::BaseField: ToConstraintField<P::BasePrimeField>,
733{
734 fn to_field_elements(&self) -> Option<Vec<P::BasePrimeField>> {
735 let mut res = Vec::new();
736 let mut c0_elems = self.c0.to_field_elements()?;
737 let mut c1_elems = self.c1.to_field_elements()?;
738
739 res.append(&mut c0_elems);
740 res.append(&mut c1_elems);
741
742 Some(res)
743 }
744}
745
746impl<P: QuadExtConfig> FftField for QuadExtField<P>
747where
748 P::BaseField: FftField,
749{
750 const GENERATOR: Self = Self::new(P::BaseField::GENERATOR, P::BaseField::ZERO);
751 const TWO_ADICITY: u32 = P::BaseField::TWO_ADICITY;
752 const TWO_ADIC_ROOT_OF_UNITY: Self =
753 Self::new(P::BaseField::TWO_ADIC_ROOT_OF_UNITY, P::BaseField::ZERO);
754 const SMALL_SUBGROUP_BASE: Option<u32> = P::BaseField::SMALL_SUBGROUP_BASE;
755 const SMALL_SUBGROUP_BASE_ADICITY: Option<u32> = P::BaseField::SMALL_SUBGROUP_BASE_ADICITY;
756 const LARGE_SUBGROUP_ROOT_OF_UNITY: Option<Self> =
757 if let Some(x) = P::BaseField::LARGE_SUBGROUP_ROOT_OF_UNITY {
758 Some(Self::new(x, P::BaseField::ZERO))
759 } else {
760 None
761 };
762}
763
764#[cfg(test)]
765mod quad_ext_tests {
766 use super::*;
767 use ark_std::test_rng;
768 use ark_test_curves::{
769 ark_ff::Field,
770 bls12_381::{Fq, Fq2},
771 };
772
773 #[test]
774 fn test_from_base_prime_field_elements() {
775 let ext_degree = Fq2::extension_degree() as usize;
776 let max_num_elems_to_test = 4;
778 for d in 0..max_num_elems_to_test {
779 if d == ext_degree {
780 continue;
781 }
782 let mut random_coeffs = Vec::new();
783 for _ in 0..d {
784 random_coeffs.push(Fq::rand(&mut test_rng()));
785 }
786 let res = Fq2::from_base_prime_field_elems(random_coeffs);
787 assert_eq!(res, None);
788 }
789 let number_of_tests = 10;
792 for _ in 0..number_of_tests {
793 let mut random_coeffs = Vec::new();
794 for _ in 0..ext_degree {
795 random_coeffs.push(Fq::rand(&mut test_rng()));
796 }
797 let expected = Fq2::new(random_coeffs[0], random_coeffs[1]);
798 let actual = Fq2::from_base_prime_field_elems(random_coeffs).unwrap();
799 assert_eq!(actual, expected);
800 }
801 }
802
803 #[test]
804 fn test_from_base_prime_field_element() {
805 let max_num_elems_to_test = 10;
806 for _ in 0..max_num_elems_to_test {
807 let mut random_coeffs = [Fq::zero(); 2];
808 let random_coeff = Fq::rand(&mut test_rng());
809 let res = Fq2::from_base_prime_field(random_coeff);
810 random_coeffs[0] = random_coeff;
811 assert_eq!(
812 res,
813 Fq2::from_base_prime_field_elems(random_coeffs).unwrap()
814 );
815 }
816 }
817}