1use std::fmt::Display;
2use std::iter::Sum;
3use std::ops::Add;
4use std::ops::AddAssign;
5use std::ops::Div;
6use std::ops::Mul;
7use std::ops::MulAssign;
8use std::ops::Neg;
9use std::ops::Sub;
10use std::ops::SubAssign;
11
12use arbitrary::Arbitrary;
13use bfieldcodec_derive::BFieldCodec;
14use get_size2::GetSize;
15use num_traits::ConstOne;
16use num_traits::ConstZero;
17use num_traits::One;
18use num_traits::Zero;
19use rand::Rng;
20use rand::RngExt;
21use rand::distr::Distribution;
22use rand::distr::StandardUniform;
23use serde::Deserialize;
24use serde::Serialize;
25
26use crate::bfe_vec;
27use crate::error::TryFromXFieldElementError;
28use crate::math::b_field_element::BFieldElement;
29use crate::math::polynomial::Polynomial;
30use crate::math::traits::CyclicGroupGenerator;
31use crate::math::traits::FiniteField;
32use crate::math::traits::Inverse;
33use crate::math::traits::ModPowU32;
34use crate::math::traits::ModPowU64;
35use crate::math::traits::PrimitiveRootOfUnity;
36use crate::tip5::Digest;
37
38pub const EXTENSION_DEGREE: usize = 3;
39
40#[rustfmt::skip::attributes(derive)]
43#[derive(
44 Debug,
45 PartialEq,
46 Eq,
47 Copy,
48 Clone,
49 Hash,
50 Serialize,
51 Deserialize,
52 GetSize,
53 BFieldCodec,
54 Arbitrary,
55)]
56#[repr(transparent)]
57pub struct XFieldElement {
58 pub coefficients: [BFieldElement; EXTENSION_DEGREE],
59}
60
61#[macro_export]
76macro_rules! xfe {
77 ($value:expr) => {
78 XFieldElement::from($value)
79 };
80}
81
82#[macro_export]
124macro_rules! xfe_vec {
125 ($x:expr; $n:expr) => {
126 vec![XFieldElement::from($x); $n]
127 };
128 ([$c0:expr, $c1:expr, $c2:expr]; $n:expr) => {
129 vec![XFieldElement::from([$c0, $c1, $c2]); $n]
130 };
131 ($($x:expr),* $(,)?) => {
132 vec![$(XFieldElement::from($x)),*]
133 };
134 ($([$c0:expr, $c1:expr, $c2:expr]),* $(,)?) => {
135 vec![$(XFieldElement::from([$c0, $c1, $c2])),*]
136 };
137}
138
139#[macro_export]
181macro_rules! xfe_array {
182 ($x:expr; $n:expr) => {
183 [XFieldElement::from($x); $n]
184 };
185 ([$c0:expr, $c1:expr, $c2:expr]; $n:expr) => {
186 [XFieldElement::from([$c0, $c1, $c2]); $n]
187 };
188 ($($x:expr),* $(,)?) => {
189 [$(XFieldElement::from($x)),*]
190 };
191 ($([$c0:expr, $c1:expr, $c2:expr]),* $(,)?) => {
192 [$(XFieldElement::from([$c0, $c1, $c2])),*]
193 };
194}
195
196pub fn as_flat_slice(xfe_slice: &[XFieldElement]) -> &[BFieldElement] {
237 let slice_pointer = xfe_slice.as_ptr() as *const BFieldElement;
238 let bfe_slice_len = xfe_slice.len() * EXTENSION_DEGREE;
239
240 unsafe { std::slice::from_raw_parts(slice_pointer, bfe_slice_len) }
268}
269
270impl From<XFieldElement> for Digest {
271 fn from(xfe: XFieldElement) -> Self {
277 let [c0, c1, c2] = xfe.coefficients;
278 Digest::new([c0, c1, c2, BFieldElement::ZERO, BFieldElement::ZERO])
279 }
280}
281
282impl TryFrom<Digest> for XFieldElement {
283 type Error = TryFromXFieldElementError;
284
285 fn try_from(digest: Digest) -> Result<Self, Self::Error> {
286 let Digest([c0, c1, c2, BFieldElement::ZERO, BFieldElement::ZERO]) = digest else {
287 return Err(TryFromXFieldElementError::InvalidDigest);
288 };
289
290 Ok(Self::new([c0, c1, c2]))
291 }
292}
293
294impl Sum for XFieldElement {
295 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
296 iter.reduce(|a, b| a + b).unwrap_or(XFieldElement::ZERO)
297 }
298}
299
300impl<T> From<T> for XFieldElement
301where
302 T: Into<BFieldElement>,
303{
304 fn from(value: T) -> Self {
305 Self::new_const(value.into())
306 }
307}
308
309impl<T> From<[T; EXTENSION_DEGREE]> for XFieldElement
310where
311 T: Into<BFieldElement>,
312{
313 fn from(value: [T; EXTENSION_DEGREE]) -> Self {
314 Self::new(value.map(Into::into))
315 }
316}
317
318impl From<Polynomial<'_, BFieldElement>> for XFieldElement {
319 fn from(poly: Polynomial<'_, BFieldElement>) -> Self {
320 let (_, rem) = poly.naive_divide(&Self::shah_polynomial());
321 let mut xfe = [BFieldElement::ZERO; EXTENSION_DEGREE];
322
323 let Ok(rem_degree) = usize::try_from(rem.degree()) else {
324 return Self::ZERO;
325 };
326 xfe[..=rem_degree].copy_from_slice(&rem.coefficients()[..=rem_degree]);
327
328 XFieldElement::new(xfe)
329 }
330}
331
332impl TryFrom<&[BFieldElement]> for XFieldElement {
333 type Error = TryFromXFieldElementError;
334
335 fn try_from(value: &[BFieldElement]) -> Result<Self, Self::Error> {
336 value
337 .try_into()
338 .map(XFieldElement::new)
339 .map_err(|_| Self::Error::InvalidLength(value.len()))
340 }
341}
342
343impl TryFrom<Vec<BFieldElement>> for XFieldElement {
344 type Error = TryFromXFieldElementError;
345
346 fn try_from(value: Vec<BFieldElement>) -> Result<Self, Self::Error> {
347 XFieldElement::try_from(value.as_ref())
348 }
349}
350
351impl XFieldElement {
352 #[inline]
355 pub fn shah_polynomial() -> Polynomial<'static, BFieldElement> {
356 Polynomial::new(bfe_vec![1, -1, 0, 1])
357 }
358
359 #[inline]
360 pub const fn new(coefficients: [BFieldElement; EXTENSION_DEGREE]) -> Self {
361 Self { coefficients }
362 }
363
364 #[inline]
365 pub const fn new_const(element: BFieldElement) -> Self {
366 let zero = BFieldElement::ZERO;
367 Self::new([element, zero, zero])
368 }
369
370 #[must_use]
376 pub fn inverse(&self) -> Self {
377 assert!(
378 !self.is_zero(),
379 "Cannot invert the zero element in the extension field."
380 );
381
382 let [a0, a1, a2] = self.coefficients;
394 let a0_plus_a2 = a0 + a2;
395 let cofactor_00 = a0_plus_a2 * a0_plus_a2 - a1 * (a1 - a2);
396 let cofactor_01 = -(a0 * a1 + a2 * a2);
397 let cofactor_02 = a1 * a1 - a2 * a0_plus_a2;
398 let determinant = a0 * cofactor_00 - a1 * cofactor_02 - a2 * cofactor_01;
401 let determinant_inverse = determinant.inverse();
402
403 Self::new([
404 cofactor_00 * determinant_inverse,
405 cofactor_01 * determinant_inverse,
406 cofactor_02 * determinant_inverse,
407 ])
408 }
409
410 pub fn unlift(&self) -> Option<BFieldElement> {
411 let Self { coefficients } = self;
412 let [bfe, BFieldElement::ZERO, BFieldElement::ZERO] = coefficients else {
413 return None;
414 };
415
416 Some(*bfe)
417 }
418
419 #[deprecated(since = "2.0.0")]
420 #[doc(hidden)]
421 pub fn increment(&mut self, index: usize) {
422 self.coefficients[index].increment();
423 }
424
425 #[deprecated(since = "2.0.0")]
426 #[doc(hidden)]
427 pub fn decrement(&mut self, index: usize) {
428 self.coefficients[index].decrement();
429 }
430}
431
432impl Inverse for XFieldElement {
433 fn inverse(&self) -> Self {
434 self.inverse()
435 }
436}
437
438impl PrimitiveRootOfUnity for XFieldElement {
439 fn primitive_root_of_unity(n: u64) -> Option<XFieldElement> {
440 let b_root = BFieldElement::primitive_root_of_unity(n);
441 b_root.map(XFieldElement::new_const)
442 }
443}
444
445impl Distribution<XFieldElement> for StandardUniform {
446 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> XFieldElement {
447 XFieldElement::new(rng.random())
448 }
449}
450
451impl CyclicGroupGenerator for XFieldElement {
452 fn get_cyclic_group_elements(&self, max: Option<usize>) -> Vec<Self> {
453 let mut val = *self;
454 let mut ret: Vec<Self> = vec![Self::one()];
455
456 loop {
457 ret.push(val);
458 val *= *self;
459 if val.is_one() || max.is_some() && ret.len() >= max.unwrap() {
460 break;
461 }
462 }
463 ret
464 }
465}
466
467impl Display for XFieldElement {
468 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
469 if let Some(bfe) = self.unlift() {
470 return write!(f, "{bfe}_xfe");
471 }
472
473 let [c0, c1, c2] = self.coefficients;
474 write!(f, "({c2:>020}·x² + {c1:>020}·x + {c0:>020})")
475 }
476}
477
478impl Zero for XFieldElement {
479 fn zero() -> Self {
480 Self::ZERO
481 }
482
483 fn is_zero(&self) -> bool {
484 self == &Self::ZERO
485 }
486}
487
488impl ConstZero for XFieldElement {
489 const ZERO: Self = Self::new([BFieldElement::ZERO; EXTENSION_DEGREE]);
490}
491
492impl One for XFieldElement {
493 fn one() -> Self {
494 Self::ONE
495 }
496
497 fn is_one(&self) -> bool {
498 self == &Self::ONE
499 }
500}
501
502impl ConstOne for XFieldElement {
503 const ONE: Self = Self::new([BFieldElement::ONE, BFieldElement::ZERO, BFieldElement::ZERO]);
504}
505
506impl FiniteField for XFieldElement {
507 const NUM_BFE_LIMBS: Option<usize> = Some(EXTENSION_DEGREE);
509}
510
511impl Add<XFieldElement> for XFieldElement {
512 type Output = Self;
513
514 #[inline]
515 fn add(self, other: Self) -> Self {
516 let [s0, s1, s2] = self.coefficients;
517 let [o0, o1, o2] = other.coefficients;
518 let coefficients = [s0 + o0, s1 + o1, s2 + o2];
519 Self { coefficients }
520 }
521}
522
523impl Add<BFieldElement> for XFieldElement {
524 type Output = Self;
525
526 #[inline]
527 fn add(mut self, other: BFieldElement) -> Self {
528 self.coefficients[0] += other;
529 self
530 }
531}
532
533impl Add<XFieldElement> for BFieldElement {
535 type Output = XFieldElement;
536
537 #[inline]
538 fn add(self, mut other: XFieldElement) -> XFieldElement {
539 other.coefficients[0] += self;
540 other
541 }
542}
543
544impl Mul<XFieldElement> for XFieldElement {
545 type Output = Self;
546
547 #[inline]
548 fn mul(self, other: Self) -> Self {
549 let [c, b, a] = self.coefficients;
560 let [f, e, d] = other.coefficients;
561
562 let r0 = c * f - a * e - b * d;
563 let r1 = b * f + c * e - a * d + a * e + b * d;
564 let r2 = a * f + b * e + c * d + a * d;
565
566 Self::new([r0, r1, r2])
567 }
568}
569
570impl Mul<BFieldElement> for XFieldElement {
573 type Output = Self;
574
575 #[inline]
576 fn mul(self, other: BFieldElement) -> Self {
577 let coefficients = self.coefficients.map(|c| c * other);
578 Self { coefficients }
579 }
580}
581
582impl Mul<XFieldElement> for BFieldElement {
583 type Output = XFieldElement;
584
585 #[inline]
586 fn mul(self, other: XFieldElement) -> XFieldElement {
587 let coefficients = other.coefficients.map(|c| c * self);
588 XFieldElement { coefficients }
589 }
590}
591
592impl Neg for XFieldElement {
593 type Output = Self;
594
595 #[inline]
596 fn neg(self) -> Self {
597 let coefficients = self.coefficients.map(Neg::neg);
598 Self { coefficients }
599 }
600}
601
602impl Sub<XFieldElement> for XFieldElement {
603 type Output = Self;
604
605 #[inline]
606 fn sub(self, other: Self) -> Self {
607 self + (-other)
608 }
609}
610
611impl Sub<BFieldElement> for XFieldElement {
612 type Output = Self;
613
614 #[inline]
615 fn sub(self, other: BFieldElement) -> Self {
616 self + (-other)
617 }
618}
619
620impl Sub<XFieldElement> for BFieldElement {
621 type Output = XFieldElement;
622
623 #[inline]
624 fn sub(self, other: XFieldElement) -> XFieldElement {
625 self + (-other)
626 }
627}
628
629impl AddAssign<XFieldElement> for XFieldElement {
630 #[inline]
631 fn add_assign(&mut self, rhs: Self) {
632 self.coefficients[0] += rhs.coefficients[0];
633 self.coefficients[1] += rhs.coefficients[1];
634 self.coefficients[2] += rhs.coefficients[2];
635 }
636}
637
638impl AddAssign<BFieldElement> for XFieldElement {
639 #[inline]
640 fn add_assign(&mut self, rhs: BFieldElement) {
641 self.coefficients[0] += rhs;
642 }
643}
644
645impl MulAssign<XFieldElement> for XFieldElement {
646 #[inline]
647 fn mul_assign(&mut self, rhs: Self) {
648 *self = *self * rhs;
649 }
650}
651
652impl MulAssign<BFieldElement> for XFieldElement {
653 #[inline]
654 fn mul_assign(&mut self, rhs: BFieldElement) {
655 *self = *self * rhs;
656 }
657}
658
659impl SubAssign<XFieldElement> for XFieldElement {
660 #[inline]
661 fn sub_assign(&mut self, rhs: Self) {
662 self.coefficients[0] -= rhs.coefficients[0];
663 self.coefficients[1] -= rhs.coefficients[1];
664 self.coefficients[2] -= rhs.coefficients[2];
665 }
666}
667
668impl SubAssign<BFieldElement> for XFieldElement {
669 #[inline]
670 fn sub_assign(&mut self, rhs: BFieldElement) {
671 self.coefficients[0] -= rhs;
672 }
673}
674
675impl Div for XFieldElement {
676 type Output = Self;
677
678 #[expect(clippy::suspicious_arithmetic_impl)]
679 fn div(self, other: Self) -> Self {
680 self * other.inverse()
681 }
682}
683
684impl ModPowU64 for XFieldElement {
685 #[inline]
686 fn mod_pow_u64(&self, exponent: u64) -> Self {
687 let mut x = *self;
688 let mut result = Self::one();
689 let mut i = exponent;
690
691 while i > 0 {
692 if i & 1 == 1 {
693 result *= x;
694 }
695
696 x *= x;
697 i >>= 1;
698 }
699
700 result
701 }
702}
703
704impl ModPowU32 for XFieldElement {
705 #[inline]
706 fn mod_pow_u32(&self, exp: u32) -> Self {
707 self.mod_pow_u64(exp as u64)
708 }
709}
710
711#[cfg(test)]
712#[cfg_attr(coverage_nightly, coverage(off))]
713mod tests {
714 use itertools::Itertools;
715 use itertools::izip;
716 use num_traits::ConstOne;
717 use proptest::collection::vec;
718 use proptest::prelude::*;
719 use proptest_arbitrary_adapter::arb;
720
721 use super::*;
722 use crate::bfe;
723 use crate::math::b_field_element::*;
724 use crate::math::ntt::intt;
725 use crate::math::ntt::ntt;
726 use crate::math::other::random_elements;
727 use crate::tests::proptest;
728 use crate::tests::test;
729
730 impl proptest::arbitrary::Arbitrary for XFieldElement {
731 type Parameters = ();
732
733 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
734 arb().boxed()
735 }
736
737 type Strategy = BoxedStrategy<Self>;
738 }
739
740 #[macro_rules_attr::apply(test)]
741 fn display_is_as_expected() {
742 assert_eq!("42_xfe", xfe!(42).to_string());
743 assert_eq!("(3·x² + 2·x + 1)", xfe!([1, 2, 3]).to_string());
744 }
745
746 #[macro_rules_attr::apply(test)]
747 fn one_zero_test() {
748 let one = XFieldElement::one();
749 assert!(one.is_one());
750 assert!(one.coefficients[0].is_one());
751 assert!(one.coefficients[1].is_zero());
752 assert!(one.coefficients[2].is_zero());
753 assert_eq!(one, XFieldElement::ONE);
754 let zero = XFieldElement::zero();
755 assert!(zero.is_zero());
756 assert!(zero.coefficients[0].is_zero());
757 assert!(zero.coefficients[1].is_zero());
758 assert!(zero.coefficients[2].is_zero());
759 assert_eq!(zero, XFieldElement::ZERO);
760 let two = XFieldElement::new([
761 BFieldElement::new(2),
762 BFieldElement::ZERO,
763 BFieldElement::ZERO,
764 ]);
765 assert!(!two.is_one());
766 assert!(!zero.is_one());
767 let one_as_constant_term_0 = XFieldElement::new([
768 BFieldElement::new(1),
769 BFieldElement::ONE,
770 BFieldElement::ZERO,
771 ]);
772 let one_as_constant_term_1 = XFieldElement::new([
773 BFieldElement::new(1),
774 BFieldElement::ZERO,
775 BFieldElement::ONE,
776 ]);
777 assert!(!one_as_constant_term_0.is_one());
778 assert!(!one_as_constant_term_1.is_one());
779 assert!(!one_as_constant_term_0.is_zero());
780 assert!(!one_as_constant_term_1.is_zero());
781 }
782
783 #[macro_rules_attr::apply(test)]
784 fn x_field_random_element_generation_test() {
785 let rand_xs: Vec<XFieldElement> = random_elements(14);
786 assert_eq!(14, rand_xs.len());
787
788 assert!(rand_xs.into_iter().all_unique());
790 }
791
792 #[macro_rules_attr::apply(proptest)]
793 fn unlifting_random_xfe_doesnt_work(xfe: XFieldElement) {
794 prop_assert!(xfe.unlift().is_none());
795 }
796
797 #[macro_rules_attr::apply(test)]
798 fn summing_gives_expected_result() {
799 let empty_sum = [].into_iter().sum();
800 assert_eq!(XFieldElement::ZERO, empty_sum);
801
802 let a = xfe!([1, 0, 0]);
803 let b = xfe!([0, 2, 0]);
804 let c = xfe!([0, 0, 3]);
805 let d = xfe!([40, 50, 60]);
806 let sum = [a, b, c, d].into_iter().sum();
807
808 assert_eq!(xfe!([41, 52, 63]), sum);
809 }
810
811 #[macro_rules_attr::apply(proptest)]
812 fn bfe_vector_of_correct_length_can_become_xfe(
813 #[strategy(vec(arb(), EXTENSION_DEGREE))] bfes: Vec<BFieldElement>,
814 ) {
815 prop_assert!(XFieldElement::try_from(bfes).is_ok());
816 }
817
818 #[macro_rules_attr::apply(proptest)]
819 fn bfe_vector_of_incorrect_length_cannot_become_xfe(
820 #[filter(#bfes.len() != EXTENSION_DEGREE)] bfes: Vec<BFieldElement>,
821 ) {
822 prop_assert!(XFieldElement::try_from(bfes).is_err());
823 }
824
825 #[macro_rules_attr::apply(test)]
828 #[expect(deprecated)]
829 fn incr_decr_test() {
830 let one_const = XFieldElement::new([1, 0, 0].map(BFieldElement::new));
831 let two_const = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
832 let one_x = XFieldElement::new([0, 1, 0].map(BFieldElement::new));
833 let two_x = XFieldElement::new([0, 2, 0].map(BFieldElement::new));
834 let one_x_squared = XFieldElement::new([0, 0, 1].map(BFieldElement::new));
835 let two_x_squared = XFieldElement::new([0, 0, 2].map(BFieldElement::new));
836 let max_const = XFieldElement::new([BFieldElement::MAX, 0, 0].map(BFieldElement::new));
837 let max_x = XFieldElement::new([0, BFieldElement::MAX, 0].map(BFieldElement::new));
838 let max_x_squared = XFieldElement::new([0, 0, BFieldElement::MAX].map(BFieldElement::new));
839 let mut val = XFieldElement::ZERO;
840 val.increment(0);
841 assert!(val.is_one());
842 val.increment(0);
843 assert_eq!(two_const, val);
844 val.decrement(0);
845 assert!(val.is_one());
846 val.decrement(0);
847 assert!(val.is_zero());
848 val.decrement(0);
849 assert_eq!(max_const, val);
850 val.decrement(0);
851 assert_eq!(max_const - XFieldElement::ONE, val);
852 val.decrement(0);
853 assert_eq!(max_const - XFieldElement::ONE - XFieldElement::ONE, val);
854 val.increment(0);
855 val.increment(0);
856 val.increment(0);
857 assert!(val.is_zero());
858 val.increment(1);
859 assert_eq!(one_x, val);
860 val.increment(1);
861 assert_eq!(two_x, val);
862 val.decrement(1);
863 val.decrement(1);
864 assert!(val.is_zero());
865 val.decrement(1);
866 assert_eq!(max_x, val);
867 val.increment(1);
868 val.increment(2);
869 assert_eq!(one_x_squared, val);
870 val.increment(2);
871 assert_eq!(two_x_squared, val);
872 val.decrement(2);
873 val.decrement(2);
874 assert!(val.is_zero());
875 val.decrement(2);
876 assert_eq!(max_x_squared, val);
877 val.decrement(1);
878 val.decrement(0);
879 assert_eq!(max_x_squared + max_x + max_const, val);
880 val.decrement(2);
881 val.decrement(1);
882 val.decrement(0);
883 assert_eq!(
884 max_x_squared + max_x + max_const - one_const - one_x - one_x_squared,
885 val
886 );
887 }
888
889 #[macro_rules_attr::apply(test)]
890 fn x_field_add_test() {
891 let poly1 = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
892 let poly2 = XFieldElement::new([3, 0, 0].map(BFieldElement::new));
893
894 let mut poly_sum = XFieldElement::new([5, 0, 0].map(BFieldElement::new));
895 assert_eq!(poly_sum, poly1 + poly2);
896
897 let poly3 = XFieldElement::new([0, 5, 0].map(BFieldElement::new));
898 let poly4 = XFieldElement::new([0, 7, 0].map(BFieldElement::new));
899
900 poly_sum = XFieldElement::new([0, 12, 0].map(BFieldElement::new));
901 assert_eq!(poly_sum, poly3 + poly4);
902
903 let poly5 = XFieldElement::new([0, 0, 14].map(BFieldElement::new));
904 let poly6 = XFieldElement::new([0, 0, 23].map(BFieldElement::new));
905
906 poly_sum = XFieldElement::new([0, 0, 37].map(BFieldElement::new));
907 assert_eq!(poly_sum, poly5 + poly6);
908
909 let poly7 = XFieldElement::new([0, 0, BFieldElement::MAX].map(BFieldElement::new));
910 let poly8 = XFieldElement::new([0, 0, 23].map(BFieldElement::new));
911
912 poly_sum = XFieldElement::new([0, 0, 22].map(BFieldElement::new));
913 assert_eq!(poly_sum, poly7 + poly8);
914
915 let poly9 = XFieldElement::new([BFieldElement::MAX - 2, 12, 4].map(BFieldElement::new));
916 let poly10 = XFieldElement::new([2, 45000, BFieldElement::MAX - 3].map(BFieldElement::new));
917
918 poly_sum = XFieldElement::new([BFieldElement::MAX, 45012, 0].map(BFieldElement::new));
919 assert_eq!(poly_sum, poly9 + poly10);
920 }
921
922 #[macro_rules_attr::apply(test)]
923 fn x_field_sub_test() {
924 let poly1 = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
925 let poly2 = XFieldElement::new([3, 0, 0].map(BFieldElement::new));
926
927 let mut poly_diff = XFieldElement::new([1, 0, 0].map(BFieldElement::new));
928 assert_eq!(poly_diff, poly2 - poly1);
929
930 let poly3 = XFieldElement::new([0, 5, 0].map(BFieldElement::new));
931 let poly4 = XFieldElement::new([0, 7, 0].map(BFieldElement::new));
932
933 poly_diff = XFieldElement::new([0, 2, 0].map(BFieldElement::new));
934 assert_eq!(poly_diff, poly4 - poly3);
935
936 let poly5 = XFieldElement::new([0, 0, 14].map(BFieldElement::new));
937 let poly6 = XFieldElement::new([0, 0, 23].map(BFieldElement::new));
938
939 poly_diff = XFieldElement::new([0, 0, 9].map(BFieldElement::new));
940 assert_eq!(poly_diff, poly6 - poly5);
941
942 let poly7 = XFieldElement::new([0, 0, BFieldElement::MAX].map(BFieldElement::new));
943 let poly8 = XFieldElement::new([0, 0, 23].map(BFieldElement::new));
944
945 poly_diff = XFieldElement::new([0, 0, 24].map(BFieldElement::new));
946 assert_eq!(poly_diff, poly8 - poly7);
947
948 let poly9 = XFieldElement::new([BFieldElement::MAX - 2, 12, 4].map(BFieldElement::new));
949 let poly10 = XFieldElement::new([2, 45000, BFieldElement::MAX - 3].map(BFieldElement::new));
950
951 poly_diff = XFieldElement::new([5, 44988, BFieldElement::MAX - 7].map(BFieldElement::new));
952 assert_eq!(poly_diff, poly10 - poly9);
953 }
954
955 #[macro_rules_attr::apply(test)]
956 fn x_field_mul_test() {
957 let poly1 = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
958 let poly2 = XFieldElement::new([3, 0, 0].map(BFieldElement::new));
959
960 let poly12_product = XFieldElement::new([6, 0, 0].map(BFieldElement::new));
961 assert_eq!(poly12_product, poly1 * poly2);
962
963 let poly3 = XFieldElement::new([0, 3, 0].map(BFieldElement::new));
964 let poly4 = XFieldElement::new([0, 3, 0].map(BFieldElement::new));
965
966 let poly34_product = XFieldElement::new([0, 0, 9].map(BFieldElement::new));
967 assert_eq!(poly34_product, poly3 * poly4);
968
969 let poly5 = XFieldElement::new([125, 0, 0].map(BFieldElement::new));
970 let poly6 = XFieldElement::new([0, 0, 5].map(BFieldElement::new));
971
972 let poly56_product = XFieldElement::new([0, 0, 625].map(BFieldElement::new));
973 assert_eq!(poly56_product, poly5 * poly6);
974
975 let poly7 = XFieldElement::new([0, 0, 1].map(BFieldElement::new));
977 let poly8 = XFieldElement::new([0, 0, 1].map(BFieldElement::new));
978
979 let poly78_product = XFieldElement::new([0, BFieldElement::MAX, 1].map(BFieldElement::new));
980 assert_eq!(poly78_product, poly7 * poly8);
981
982 let poly9 = XFieldElement::new([0, 1, 0].map(BFieldElement::new));
984 let poly10 = XFieldElement::new([0, 0, 1].map(BFieldElement::new));
985
986 let poly910_product =
987 XFieldElement::new([BFieldElement::MAX, 1, 0].map(BFieldElement::new));
988 assert_eq!(poly910_product, poly9 * poly10);
989
990 let poly11 = XFieldElement::new([13, 2, 3].map(BFieldElement::new));
992 let poly12 = XFieldElement::new([19, 0, 5].map(BFieldElement::new));
993
994 let poly1112_product = XFieldElement::new([237, 33, 137].map(BFieldElement::new));
995 assert_eq!(poly1112_product, poly11 * poly12);
996 }
997
998 #[macro_rules_attr::apply(test)]
999 fn x_field_overloaded_arithmetic_test() {
1000 let mut rng = rand::rng();
1001 for _ in 0..100 {
1002 let xfe = rng.random::<XFieldElement>();
1003 let bfe = rng.random::<BFieldElement>();
1004
1005 let expected_add = xfe + bfe.lift();
1009 assert_eq!(expected_add, bfe.lift() + xfe);
1010 assert_eq!(expected_add, xfe + bfe);
1011 assert_eq!(expected_add, bfe + xfe);
1012
1013 let expected_mul = xfe * bfe.lift();
1017 assert_eq!(expected_mul, bfe.lift() * xfe);
1018 assert_eq!(expected_mul, xfe * bfe);
1019 assert_eq!(expected_mul, bfe * xfe);
1020
1021 assert_eq!(xfe - bfe.lift(), xfe - bfe);
1024 assert_eq!(bfe.lift() - xfe, bfe - xfe);
1025 }
1026 }
1027
1028 #[macro_rules_attr::apply(test)]
1029 fn x_field_into_test() {
1030 let zero_poly: XFieldElement = Polynomial::<BFieldElement>::new(vec![]).into();
1031 assert!(zero_poly.is_zero());
1032
1033 let shah_zero: XFieldElement = XFieldElement::shah_polynomial().into();
1034 assert!(shah_zero.is_zero());
1035
1036 let neg_shah_zero: XFieldElement =
1037 XFieldElement::shah_polynomial().scalar_mul(bfe!(-1)).into();
1038 assert!(neg_shah_zero.is_zero());
1039 }
1040
1041 #[macro_rules_attr::apply(test)]
1042 fn x_field_xgcp_test() {
1043 let one = XFieldElement::new([1, 0, 0].map(BFieldElement::new));
1046 let two = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
1047 let hundred = XFieldElement::new([100, 0, 0].map(BFieldElement::new));
1048 let x = XFieldElement::new([0, 1, 0].map(BFieldElement::new));
1049 let x_squared = XFieldElement::new([0, 0, 1].map(BFieldElement::new));
1050 let one_one_one = XFieldElement::new([1, 1, 1].map(BFieldElement::new));
1051 let complex0 = XFieldElement::new([450, 967, 21444444201].map(BFieldElement::new));
1052 let complex1 = XFieldElement::new([456230, 0, 4563210789].map(BFieldElement::new));
1053 let complex2 = XFieldElement::new([0, 96701, 456703214].map(BFieldElement::new));
1054 let complex3 = XFieldElement::new([124504, 9654677, 0].map(BFieldElement::new));
1055 let complex4 = XFieldElement::new(
1056 [BFieldElement::MAX, BFieldElement::MAX, BFieldElement::MAX].map(BFieldElement::new),
1057 );
1058 let complex5 =
1059 XFieldElement::new([0, BFieldElement::MAX, BFieldElement::MAX].map(BFieldElement::new));
1060 let complex6 =
1061 XFieldElement::new([BFieldElement::MAX, 0, BFieldElement::MAX].map(BFieldElement::new));
1062 let complex7 =
1063 XFieldElement::new([BFieldElement::MAX, BFieldElement::MAX, 0].map(BFieldElement::new));
1064
1065 let x_field_elements = vec![
1066 one,
1067 two,
1068 hundred,
1069 x,
1070 x_squared,
1071 one_one_one,
1072 complex0,
1073 complex1,
1074 complex2,
1075 complex3,
1076 complex4,
1077 complex5,
1078 complex6,
1079 complex7,
1080 ];
1081 for x_field_element in x_field_elements.iter() {
1082 let x_field_element_poly: Polynomial<BFieldElement> = (*x_field_element).into();
1083 let (gcd_0, a_0, b_0) = Polynomial::xgcd(
1085 x_field_element_poly.clone(),
1086 XFieldElement::shah_polynomial(),
1087 );
1088 let (gcd_1, b_1, a_1) =
1089 Polynomial::xgcd(XFieldElement::shah_polynomial(), (*x_field_element).into());
1090
1091 assert!(gcd_0.is_one());
1094 assert!(gcd_1.is_one());
1095 assert_eq!(a_0, a_1);
1096 assert_eq!(b_0, b_1);
1097
1098 assert_eq!(
1100 gcd_0,
1101 a_0 * x_field_element_poly + b_0 * XFieldElement::shah_polynomial()
1102 );
1103 }
1104 }
1105
1106 #[macro_rules_attr::apply(test)]
1107 fn x_field_inv_test() {
1108 let one = XFieldElement::new([1, 0, 0].map(BFieldElement::new));
1109 let one_inv = one.inverse();
1110 assert!((one_inv * one).is_one());
1111 assert!((one * one_inv).is_one());
1112
1113 let two = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
1114 let two_inv = two.inverse();
1115 assert!((two_inv * two).is_one());
1116 assert!((two * two_inv).is_one());
1117
1118 let three = XFieldElement::new([3, 0, 0].map(BFieldElement::new));
1119 let three_inv = three.inverse();
1120 assert!((three_inv * three).is_one());
1121 assert!((three * three_inv).is_one());
1122
1123 let hundred = XFieldElement::new([100, 0, 0].map(BFieldElement::new));
1124 let hundred_inv = hundred.inverse();
1125 assert!((hundred_inv * hundred).is_one());
1126 assert!((hundred * hundred_inv).is_one());
1127
1128 let x = XFieldElement::new([0, 1, 0].map(BFieldElement::new));
1129 let x_inv = x.inverse();
1130 assert!((x_inv * x).is_one());
1131 assert!((x * x_inv).is_one());
1132
1133 let mut inverses = XFieldElement::batch_inversion(vec![]);
1135 assert!(inverses.is_empty());
1136 inverses = XFieldElement::batch_inversion(vec![one]);
1137 assert_eq!(1, inverses.len());
1138 assert!(inverses[0].is_one());
1139 inverses = XFieldElement::batch_inversion(vec![two]);
1140 assert_eq!(1, inverses.len());
1141 assert_eq!(two_inv, inverses[0]);
1142 inverses = XFieldElement::batch_inversion(vec![x]);
1143 assert_eq!(1, inverses.len());
1144 assert_eq!(x_inv, inverses[0]);
1145 inverses = XFieldElement::batch_inversion(vec![two, x]);
1146 assert_eq!(2, inverses.len());
1147 assert_eq!(two_inv, inverses[0]);
1148 assert_eq!(x_inv, inverses[1]);
1149
1150 let input = vec![one, two, three, hundred, x];
1151 inverses = XFieldElement::batch_inversion(input.clone());
1152 let inverses_inverses = XFieldElement::batch_inversion(inverses.clone());
1153 assert_eq!(input.len(), inverses.len());
1154 for i in 0..input.len() {
1155 assert!((inverses[i] * input[i]).is_one());
1156 assert_eq!(input[i], inverses_inverses[i]);
1157 }
1158 }
1159
1160 #[macro_rules_attr::apply(proptest)]
1161 fn field_element_inversion(
1162 #[filter(!#x.is_zero())] x: XFieldElement,
1163 #[filter(!#disturbance.is_zero())]
1164 #[filter(#x != #disturbance)]
1165 disturbance: XFieldElement,
1166 ) {
1167 let not_x = x - disturbance;
1168 prop_assert_eq!(XFieldElement::ONE, x * x.inverse());
1169 prop_assert_eq!(XFieldElement::ONE, not_x * not_x.inverse());
1170 prop_assert_ne!(XFieldElement::ONE, x * not_x.inverse());
1171 }
1172
1173 #[macro_rules_attr::apply(proptest)]
1174 fn field_element_batch_inversion(
1175 #[filter(!#xs.iter().any(|x| x.is_zero()))] xs: Vec<XFieldElement>,
1176 ) {
1177 let inverses = XFieldElement::batch_inversion(xs.clone());
1178 for (x, inv) in xs.into_iter().zip(inverses) {
1179 prop_assert_eq!(XFieldElement::ONE, x * inv);
1180 }
1181 }
1182
1183 #[macro_rules_attr::apply(test)]
1184 fn mul_xfe_with_bfe_pbt() {
1185 let test_iterations = 100;
1186 let rands_x: Vec<XFieldElement> = random_elements(test_iterations);
1187 let rands_b: Vec<BFieldElement> = random_elements(test_iterations);
1188 for (mut x, b) in izip!(rands_x, rands_b) {
1189 let res_mul = x * b;
1190 assert_eq!(res_mul.coefficients[0], x.coefficients[0] * b);
1191 assert_eq!(res_mul.coefficients[1], x.coefficients[1] * b);
1192 assert_eq!(res_mul.coefficients[2], x.coefficients[2] * b);
1193
1194 x *= b;
1196 let res_mul_assign = x;
1197 assert_eq!(res_mul, res_mul_assign);
1198 }
1199 }
1200
1201 #[macro_rules_attr::apply(proptest(cases = 1_000))]
1202 fn x_field_division_mul_pbt(a: XFieldElement, b: XFieldElement) {
1203 let a_b = a * b;
1204 let b_a = b * a;
1205 prop_assert_eq!(a_b, b_a);
1206 prop_assert_eq!(a_b / b, a);
1207 prop_assert_eq!(a_b / a, b);
1208 prop_assert_eq!(a * a, a.square());
1209
1210 let mut a_minus_b = a;
1212 a_minus_b -= b;
1213 prop_assert_eq!(a - b, a_minus_b);
1214
1215 let mut a_plus_b = a;
1216 a_plus_b += b;
1217 prop_assert_eq!(a + b, a_plus_b);
1218
1219 let mut a_mul_b = a;
1220 a_mul_b *= b;
1221 prop_assert_eq!(a * b, a_mul_b);
1222
1223 let b_field_b = XFieldElement::new_const(b.coefficients[0]);
1229 let mut a_mul_b_field_b_as_x = a;
1230 a_mul_b_field_b_as_x *= b_field_b;
1231 prop_assert_eq!(a * b_field_b, a_mul_b_field_b_as_x);
1232 prop_assert_eq!(a, a_mul_b_field_b_as_x / b_field_b);
1233 prop_assert_eq!(b_field_b, a_mul_b_field_b_as_x / a);
1234 prop_assert_eq!(a_mul_b_field_b_as_x, a * b.coefficients[0]);
1235 prop_assert_eq!(a_mul_b_field_b_as_x, b.coefficients[0] * a);
1236 let mut a_mul_b_field_b_as_b = a;
1237 a_mul_b_field_b_as_b *= b.coefficients[0];
1238 prop_assert_eq!(a_mul_b_field_b_as_b, a_mul_b_field_b_as_x);
1239
1240 let mut a_plus_b_field_b_as_x = a;
1242 a_plus_b_field_b_as_x += b_field_b;
1243 prop_assert_eq!(a + b_field_b, a_plus_b_field_b_as_x);
1244 prop_assert_eq!(a, a_plus_b_field_b_as_x - b_field_b);
1245 prop_assert_eq!(b_field_b, a_plus_b_field_b_as_x - a);
1246 prop_assert_eq!(a_plus_b_field_b_as_x, a + b.coefficients[0]);
1247 prop_assert_eq!(a_plus_b_field_b_as_x, b.coefficients[0] + a);
1248 let mut a_plus_b_field_b_as_b = a;
1249 a_plus_b_field_b_as_b += b.coefficients[0];
1250 prop_assert_eq!(a_plus_b_field_b_as_b, a_plus_b_field_b_as_x);
1251
1252 let mut a_minus_b_field_b_as_x = a;
1254 a_minus_b_field_b_as_x -= b_field_b;
1255 prop_assert_eq!(a - b_field_b, a_minus_b_field_b_as_x);
1256 prop_assert_eq!(a, a_minus_b_field_b_as_x + b_field_b);
1257 prop_assert_eq!(-b_field_b, a_minus_b_field_b_as_x - a);
1258 prop_assert_eq!(a_minus_b_field_b_as_x, a - b.coefficients[0]);
1259 prop_assert_eq!(-a_minus_b_field_b_as_x, b.coefficients[0] - a);
1260 let mut a_minus_b_field_b_as_b = a;
1261 a_minus_b_field_b_as_b -= b.coefficients[0];
1262 prop_assert_eq!(a_minus_b_field_b_as_b, a_minus_b_field_b_as_x);
1263 }
1264
1265 #[macro_rules_attr::apply(test)]
1266 fn xfe_mod_pow_zero() {
1267 assert!(XFieldElement::ZERO.mod_pow_u32(0).is_one());
1268 assert!(XFieldElement::ZERO.mod_pow_u64(0).is_one());
1269 assert!(XFieldElement::ONE.mod_pow_u32(0).is_one());
1270 assert!(XFieldElement::ONE.mod_pow_u64(0).is_one());
1271 }
1272
1273 #[macro_rules_attr::apply(proptest)]
1274 fn xfe_mod_pow(base: XFieldElement, #[strategy(0_u32..200)] exponent: u32) {
1275 let mut acc = XFieldElement::ONE;
1276 for i in 0..exponent {
1277 assert_eq!(acc, base.mod_pow_u32(i));
1278 acc *= base;
1279 }
1280 }
1281
1282 #[macro_rules_attr::apply(test)]
1283 fn xfe_mod_pow_static() {
1284 let three_to_the_n = |n| xfe!(3).mod_pow_u64(n);
1285 let actual = [0, 1, 2, 3, 4, 5].map(three_to_the_n);
1286 let expected = xfe_array![1, 3, 9, 27, 81, 243];
1287 assert_eq!(expected, actual);
1288 }
1289
1290 #[macro_rules_attr::apply(proptest(cases = 100))]
1291 fn xfe_intt_is_inverse_of_xfe_ntt(
1292 #[strategy(1..=11)]
1293 #[map(|log| 1_usize << log)]
1294 _num_inputs: usize,
1295 #[strategy(vec(arb(), #_num_inputs))] inputs: Vec<XFieldElement>,
1296 ) {
1297 let mut rv = inputs.clone();
1298 ntt::<XFieldElement>(&mut rv);
1299 intt::<XFieldElement>(&mut rv);
1300 prop_assert_eq!(inputs, rv);
1301 }
1302
1303 #[macro_rules_attr::apply(proptest(cases = 40))]
1304 fn xfe_ntt_corresponds_to_polynomial_evaluation(
1305 #[strategy(1..=11)]
1306 #[map(|log_2| 1_u64 << log_2)]
1307 root_order: u64,
1308 #[strategy(vec(arb(), #root_order as usize))] inputs: Vec<XFieldElement>,
1309 ) {
1310 let root = XFieldElement::primitive_root_of_unity(root_order).unwrap();
1311 let mut rv = inputs.clone();
1312 ntt::<XFieldElement>(&mut rv);
1313
1314 let poly = Polynomial::new(inputs);
1315 let domain = root.get_cyclic_group_elements(None);
1316 let evaluations = poly.batch_evaluate(&domain);
1317 prop_assert_eq!(evaluations, rv);
1318 }
1319
1320 fn inverse_by_xgcd(xfe: XFieldElement) -> XFieldElement {
1323 let xfe_as_poly: Polynomial<BFieldElement> = xfe.into();
1324 let (_, a, _) = Polynomial::xgcd(xfe_as_poly, XFieldElement::shah_polynomial());
1325 a.into()
1326 }
1327
1328 #[macro_rules_attr::apply(proptest)]
1329 fn inverse_agrees_with_extended_euclidean_algorithm(
1330 #[filter(!#xfe.is_zero())] xfe: XFieldElement,
1331 ) {
1332 prop_assert_eq!(inverse_by_xgcd(xfe), xfe.inverse());
1333 prop_assert_eq!(XFieldElement::ONE, xfe * xfe.inverse());
1334 }
1335
1336 #[macro_rules_attr::apply(proptest)]
1337 fn inverse_of_lifted_base_field_element_is_lifted_inverse(
1338 #[filter(!#bfe.is_zero())] bfe: BFieldElement,
1339 ) {
1340 let xfe = bfe.lift();
1341 prop_assert_eq!(bfe.inverse().lift(), xfe.inverse());
1342 }
1343
1344 #[macro_rules_attr::apply(test)]
1345 fn inverse_of_elements_with_zero_coefficients() {
1346 for coefficients in [
1347 [1, 0, 0],
1348 [0, 1, 0],
1349 [0, 0, 1],
1350 [1, 1, 0],
1351 [1, 0, 1],
1352 [0, 1, 1],
1353 [BFieldElement::MAX, 0, 0],
1354 [0, BFieldElement::MAX, 0],
1355 [0, 0, BFieldElement::MAX],
1356 ] {
1357 let xfe = xfe!(coefficients);
1358 assert_eq!(inverse_by_xgcd(xfe), xfe.inverse(), "{xfe}");
1359 assert_eq!(XFieldElement::ONE, xfe * xfe.inverse(), "{xfe}");
1360 }
1361 }
1362
1363 #[macro_rules_attr::apply(test)]
1364 fn inverse_or_zero_of_zero_is_zero() {
1365 let zero = XFieldElement::ZERO;
1366 assert_eq!(zero, zero.inverse_or_zero());
1367 }
1368
1369 #[macro_rules_attr::apply(proptest)]
1370 fn inverse_or_zero_of_non_zero_is_inverse(#[filter(!#xfe.is_zero())] xfe: XFieldElement) {
1371 let inv = xfe.inverse_or_zero();
1372 prop_assert_ne!(XFieldElement::ZERO, inv);
1373 prop_assert_eq!(XFieldElement::ONE, xfe * inv);
1374 }
1375
1376 #[macro_rules_attr::apply(test)]
1377 #[should_panic(expected = "Cannot invert the zero element in the extension field.")]
1378 fn multiplicative_inverse_of_zero() {
1379 let zero = XFieldElement::ZERO;
1380 let _ = zero.inverse();
1381 }
1382
1383 #[macro_rules_attr::apply(proptest)]
1384 fn xfe_to_digest_to_xfe_is_invariant(xfe: XFieldElement) {
1385 let digest: Digest = xfe.into();
1386 let xfe2: XFieldElement = digest.try_into().unwrap();
1387 assert_eq!(xfe, xfe2);
1388 }
1389
1390 #[macro_rules_attr::apply(proptest)]
1391 fn converting_random_digest_to_xfield_element_fails(digest: Digest) {
1392 if XFieldElement::try_from(digest).is_ok() {
1393 let reason = "Should not be able to convert random `Digest` to an `XFieldElement`.";
1394 return Err(TestCaseError::Fail(reason.into()));
1395 }
1396 }
1397
1398 #[macro_rules_attr::apply(test)]
1399 fn xfe_macro_can_be_used() {
1400 let x = xfe!(42);
1401 let _ = xfe!(42u32);
1402 let _ = xfe!(-1);
1403 let _ = xfe!(x);
1404 let _ = xfe!([x.coefficients[0], x.coefficients[1], x.coefficients[2]]);
1405 let y = xfe!(bfe!(42));
1406 assert_eq!(x, y);
1407
1408 let a = xfe!([bfe!(42), bfe!(43), bfe!(44)]);
1409 let b = xfe!([42, 43, 44]);
1410 assert_eq!(a, b);
1411
1412 let m: [XFieldElement; 3] = xfe_array![[1, 2, 3], [4, 5, 6], [7, 8, 9]];
1413 let n: Vec<XFieldElement> = xfe_vec![[1, 2, 3], [4, 5, 6], [7, 8, 9]];
1414 assert_eq!(m.to_vec(), n);
1415 }
1416
1417 #[macro_rules_attr::apply(proptest)]
1418 fn xfe_macro_produces_same_result_as_calling_new(coeffs: [BFieldElement; EXTENSION_DEGREE]) {
1419 let xfe = XFieldElement::new(coeffs);
1420 prop_assert_eq!(xfe, xfe!(coeffs));
1421 }
1422
1423 #[macro_rules_attr::apply(proptest)]
1424 fn xfe_macro_produces_same_result_as_calling_new_const(scalar: BFieldElement) {
1425 let xfe = XFieldElement::new_const(scalar);
1426 prop_assert_eq!(xfe, xfe!(scalar));
1427 }
1428
1429 #[macro_rules_attr::apply(proptest)]
1430 fn as_flat_slice_produces_expected_slices(xfes: Vec<XFieldElement>) {
1431 let bfes = xfes.iter().flat_map(|&x| x.coefficients).collect_vec();
1432 prop_assert_eq!(&bfes, as_flat_slice(&xfes));
1433 }
1434}