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    /// The multiplicative inverse.
371    ///
372    /// # Panics
373    ///
374    /// Panics if `self` is zero.
375    #[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        // Multiplication by a = a₀ + a₁·x + a₂·x² is a linear map on the
383        // vector space with basis (1, x, x²). Modulo the Shah polynomial
384        // x³ - x + 1, its matrix is
385        //
386        //       ⎛ a₀   -a₂     -a₁   ⎞
387        //   M = ⎜ a₁  a₀ + a₂  a₁ - a₂ ⎟.
388        //       ⎝ a₂    a₁    a₀ + a₂ ⎠
389        //
390        // The inverse of a is the first column of M⁻¹, which is the first
391        // column of the adjugate of M divided by the determinant of M. This
392        // requires only one inversion in the base field.
393        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        // The remaining cofactors of the first column are the negations of
399        // the cofactors computed above: C₁₀ = -C₀₂ and C₂₀ = -C₀₁.
400        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    // `XFieldElement` is `#[repr(transparent)]` over `[BFieldElement; 3]`.
508    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
533/// The `bfe + xfe -> xfe` instance belongs to BFieldElement.
534impl 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        // XField * XField means:
550        //
551        // (ax^2 + bx + c) * (dx^2 + ex + f)   (mod x^3 - x + 1)
552        //
553        // =   adx^4 + aex^3 + afx^2
554        //   + bdx^3 + bex^2 + bfx
555        //   + cdx^2 + cex   + cf
556        //
557        // = adx^4 + (ae + bd)x^3 + (af + be + cd)x^2 + (bf + ce)x + cf   (mod x^3 - x + 1)
558
559        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
570/// XField * BField means scalar multiplication of the
571/// BFieldElement onto each coefficient of the XField.
572impl 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        // TODO: Consider doing a statistical test.
789        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    /// To be removed once [XFieldElement::increment] and
826    /// [XFieldElement::decrement] are gone.
827    #[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        // x^2 * x^2 = x^4 = x^2 - x mod (x^3 - x + 1)
976        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        // x^2 * x = x^3 = x - 1 mod (x^3 - x + 1)
983        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        // (13+2x+3x2)(19+5x2) = 247+122x^2+38x+10x^3+15x^4
991        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            // 1. xfe + bfe.lift() = bfe.lift() + xfe
1006            // 2. xfe + bfe = xfe + bfe.lift()
1007            // 3. bfe + xfe = xfe + bfe.lift()
1008            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            // 4. xfe * bfe.lift() = bfe.lift() * xfe
1014            // 5. xfe * bfe = xfe * bfe.lift()
1015            // 6. bfe * xfe = xfe * bfe.lift()
1016            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            // 7. xfe - bfe = xfe - bfe.lift()
1022            // 8. bfe - xfe = xfe - bfe.lift()
1023            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        // Verify expected properties of XGCP: symmetry and that gcd is always
1044        // one. gcd is always one for all field elements.
1045        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            // XGCP for x
1084            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            // Verify symmetry, and that all elements are mutual primes, meaning that
1092            // they form a field
1093            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            // Verify Bezout relations: ax + by = gcd
1099            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        // Test batch inversion
1134        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            // Also verify that the `MulAssign` implementation agrees with the `Mul` implementation
1195            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        // Test the add/sub/mul assign operators
1211        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        // Test the add/sub/mul assign operators, when the higher coefficients are zero.
1224        // Also tests add/sub/mul operators and add/sub/mul assign operators when RHS has
1225        // the type of B field element. And add/sub/mul operators when LHS is a B-field
1226        // element and RHS is an X-field element.
1227        // mul-assign `*=`
1228        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        // `+=`
1241        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        // `-=`
1253        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    /// The inverse computed through the extended Euclidean algorithm, as a
1321    /// reference implementation.
1322    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}