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]
371 pub fn inverse(&self) -> Self {
372 assert!(
373 !self.is_zero(),
374 "Cannot invert the zero element in the extension field."
375 );
376 let self_as_poly: Polynomial<BFieldElement> = self.to_owned().into();
377 let (_, a, _) = Polynomial::<BFieldElement>::xgcd(self_as_poly, Self::shah_polynomial());
378 a.into()
379 }
380
381 pub fn unlift(&self) -> Option<BFieldElement> {
382 let Self { coefficients } = self;
383 let [bfe, BFieldElement::ZERO, BFieldElement::ZERO] = coefficients else {
384 return None;
385 };
386
387 Some(*bfe)
388 }
389
390 #[deprecated(since = "2.0.0")]
391 #[doc(hidden)]
392 pub fn increment(&mut self, index: usize) {
393 self.coefficients[index].increment();
394 }
395
396 #[deprecated(since = "2.0.0")]
397 #[doc(hidden)]
398 pub fn decrement(&mut self, index: usize) {
399 self.coefficients[index].decrement();
400 }
401}
402
403impl Inverse for XFieldElement {
404 fn inverse(&self) -> Self {
405 self.inverse()
406 }
407}
408
409impl PrimitiveRootOfUnity for XFieldElement {
410 fn primitive_root_of_unity(n: u64) -> Option<XFieldElement> {
411 let b_root = BFieldElement::primitive_root_of_unity(n);
412 b_root.map(XFieldElement::new_const)
413 }
414}
415
416impl Distribution<XFieldElement> for StandardUniform {
417 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> XFieldElement {
418 XFieldElement::new(rng.random())
419 }
420}
421
422impl CyclicGroupGenerator for XFieldElement {
423 fn get_cyclic_group_elements(&self, max: Option<usize>) -> Vec<Self> {
424 let mut val = *self;
425 let mut ret: Vec<Self> = vec![Self::one()];
426
427 loop {
428 ret.push(val);
429 val *= *self;
430 if val.is_one() || max.is_some() && ret.len() >= max.unwrap() {
431 break;
432 }
433 }
434 ret
435 }
436}
437
438impl Display for XFieldElement {
439 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
440 if let Some(bfe) = self.unlift() {
441 return write!(f, "{bfe}_xfe");
442 }
443
444 let [c0, c1, c2] = self.coefficients;
445 write!(f, "({c2:>020}·x² + {c1:>020}·x + {c0:>020})")
446 }
447}
448
449impl Zero for XFieldElement {
450 fn zero() -> Self {
451 Self::ZERO
452 }
453
454 fn is_zero(&self) -> bool {
455 self == &Self::ZERO
456 }
457}
458
459impl ConstZero for XFieldElement {
460 const ZERO: Self = Self::new([BFieldElement::ZERO; EXTENSION_DEGREE]);
461}
462
463impl One for XFieldElement {
464 fn one() -> Self {
465 Self::ONE
466 }
467
468 fn is_one(&self) -> bool {
469 self == &Self::ONE
470 }
471}
472
473impl ConstOne for XFieldElement {
474 const ONE: Self = Self::new([BFieldElement::ONE, BFieldElement::ZERO, BFieldElement::ZERO]);
475}
476
477impl FiniteField for XFieldElement {}
478
479impl Add<XFieldElement> for XFieldElement {
480 type Output = Self;
481
482 #[inline]
483 fn add(self, other: Self) -> Self {
484 let [s0, s1, s2] = self.coefficients;
485 let [o0, o1, o2] = other.coefficients;
486 let coefficients = [s0 + o0, s1 + o1, s2 + o2];
487 Self { coefficients }
488 }
489}
490
491impl Add<BFieldElement> for XFieldElement {
492 type Output = Self;
493
494 #[inline]
495 fn add(mut self, other: BFieldElement) -> Self {
496 self.coefficients[0] += other;
497 self
498 }
499}
500
501impl Add<XFieldElement> for BFieldElement {
503 type Output = XFieldElement;
504
505 #[inline]
506 fn add(self, mut other: XFieldElement) -> XFieldElement {
507 other.coefficients[0] += self;
508 other
509 }
510}
511
512impl Mul<XFieldElement> for XFieldElement {
513 type Output = Self;
514
515 #[inline]
516 fn mul(self, other: Self) -> Self {
517 let [c, b, a] = self.coefficients;
528 let [f, e, d] = other.coefficients;
529
530 let r0 = c * f - a * e - b * d;
531 let r1 = b * f + c * e - a * d + a * e + b * d;
532 let r2 = a * f + b * e + c * d + a * d;
533
534 Self::new([r0, r1, r2])
535 }
536}
537
538impl Mul<BFieldElement> for XFieldElement {
541 type Output = Self;
542
543 #[inline]
544 fn mul(self, other: BFieldElement) -> Self {
545 let coefficients = self.coefficients.map(|c| c * other);
546 Self { coefficients }
547 }
548}
549
550impl Mul<XFieldElement> for BFieldElement {
551 type Output = XFieldElement;
552
553 #[inline]
554 fn mul(self, other: XFieldElement) -> XFieldElement {
555 let coefficients = other.coefficients.map(|c| c * self);
556 XFieldElement { coefficients }
557 }
558}
559
560impl Neg for XFieldElement {
561 type Output = Self;
562
563 #[inline]
564 fn neg(self) -> Self {
565 let coefficients = self.coefficients.map(Neg::neg);
566 Self { coefficients }
567 }
568}
569
570impl Sub<XFieldElement> for XFieldElement {
571 type Output = Self;
572
573 #[inline]
574 fn sub(self, other: Self) -> Self {
575 self + (-other)
576 }
577}
578
579impl Sub<BFieldElement> for XFieldElement {
580 type Output = Self;
581
582 #[inline]
583 fn sub(self, other: BFieldElement) -> Self {
584 self + (-other)
585 }
586}
587
588impl Sub<XFieldElement> for BFieldElement {
589 type Output = XFieldElement;
590
591 #[inline]
592 fn sub(self, other: XFieldElement) -> XFieldElement {
593 self + (-other)
594 }
595}
596
597impl AddAssign<XFieldElement> for XFieldElement {
598 #[inline]
599 fn add_assign(&mut self, rhs: Self) {
600 self.coefficients[0] += rhs.coefficients[0];
601 self.coefficients[1] += rhs.coefficients[1];
602 self.coefficients[2] += rhs.coefficients[2];
603 }
604}
605
606impl AddAssign<BFieldElement> for XFieldElement {
607 #[inline]
608 fn add_assign(&mut self, rhs: BFieldElement) {
609 self.coefficients[0] += rhs;
610 }
611}
612
613impl MulAssign<XFieldElement> for XFieldElement {
614 #[inline]
615 fn mul_assign(&mut self, rhs: Self) {
616 *self = *self * rhs;
617 }
618}
619
620impl MulAssign<BFieldElement> for XFieldElement {
621 #[inline]
622 fn mul_assign(&mut self, rhs: BFieldElement) {
623 *self = *self * rhs;
624 }
625}
626
627impl SubAssign<XFieldElement> for XFieldElement {
628 #[inline]
629 fn sub_assign(&mut self, rhs: Self) {
630 self.coefficients[0] -= rhs.coefficients[0];
631 self.coefficients[1] -= rhs.coefficients[1];
632 self.coefficients[2] -= rhs.coefficients[2];
633 }
634}
635
636impl SubAssign<BFieldElement> for XFieldElement {
637 #[inline]
638 fn sub_assign(&mut self, rhs: BFieldElement) {
639 self.coefficients[0] -= rhs;
640 }
641}
642
643impl Div for XFieldElement {
644 type Output = Self;
645
646 #[expect(clippy::suspicious_arithmetic_impl)]
647 fn div(self, other: Self) -> Self {
648 self * other.inverse()
649 }
650}
651
652impl ModPowU64 for XFieldElement {
653 #[inline]
654 fn mod_pow_u64(&self, exponent: u64) -> Self {
655 let mut x = *self;
656 let mut result = Self::one();
657 let mut i = exponent;
658
659 while i > 0 {
660 if i & 1 == 1 {
661 result *= x;
662 }
663
664 x *= x;
665 i >>= 1;
666 }
667
668 result
669 }
670}
671
672impl ModPowU32 for XFieldElement {
673 #[inline]
674 fn mod_pow_u32(&self, exp: u32) -> Self {
675 self.mod_pow_u64(exp as u64)
676 }
677}
678
679#[cfg(test)]
680#[cfg_attr(coverage_nightly, coverage(off))]
681mod tests {
682 use itertools::Itertools;
683 use itertools::izip;
684 use num_traits::ConstOne;
685 use proptest::collection::vec;
686 use proptest::prelude::*;
687 use proptest_arbitrary_adapter::arb;
688
689 use super::*;
690 use crate::bfe;
691 use crate::math::b_field_element::*;
692 use crate::math::ntt::intt;
693 use crate::math::ntt::ntt;
694 use crate::math::other::random_elements;
695 use crate::tests::proptest;
696 use crate::tests::test;
697
698 impl proptest::arbitrary::Arbitrary for XFieldElement {
699 type Parameters = ();
700
701 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
702 arb().boxed()
703 }
704
705 type Strategy = BoxedStrategy<Self>;
706 }
707
708 #[macro_rules_attr::apply(test)]
709 fn display_is_as_expected() {
710 assert_eq!("42_xfe", xfe!(42).to_string());
711 assert_eq!("(3·x² + 2·x + 1)", xfe!([1, 2, 3]).to_string());
712 }
713
714 #[macro_rules_attr::apply(test)]
715 fn one_zero_test() {
716 let one = XFieldElement::one();
717 assert!(one.is_one());
718 assert!(one.coefficients[0].is_one());
719 assert!(one.coefficients[1].is_zero());
720 assert!(one.coefficients[2].is_zero());
721 assert_eq!(one, XFieldElement::ONE);
722 let zero = XFieldElement::zero();
723 assert!(zero.is_zero());
724 assert!(zero.coefficients[0].is_zero());
725 assert!(zero.coefficients[1].is_zero());
726 assert!(zero.coefficients[2].is_zero());
727 assert_eq!(zero, XFieldElement::ZERO);
728 let two = XFieldElement::new([
729 BFieldElement::new(2),
730 BFieldElement::ZERO,
731 BFieldElement::ZERO,
732 ]);
733 assert!(!two.is_one());
734 assert!(!zero.is_one());
735 let one_as_constant_term_0 = XFieldElement::new([
736 BFieldElement::new(1),
737 BFieldElement::ONE,
738 BFieldElement::ZERO,
739 ]);
740 let one_as_constant_term_1 = XFieldElement::new([
741 BFieldElement::new(1),
742 BFieldElement::ZERO,
743 BFieldElement::ONE,
744 ]);
745 assert!(!one_as_constant_term_0.is_one());
746 assert!(!one_as_constant_term_1.is_one());
747 assert!(!one_as_constant_term_0.is_zero());
748 assert!(!one_as_constant_term_1.is_zero());
749 }
750
751 #[macro_rules_attr::apply(test)]
752 fn x_field_random_element_generation_test() {
753 let rand_xs: Vec<XFieldElement> = random_elements(14);
754 assert_eq!(14, rand_xs.len());
755
756 assert!(rand_xs.into_iter().all_unique());
758 }
759
760 #[macro_rules_attr::apply(proptest)]
761 fn unlifting_random_xfe_doesnt_work(xfe: XFieldElement) {
762 prop_assert!(xfe.unlift().is_none());
763 }
764
765 #[macro_rules_attr::apply(test)]
766 fn summing_gives_expected_result() {
767 let empty_sum = [].into_iter().sum();
768 assert_eq!(XFieldElement::ZERO, empty_sum);
769
770 let a = xfe!([1, 0, 0]);
771 let b = xfe!([0, 2, 0]);
772 let c = xfe!([0, 0, 3]);
773 let d = xfe!([40, 50, 60]);
774 let sum = [a, b, c, d].into_iter().sum();
775
776 assert_eq!(xfe!([41, 52, 63]), sum);
777 }
778
779 #[macro_rules_attr::apply(proptest)]
780 fn bfe_vector_of_correct_length_can_become_xfe(
781 #[strategy(vec(arb(), EXTENSION_DEGREE))] bfes: Vec<BFieldElement>,
782 ) {
783 prop_assert!(XFieldElement::try_from(bfes).is_ok());
784 }
785
786 #[macro_rules_attr::apply(proptest)]
787 fn bfe_vector_of_incorrect_length_cannot_become_xfe(
788 #[filter(#bfes.len() != EXTENSION_DEGREE)] bfes: Vec<BFieldElement>,
789 ) {
790 prop_assert!(XFieldElement::try_from(bfes).is_err());
791 }
792
793 #[macro_rules_attr::apply(test)]
796 #[expect(deprecated)]
797 fn incr_decr_test() {
798 let one_const = XFieldElement::new([1, 0, 0].map(BFieldElement::new));
799 let two_const = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
800 let one_x = XFieldElement::new([0, 1, 0].map(BFieldElement::new));
801 let two_x = XFieldElement::new([0, 2, 0].map(BFieldElement::new));
802 let one_x_squared = XFieldElement::new([0, 0, 1].map(BFieldElement::new));
803 let two_x_squared = XFieldElement::new([0, 0, 2].map(BFieldElement::new));
804 let max_const = XFieldElement::new([BFieldElement::MAX, 0, 0].map(BFieldElement::new));
805 let max_x = XFieldElement::new([0, BFieldElement::MAX, 0].map(BFieldElement::new));
806 let max_x_squared = XFieldElement::new([0, 0, BFieldElement::MAX].map(BFieldElement::new));
807 let mut val = XFieldElement::ZERO;
808 val.increment(0);
809 assert!(val.is_one());
810 val.increment(0);
811 assert_eq!(two_const, val);
812 val.decrement(0);
813 assert!(val.is_one());
814 val.decrement(0);
815 assert!(val.is_zero());
816 val.decrement(0);
817 assert_eq!(max_const, val);
818 val.decrement(0);
819 assert_eq!(max_const - XFieldElement::ONE, val);
820 val.decrement(0);
821 assert_eq!(max_const - XFieldElement::ONE - XFieldElement::ONE, val);
822 val.increment(0);
823 val.increment(0);
824 val.increment(0);
825 assert!(val.is_zero());
826 val.increment(1);
827 assert_eq!(one_x, val);
828 val.increment(1);
829 assert_eq!(two_x, val);
830 val.decrement(1);
831 val.decrement(1);
832 assert!(val.is_zero());
833 val.decrement(1);
834 assert_eq!(max_x, val);
835 val.increment(1);
836 val.increment(2);
837 assert_eq!(one_x_squared, val);
838 val.increment(2);
839 assert_eq!(two_x_squared, val);
840 val.decrement(2);
841 val.decrement(2);
842 assert!(val.is_zero());
843 val.decrement(2);
844 assert_eq!(max_x_squared, val);
845 val.decrement(1);
846 val.decrement(0);
847 assert_eq!(max_x_squared + max_x + max_const, val);
848 val.decrement(2);
849 val.decrement(1);
850 val.decrement(0);
851 assert_eq!(
852 max_x_squared + max_x + max_const - one_const - one_x - one_x_squared,
853 val
854 );
855 }
856
857 #[macro_rules_attr::apply(test)]
858 fn x_field_add_test() {
859 let poly1 = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
860 let poly2 = XFieldElement::new([3, 0, 0].map(BFieldElement::new));
861
862 let mut poly_sum = XFieldElement::new([5, 0, 0].map(BFieldElement::new));
863 assert_eq!(poly_sum, poly1 + poly2);
864
865 let poly3 = XFieldElement::new([0, 5, 0].map(BFieldElement::new));
866 let poly4 = XFieldElement::new([0, 7, 0].map(BFieldElement::new));
867
868 poly_sum = XFieldElement::new([0, 12, 0].map(BFieldElement::new));
869 assert_eq!(poly_sum, poly3 + poly4);
870
871 let poly5 = XFieldElement::new([0, 0, 14].map(BFieldElement::new));
872 let poly6 = XFieldElement::new([0, 0, 23].map(BFieldElement::new));
873
874 poly_sum = XFieldElement::new([0, 0, 37].map(BFieldElement::new));
875 assert_eq!(poly_sum, poly5 + poly6);
876
877 let poly7 = XFieldElement::new([0, 0, BFieldElement::MAX].map(BFieldElement::new));
878 let poly8 = XFieldElement::new([0, 0, 23].map(BFieldElement::new));
879
880 poly_sum = XFieldElement::new([0, 0, 22].map(BFieldElement::new));
881 assert_eq!(poly_sum, poly7 + poly8);
882
883 let poly9 = XFieldElement::new([BFieldElement::MAX - 2, 12, 4].map(BFieldElement::new));
884 let poly10 = XFieldElement::new([2, 45000, BFieldElement::MAX - 3].map(BFieldElement::new));
885
886 poly_sum = XFieldElement::new([BFieldElement::MAX, 45012, 0].map(BFieldElement::new));
887 assert_eq!(poly_sum, poly9 + poly10);
888 }
889
890 #[macro_rules_attr::apply(test)]
891 fn x_field_sub_test() {
892 let poly1 = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
893 let poly2 = XFieldElement::new([3, 0, 0].map(BFieldElement::new));
894
895 let mut poly_diff = XFieldElement::new([1, 0, 0].map(BFieldElement::new));
896 assert_eq!(poly_diff, poly2 - poly1);
897
898 let poly3 = XFieldElement::new([0, 5, 0].map(BFieldElement::new));
899 let poly4 = XFieldElement::new([0, 7, 0].map(BFieldElement::new));
900
901 poly_diff = XFieldElement::new([0, 2, 0].map(BFieldElement::new));
902 assert_eq!(poly_diff, poly4 - poly3);
903
904 let poly5 = XFieldElement::new([0, 0, 14].map(BFieldElement::new));
905 let poly6 = XFieldElement::new([0, 0, 23].map(BFieldElement::new));
906
907 poly_diff = XFieldElement::new([0, 0, 9].map(BFieldElement::new));
908 assert_eq!(poly_diff, poly6 - poly5);
909
910 let poly7 = XFieldElement::new([0, 0, BFieldElement::MAX].map(BFieldElement::new));
911 let poly8 = XFieldElement::new([0, 0, 23].map(BFieldElement::new));
912
913 poly_diff = XFieldElement::new([0, 0, 24].map(BFieldElement::new));
914 assert_eq!(poly_diff, poly8 - poly7);
915
916 let poly9 = XFieldElement::new([BFieldElement::MAX - 2, 12, 4].map(BFieldElement::new));
917 let poly10 = XFieldElement::new([2, 45000, BFieldElement::MAX - 3].map(BFieldElement::new));
918
919 poly_diff = XFieldElement::new([5, 44988, BFieldElement::MAX - 7].map(BFieldElement::new));
920 assert_eq!(poly_diff, poly10 - poly9);
921 }
922
923 #[macro_rules_attr::apply(test)]
924 fn x_field_mul_test() {
925 let poly1 = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
926 let poly2 = XFieldElement::new([3, 0, 0].map(BFieldElement::new));
927
928 let poly12_product = XFieldElement::new([6, 0, 0].map(BFieldElement::new));
929 assert_eq!(poly12_product, poly1 * poly2);
930
931 let poly3 = XFieldElement::new([0, 3, 0].map(BFieldElement::new));
932 let poly4 = XFieldElement::new([0, 3, 0].map(BFieldElement::new));
933
934 let poly34_product = XFieldElement::new([0, 0, 9].map(BFieldElement::new));
935 assert_eq!(poly34_product, poly3 * poly4);
936
937 let poly5 = XFieldElement::new([125, 0, 0].map(BFieldElement::new));
938 let poly6 = XFieldElement::new([0, 0, 5].map(BFieldElement::new));
939
940 let poly56_product = XFieldElement::new([0, 0, 625].map(BFieldElement::new));
941 assert_eq!(poly56_product, poly5 * poly6);
942
943 let poly7 = XFieldElement::new([0, 0, 1].map(BFieldElement::new));
945 let poly8 = XFieldElement::new([0, 0, 1].map(BFieldElement::new));
946
947 let poly78_product = XFieldElement::new([0, BFieldElement::MAX, 1].map(BFieldElement::new));
948 assert_eq!(poly78_product, poly7 * poly8);
949
950 let poly9 = XFieldElement::new([0, 1, 0].map(BFieldElement::new));
952 let poly10 = XFieldElement::new([0, 0, 1].map(BFieldElement::new));
953
954 let poly910_product =
955 XFieldElement::new([BFieldElement::MAX, 1, 0].map(BFieldElement::new));
956 assert_eq!(poly910_product, poly9 * poly10);
957
958 let poly11 = XFieldElement::new([13, 2, 3].map(BFieldElement::new));
960 let poly12 = XFieldElement::new([19, 0, 5].map(BFieldElement::new));
961
962 let poly1112_product = XFieldElement::new([237, 33, 137].map(BFieldElement::new));
963 assert_eq!(poly1112_product, poly11 * poly12);
964 }
965
966 #[macro_rules_attr::apply(test)]
967 fn x_field_overloaded_arithmetic_test() {
968 let mut rng = rand::rng();
969 for _ in 0..100 {
970 let xfe = rng.random::<XFieldElement>();
971 let bfe = rng.random::<BFieldElement>();
972
973 let expected_add = xfe + bfe.lift();
977 assert_eq!(expected_add, bfe.lift() + xfe);
978 assert_eq!(expected_add, xfe + bfe);
979 assert_eq!(expected_add, bfe + xfe);
980
981 let expected_mul = xfe * bfe.lift();
985 assert_eq!(expected_mul, bfe.lift() * xfe);
986 assert_eq!(expected_mul, xfe * bfe);
987 assert_eq!(expected_mul, bfe * xfe);
988
989 assert_eq!(xfe - bfe.lift(), xfe - bfe);
992 assert_eq!(bfe.lift() - xfe, bfe - xfe);
993 }
994 }
995
996 #[macro_rules_attr::apply(test)]
997 fn x_field_into_test() {
998 let zero_poly: XFieldElement = Polynomial::<BFieldElement>::new(vec![]).into();
999 assert!(zero_poly.is_zero());
1000
1001 let shah_zero: XFieldElement = XFieldElement::shah_polynomial().into();
1002 assert!(shah_zero.is_zero());
1003
1004 let neg_shah_zero: XFieldElement =
1005 XFieldElement::shah_polynomial().scalar_mul(bfe!(-1)).into();
1006 assert!(neg_shah_zero.is_zero());
1007 }
1008
1009 #[macro_rules_attr::apply(test)]
1010 fn x_field_xgcp_test() {
1011 let one = XFieldElement::new([1, 0, 0].map(BFieldElement::new));
1014 let two = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
1015 let hundred = XFieldElement::new([100, 0, 0].map(BFieldElement::new));
1016 let x = XFieldElement::new([0, 1, 0].map(BFieldElement::new));
1017 let x_squared = XFieldElement::new([0, 0, 1].map(BFieldElement::new));
1018 let one_one_one = XFieldElement::new([1, 1, 1].map(BFieldElement::new));
1019 let complex0 = XFieldElement::new([450, 967, 21444444201].map(BFieldElement::new));
1020 let complex1 = XFieldElement::new([456230, 0, 4563210789].map(BFieldElement::new));
1021 let complex2 = XFieldElement::new([0, 96701, 456703214].map(BFieldElement::new));
1022 let complex3 = XFieldElement::new([124504, 9654677, 0].map(BFieldElement::new));
1023 let complex4 = XFieldElement::new(
1024 [BFieldElement::MAX, BFieldElement::MAX, BFieldElement::MAX].map(BFieldElement::new),
1025 );
1026 let complex5 =
1027 XFieldElement::new([0, BFieldElement::MAX, BFieldElement::MAX].map(BFieldElement::new));
1028 let complex6 =
1029 XFieldElement::new([BFieldElement::MAX, 0, BFieldElement::MAX].map(BFieldElement::new));
1030 let complex7 =
1031 XFieldElement::new([BFieldElement::MAX, BFieldElement::MAX, 0].map(BFieldElement::new));
1032
1033 let x_field_elements = vec![
1034 one,
1035 two,
1036 hundred,
1037 x,
1038 x_squared,
1039 one_one_one,
1040 complex0,
1041 complex1,
1042 complex2,
1043 complex3,
1044 complex4,
1045 complex5,
1046 complex6,
1047 complex7,
1048 ];
1049 for x_field_element in x_field_elements.iter() {
1050 let x_field_element_poly: Polynomial<BFieldElement> = (*x_field_element).into();
1051 let (gcd_0, a_0, b_0) = Polynomial::xgcd(
1053 x_field_element_poly.clone(),
1054 XFieldElement::shah_polynomial(),
1055 );
1056 let (gcd_1, b_1, a_1) =
1057 Polynomial::xgcd(XFieldElement::shah_polynomial(), (*x_field_element).into());
1058
1059 assert!(gcd_0.is_one());
1062 assert!(gcd_1.is_one());
1063 assert_eq!(a_0, a_1);
1064 assert_eq!(b_0, b_1);
1065
1066 assert_eq!(
1068 gcd_0,
1069 a_0 * x_field_element_poly + b_0 * XFieldElement::shah_polynomial()
1070 );
1071 }
1072 }
1073
1074 #[macro_rules_attr::apply(test)]
1075 fn x_field_inv_test() {
1076 let one = XFieldElement::new([1, 0, 0].map(BFieldElement::new));
1077 let one_inv = one.inverse();
1078 assert!((one_inv * one).is_one());
1079 assert!((one * one_inv).is_one());
1080
1081 let two = XFieldElement::new([2, 0, 0].map(BFieldElement::new));
1082 let two_inv = two.inverse();
1083 assert!((two_inv * two).is_one());
1084 assert!((two * two_inv).is_one());
1085
1086 let three = XFieldElement::new([3, 0, 0].map(BFieldElement::new));
1087 let three_inv = three.inverse();
1088 assert!((three_inv * three).is_one());
1089 assert!((three * three_inv).is_one());
1090
1091 let hundred = XFieldElement::new([100, 0, 0].map(BFieldElement::new));
1092 let hundred_inv = hundred.inverse();
1093 assert!((hundred_inv * hundred).is_one());
1094 assert!((hundred * hundred_inv).is_one());
1095
1096 let x = XFieldElement::new([0, 1, 0].map(BFieldElement::new));
1097 let x_inv = x.inverse();
1098 assert!((x_inv * x).is_one());
1099 assert!((x * x_inv).is_one());
1100
1101 let mut inverses = XFieldElement::batch_inversion(vec![]);
1103 assert!(inverses.is_empty());
1104 inverses = XFieldElement::batch_inversion(vec![one]);
1105 assert_eq!(1, inverses.len());
1106 assert!(inverses[0].is_one());
1107 inverses = XFieldElement::batch_inversion(vec![two]);
1108 assert_eq!(1, inverses.len());
1109 assert_eq!(two_inv, inverses[0]);
1110 inverses = XFieldElement::batch_inversion(vec![x]);
1111 assert_eq!(1, inverses.len());
1112 assert_eq!(x_inv, inverses[0]);
1113 inverses = XFieldElement::batch_inversion(vec![two, x]);
1114 assert_eq!(2, inverses.len());
1115 assert_eq!(two_inv, inverses[0]);
1116 assert_eq!(x_inv, inverses[1]);
1117
1118 let input = vec![one, two, three, hundred, x];
1119 inverses = XFieldElement::batch_inversion(input.clone());
1120 let inverses_inverses = XFieldElement::batch_inversion(inverses.clone());
1121 assert_eq!(input.len(), inverses.len());
1122 for i in 0..input.len() {
1123 assert!((inverses[i] * input[i]).is_one());
1124 assert_eq!(input[i], inverses_inverses[i]);
1125 }
1126 }
1127
1128 #[macro_rules_attr::apply(proptest)]
1129 fn field_element_inversion(
1130 #[filter(!#x.is_zero())] x: XFieldElement,
1131 #[filter(!#disturbance.is_zero())]
1132 #[filter(#x != #disturbance)]
1133 disturbance: XFieldElement,
1134 ) {
1135 let not_x = x - disturbance;
1136 prop_assert_eq!(XFieldElement::ONE, x * x.inverse());
1137 prop_assert_eq!(XFieldElement::ONE, not_x * not_x.inverse());
1138 prop_assert_ne!(XFieldElement::ONE, x * not_x.inverse());
1139 }
1140
1141 #[macro_rules_attr::apply(proptest)]
1142 fn field_element_batch_inversion(
1143 #[filter(!#xs.iter().any(|x| x.is_zero()))] xs: Vec<XFieldElement>,
1144 ) {
1145 let inverses = XFieldElement::batch_inversion(xs.clone());
1146 for (x, inv) in xs.into_iter().zip(inverses) {
1147 prop_assert_eq!(XFieldElement::ONE, x * inv);
1148 }
1149 }
1150
1151 #[macro_rules_attr::apply(test)]
1152 fn mul_xfe_with_bfe_pbt() {
1153 let test_iterations = 100;
1154 let rands_x: Vec<XFieldElement> = random_elements(test_iterations);
1155 let rands_b: Vec<BFieldElement> = random_elements(test_iterations);
1156 for (mut x, b) in izip!(rands_x, rands_b) {
1157 let res_mul = x * b;
1158 assert_eq!(res_mul.coefficients[0], x.coefficients[0] * b);
1159 assert_eq!(res_mul.coefficients[1], x.coefficients[1] * b);
1160 assert_eq!(res_mul.coefficients[2], x.coefficients[2] * b);
1161
1162 x *= b;
1164 let res_mul_assign = x;
1165 assert_eq!(res_mul, res_mul_assign);
1166 }
1167 }
1168
1169 #[macro_rules_attr::apply(proptest(cases = 1_000))]
1170 fn x_field_division_mul_pbt(a: XFieldElement, b: XFieldElement) {
1171 let a_b = a * b;
1172 let b_a = b * a;
1173 prop_assert_eq!(a_b, b_a);
1174 prop_assert_eq!(a_b / b, a);
1175 prop_assert_eq!(a_b / a, b);
1176 prop_assert_eq!(a * a, a.square());
1177
1178 let mut a_minus_b = a;
1180 a_minus_b -= b;
1181 prop_assert_eq!(a - b, a_minus_b);
1182
1183 let mut a_plus_b = a;
1184 a_plus_b += b;
1185 prop_assert_eq!(a + b, a_plus_b);
1186
1187 let mut a_mul_b = a;
1188 a_mul_b *= b;
1189 prop_assert_eq!(a * b, a_mul_b);
1190
1191 let b_field_b = XFieldElement::new_const(b.coefficients[0]);
1197 let mut a_mul_b_field_b_as_x = a;
1198 a_mul_b_field_b_as_x *= b_field_b;
1199 prop_assert_eq!(a * b_field_b, a_mul_b_field_b_as_x);
1200 prop_assert_eq!(a, a_mul_b_field_b_as_x / b_field_b);
1201 prop_assert_eq!(b_field_b, a_mul_b_field_b_as_x / a);
1202 prop_assert_eq!(a_mul_b_field_b_as_x, a * b.coefficients[0]);
1203 prop_assert_eq!(a_mul_b_field_b_as_x, b.coefficients[0] * a);
1204 let mut a_mul_b_field_b_as_b = a;
1205 a_mul_b_field_b_as_b *= b.coefficients[0];
1206 prop_assert_eq!(a_mul_b_field_b_as_b, a_mul_b_field_b_as_x);
1207
1208 let mut a_plus_b_field_b_as_x = a;
1210 a_plus_b_field_b_as_x += b_field_b;
1211 prop_assert_eq!(a + b_field_b, a_plus_b_field_b_as_x);
1212 prop_assert_eq!(a, a_plus_b_field_b_as_x - b_field_b);
1213 prop_assert_eq!(b_field_b, a_plus_b_field_b_as_x - a);
1214 prop_assert_eq!(a_plus_b_field_b_as_x, a + b.coefficients[0]);
1215 prop_assert_eq!(a_plus_b_field_b_as_x, b.coefficients[0] + a);
1216 let mut a_plus_b_field_b_as_b = a;
1217 a_plus_b_field_b_as_b += b.coefficients[0];
1218 prop_assert_eq!(a_plus_b_field_b_as_b, a_plus_b_field_b_as_x);
1219
1220 let mut a_minus_b_field_b_as_x = a;
1222 a_minus_b_field_b_as_x -= b_field_b;
1223 prop_assert_eq!(a - b_field_b, a_minus_b_field_b_as_x);
1224 prop_assert_eq!(a, a_minus_b_field_b_as_x + b_field_b);
1225 prop_assert_eq!(-b_field_b, a_minus_b_field_b_as_x - a);
1226 prop_assert_eq!(a_minus_b_field_b_as_x, a - b.coefficients[0]);
1227 prop_assert_eq!(-a_minus_b_field_b_as_x, b.coefficients[0] - a);
1228 let mut a_minus_b_field_b_as_b = a;
1229 a_minus_b_field_b_as_b -= b.coefficients[0];
1230 prop_assert_eq!(a_minus_b_field_b_as_b, a_minus_b_field_b_as_x);
1231 }
1232
1233 #[macro_rules_attr::apply(test)]
1234 fn xfe_mod_pow_zero() {
1235 assert!(XFieldElement::ZERO.mod_pow_u32(0).is_one());
1236 assert!(XFieldElement::ZERO.mod_pow_u64(0).is_one());
1237 assert!(XFieldElement::ONE.mod_pow_u32(0).is_one());
1238 assert!(XFieldElement::ONE.mod_pow_u64(0).is_one());
1239 }
1240
1241 #[macro_rules_attr::apply(proptest)]
1242 fn xfe_mod_pow(base: XFieldElement, #[strategy(0_u32..200)] exponent: u32) {
1243 let mut acc = XFieldElement::ONE;
1244 for i in 0..exponent {
1245 assert_eq!(acc, base.mod_pow_u32(i));
1246 acc *= base;
1247 }
1248 }
1249
1250 #[macro_rules_attr::apply(test)]
1251 fn xfe_mod_pow_static() {
1252 let three_to_the_n = |n| xfe!(3).mod_pow_u64(n);
1253 let actual = [0, 1, 2, 3, 4, 5].map(three_to_the_n);
1254 let expected = xfe_array![1, 3, 9, 27, 81, 243];
1255 assert_eq!(expected, actual);
1256 }
1257
1258 #[macro_rules_attr::apply(proptest(cases = 100))]
1259 fn xfe_intt_is_inverse_of_xfe_ntt(
1260 #[strategy(1..=11)]
1261 #[map(|log| 1_usize << log)]
1262 _num_inputs: usize,
1263 #[strategy(vec(arb(), #_num_inputs))] inputs: Vec<XFieldElement>,
1264 ) {
1265 let mut rv = inputs.clone();
1266 ntt::<XFieldElement>(&mut rv);
1267 intt::<XFieldElement>(&mut rv);
1268 prop_assert_eq!(inputs, rv);
1269 }
1270
1271 #[macro_rules_attr::apply(proptest(cases = 40))]
1272 fn xfe_ntt_corresponds_to_polynomial_evaluation(
1273 #[strategy(1..=11)]
1274 #[map(|log_2| 1_u64 << log_2)]
1275 root_order: u64,
1276 #[strategy(vec(arb(), #root_order as usize))] inputs: Vec<XFieldElement>,
1277 ) {
1278 let root = XFieldElement::primitive_root_of_unity(root_order).unwrap();
1279 let mut rv = inputs.clone();
1280 ntt::<XFieldElement>(&mut rv);
1281
1282 let poly = Polynomial::new(inputs);
1283 let domain = root.get_cyclic_group_elements(None);
1284 let evaluations = poly.batch_evaluate(&domain);
1285 prop_assert_eq!(evaluations, rv);
1286 }
1287
1288 #[macro_rules_attr::apply(test)]
1289 fn inverse_or_zero_of_zero_is_zero() {
1290 let zero = XFieldElement::ZERO;
1291 assert_eq!(zero, zero.inverse_or_zero());
1292 }
1293
1294 #[macro_rules_attr::apply(proptest)]
1295 fn inverse_or_zero_of_non_zero_is_inverse(#[filter(!#xfe.is_zero())] xfe: XFieldElement) {
1296 let inv = xfe.inverse_or_zero();
1297 prop_assert_ne!(XFieldElement::ZERO, inv);
1298 prop_assert_eq!(XFieldElement::ONE, xfe * inv);
1299 }
1300
1301 #[macro_rules_attr::apply(test)]
1302 #[should_panic(expected = "Cannot invert the zero element in the extension field.")]
1303 fn multiplicative_inverse_of_zero() {
1304 let zero = XFieldElement::ZERO;
1305 let _ = zero.inverse();
1306 }
1307
1308 #[macro_rules_attr::apply(proptest)]
1309 fn xfe_to_digest_to_xfe_is_invariant(xfe: XFieldElement) {
1310 let digest: Digest = xfe.into();
1311 let xfe2: XFieldElement = digest.try_into().unwrap();
1312 assert_eq!(xfe, xfe2);
1313 }
1314
1315 #[macro_rules_attr::apply(proptest)]
1316 fn converting_random_digest_to_xfield_element_fails(digest: Digest) {
1317 if XFieldElement::try_from(digest).is_ok() {
1318 let reason = "Should not be able to convert random `Digest` to an `XFieldElement`.";
1319 return Err(TestCaseError::Fail(reason.into()));
1320 }
1321 }
1322
1323 #[macro_rules_attr::apply(test)]
1324 fn xfe_macro_can_be_used() {
1325 let x = xfe!(42);
1326 let _ = xfe!(42u32);
1327 let _ = xfe!(-1);
1328 let _ = xfe!(x);
1329 let _ = xfe!([x.coefficients[0], x.coefficients[1], x.coefficients[2]]);
1330 let y = xfe!(bfe!(42));
1331 assert_eq!(x, y);
1332
1333 let a = xfe!([bfe!(42), bfe!(43), bfe!(44)]);
1334 let b = xfe!([42, 43, 44]);
1335 assert_eq!(a, b);
1336
1337 let m: [XFieldElement; 3] = xfe_array![[1, 2, 3], [4, 5, 6], [7, 8, 9]];
1338 let n: Vec<XFieldElement> = xfe_vec![[1, 2, 3], [4, 5, 6], [7, 8, 9]];
1339 assert_eq!(m.to_vec(), n);
1340 }
1341
1342 #[macro_rules_attr::apply(proptest)]
1343 fn xfe_macro_produces_same_result_as_calling_new(coeffs: [BFieldElement; EXTENSION_DEGREE]) {
1344 let xfe = XFieldElement::new(coeffs);
1345 prop_assert_eq!(xfe, xfe!(coeffs));
1346 }
1347
1348 #[macro_rules_attr::apply(proptest)]
1349 fn xfe_macro_produces_same_result_as_calling_new_const(scalar: BFieldElement) {
1350 let xfe = XFieldElement::new_const(scalar);
1351 prop_assert_eq!(xfe, xfe!(scalar));
1352 }
1353
1354 #[macro_rules_attr::apply(proptest)]
1355 fn as_flat_slice_produces_expected_slices(xfes: Vec<XFieldElement>) {
1356 let bfes = xfes.iter().flat_map(|&x| x.coefficients).collect_vec();
1357 prop_assert_eq!(&bfes, as_flat_slice(&xfes));
1358 }
1359}