Skip to main content

twenty_first/math/
x_field_element.rs

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// due to a bug in rustfmt, the formatted `derive` would exceed 100 characters
41// see also: https://github.com/rust-lang/rustfmt/issues/5796
42#[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/// Simplifies constructing [extension field element](XFieldElement)s.
62///
63/// The type [`XFieldElement`] must be in scope for this macro to work.
64/// See [`XFieldElement::from`] for supported types.
65///
66/// # Examples
67///
68/// ```
69/// # use twenty_first::prelude::*;
70/// let a = xfe!(1);
71/// let b = xfe!([2, 0, 5]);
72/// let c = xfe!([3, 0, 2 + 3]);
73/// assert_eq!(a + b, c);
74/// ```
75#[macro_export]
76macro_rules! xfe {
77    ($value:expr) => {
78        XFieldElement::from($value)
79    };
80}
81
82/// Simplifies constructing vectors of [extension field element][XFieldElement]s.
83///
84/// The type [`XFieldElement`] must be in scope for this macro to work. See also [`xfe!`].
85///
86/// # Examples
87///
88/// Vector of [constants](XFieldElement::new_const).
89///
90/// ```
91/// # use twenty_first::prelude::*;
92/// let a = xfe_vec![1, 2, 3];
93/// let b = vec![xfe!(1), xfe!(2), xfe!(3)];
94/// assert_eq!(a, b);
95/// ```
96///
97/// Vector of general [extension field element](XFieldElement)s.
98///
99/// ```
100/// # use twenty_first::prelude::*;
101/// let a = xfe_vec![[1, 0, 0], [0, 2, 0], [0, 0, 3]];
102/// let b = vec![xfe!([1, 0, 0]), xfe!([0, 2, 0]), xfe!([0, 0, 3])];
103/// assert_eq!(a, b);
104/// ```
105///
106/// Vector with the same [constant](XFieldElement::new_const) for every entry.
107///
108/// ```
109/// # use twenty_first::prelude::*;
110/// let a = xfe_vec![42; 15];
111/// let b = vec![xfe!(42); 15];
112/// assert_eq!(a, b);
113/// ```
114///
115/// Vector with the same general [extension field element](XFieldElement) for every entry.
116///
117/// ```
118/// # use twenty_first::prelude::*;
119/// let a = xfe_vec![[42, 43, 44]; 15];
120/// let b = vec![xfe!([42, 43, 44]); 15];
121/// assert_eq!(a, b);
122/// ```
123#[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/// Simplifies constructing arrays of [extension field element][XFieldElement]s.
140///
141/// The type [`XFieldElement`] must be in scope for this macro to work. See also [`xfe!`].
142///
143/// # Examples
144///
145/// Array of [constants](XFieldElement::new_const).
146///
147/// ```
148/// # use twenty_first::prelude::*;
149/// let a = xfe_array![1, 2, 3];
150/// let b = [xfe!(1), xfe!(2), xfe!(3)];
151/// assert_eq!(a, b);
152/// ```
153///
154/// Array of general [extension field element](XFieldElement)s.
155///
156/// ```
157/// # use twenty_first::prelude::*;
158/// let a = xfe_array![[1, 0, 0], [0, 2, 0], [0, 0, 3]];
159/// let b = [xfe!([1, 0, 0]), xfe!([0, 2, 0]), xfe!([0, 0, 3])];
160/// assert_eq!(a, b);
161/// ```
162///
163/// Array with the same [constant](XFieldElement::new_const) for every entry.
164///
165/// ```
166/// # use twenty_first::prelude::*;
167/// let a = xfe_array![42; 15];
168/// let b = [xfe!(42); 15];
169/// assert_eq!(a, b);
170/// ```
171///
172/// Array with the same general [extension field element](XFieldElement) for every entry.
173///
174/// ```
175/// # use twenty_first::prelude::*;
176/// let a = xfe_array![[42, 43, 44]; 15];
177/// let b = [xfe!([42, 43, 44]); 15];
178/// assert_eq!(a, b);
179/// ```
180#[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
196/// Re-interpret a slice of [`XFieldElement`]s as a slice of [`BFieldElement`]s
197/// without any memory allocation.
198///
199/// This function is semantically similar to [flat-mapping] the coefficients of
200/// the `XFieldElement`s (see examples). However, this function does not perform
201/// any memory allocation, which makes is particularly useful in
202/// high-performance scenarios.
203///
204/// # Examples
205///
206/// Re-interpretation behaves like flattening, but does not allocate or copy any
207/// data.
208///
209/// ```
210/// # use twenty_first::prelude::*;
211/// # use twenty_first::math::x_field_element::as_flat_slice;
212/// let xfes = xfe_vec![[17, 18, 19], [42, 42, 44], [97, 98, 99]];
213/// let bfes = bfe_vec![17, 18, 19, 42, 42, 44, 97, 98, 99];
214/// assert_eq!(&bfes, as_flat_slice(&xfes));
215/// ```
216///
217/// This can be particularly useful for hashing sequences of [`XFieldElement]`s,
218/// where ownership is irrelevant:
219///
220/// ```
221/// # use twenty_first::prelude::*;
222/// # use twenty_first::math::x_field_element::as_flat_slice;
223/// let xfes = xfe_vec![42; 17];
224/// let xfe_digest = Tip5::hash_varlen(as_flat_slice(&xfes));
225///
226/// // alternative requires copying data
227/// let bfes = xfes.into_iter().flat_map(|xfe| xfe.coefficients).collect::<Vec<_>>();
228/// let bfe_digest = Tip5::hash_varlen(&bfes);
229///
230/// assert_eq!(bfe_digest, xfe_digest);
231/// ```
232///
233/// [hashing]: crate::tip5::Tip5::hash_varlen
234/// [Tip5]: crate::tip5::Tip5
235/// [flat-mapping]: Iterator::flat_map
236pub 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    // SAFETY:
241    // - The slice_pointer is non-null, and is valid for reads for
242    //   xfe_slice.len() * size_of::<XFieldElement>() ==
243    //   xfe_slice.len() * size_of::<BFieldElement>() * EXTENSION_DEGREE
244    //   many bytes, and is properly aligned because both BFieldElement and
245    //   XFieldElement are #[repr(transparent)]. In particular:
246    //   - The entire memory range of the slice is contained within a single
247    //     allocated object. This is because of
248    //     (a) the origin of `slice_pointer` being a slice, and
249    //     (b) the layout and ABI of XFieldElement is identical to
250    //         [BFieldElement; EXTENSION_DEGREE] because of
251    //         #[repr(transparent)]
252    //   - The slice_pointer is non-null and aligned, again because of
253    //     #[repr(transparent)] on BFieldElement and XFieldElement.
254    // - The slice_pointer points to xfe_slice.len() * EXTENSION_DEGREE
255    //   consecutive properly initialized values of type BFieldElement,
256    //   again because of #[repr(transparent)] on BFieldElement and
257    //   XFieldElement.
258    // - The memory referenced by the returned slice cannot be mutated for
259    //   the duration of the lifetime of xfe_slice thanks to rust's
260    //   “mut XOR shared” compile time guarantees.
261    // - The total size of the produced slice is no larger than isize::MAX
262    //   since it is identical to the total size of the initial size, and
263    //   adding that size to the slice_pointer does not “wrap around” the
264    //   address space because both, the slice_pointer and the total size
265    //   have been obtained through safe code or unsafe code for which the
266    //   safety invariants have been upheld.
267    unsafe { std::slice::from_raw_parts(slice_pointer, bfe_slice_len) }
268}
269
270impl From<XFieldElement> for Digest {
271    /// Interpret the `XFieldElement` as a [`Digest`]. No hashing is performed.
272    /// This interpretation can be useful for [`Tip5`](crate::prelude::Tip5)
273    /// and, by extension, allows building
274    /// [`MerkleTree`](crate::prelude::MerkleTree)s directly from
275    /// `XFieldElement`s.
276    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    /// The quotient defining the [field extension](XFieldElement) over the
353    /// [base field](BFieldElement), namely x³ - x + 1.
354    #[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
501/// The `bfe + xfe -> xfe` instance belongs to BFieldElement.
502impl 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        // XField * XField means:
518        //
519        // (ax^2 + bx + c) * (dx^2 + ex + f)   (mod x^3 - x + 1)
520        //
521        // =   adx^4 + aex^3 + afx^2
522        //   + bdx^3 + bex^2 + bfx
523        //   + cdx^2 + cex   + cf
524        //
525        // = adx^4 + (ae + bd)x^3 + (af + be + cd)x^2 + (bf + ce)x + cf   (mod x^3 - x + 1)
526
527        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
538/// XField * BField means scalar multiplication of the
539/// BFieldElement onto each coefficient of the XField.
540impl 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        // TODO: Consider doing a statistical test.
757        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    /// To be removed once [XFieldElement::increment] and
794    /// [XFieldElement::decrement] are gone.
795    #[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        // x^2 * x^2 = x^4 = x^2 - x mod (x^3 - x + 1)
944        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        // x^2 * x = x^3 = x - 1 mod (x^3 - x + 1)
951        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        // (13+2x+3x2)(19+5x2) = 247+122x^2+38x+10x^3+15x^4
959        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            // 1. xfe + bfe.lift() = bfe.lift() + xfe
974            // 2. xfe + bfe = xfe + bfe.lift()
975            // 3. bfe + xfe = xfe + bfe.lift()
976            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            // 4. xfe * bfe.lift() = bfe.lift() * xfe
982            // 5. xfe * bfe = xfe * bfe.lift()
983            // 6. bfe * xfe = xfe * bfe.lift()
984            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            // 7. xfe - bfe = xfe - bfe.lift()
990            // 8. bfe - xfe = xfe - bfe.lift()
991            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        // Verify expected properties of XGCP: symmetry and that gcd is always
1012        // one. gcd is always one for all field elements.
1013        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            // XGCP for x
1052            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            // Verify symmetry, and that all elements are mutual primes, meaning that
1060            // they form a field
1061            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            // Verify Bezout relations: ax + by = gcd
1067            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        // Test batch inversion
1102        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            // Also verify that the `MulAssign` implementation agrees with the `Mul` implementation
1163            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        // Test the add/sub/mul assign operators
1179        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        // Test the add/sub/mul assign operators, when the higher coefficients are zero.
1192        // Also tests add/sub/mul operators and add/sub/mul assign operators when RHS has
1193        // the type of B field element. And add/sub/mul operators when LHS is a B-field
1194        // element and RHS is an X-field element.
1195        // mul-assign `*=`
1196        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        // `+=`
1209        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        // `-=`
1221        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}