Skip to main content

miden_field/native/
mod.rs

1//! Off-chain implementation of [`crate::Felt`].
2
3use alloc::{format, vec, vec::Vec};
4use core::{
5    array, fmt,
6    hash::{Hash, Hasher},
7    iter::{Product, Sum},
8    mem::{align_of, size_of},
9    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign},
10};
11
12use miden_serde_utils::{
13    ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable,
14};
15use num_bigint::BigUint;
16use p3_challenger::UniformSamplingField;
17use p3_field::{
18    Field, InjectiveMonomial, Packable, PermutationMonomial, PrimeCharacteristicRing, PrimeField,
19    PrimeField64, RawDataSerializable, TwoAdicField,
20    extension::{
21        Binomial, BinomiallyExtendable, ExtensionAlgebra, HasTwoAdicBinomialExtension,
22        binomial_mul, binomial_square,
23    },
24    impl_raw_serializable_primefield64,
25    integers::QuotientMap,
26    quotient_map_large_iint, quotient_map_large_uint, quotient_map_small_int,
27};
28use p3_goldilocks::Goldilocks;
29use p3_util::flatten_to_base;
30use rand::{
31    Rng,
32    distr::{Distribution, StandardUniform},
33};
34use subtle::{ConditionallySelectable, ConstantTimeLess};
35
36#[cfg(any(
37    all(target_arch = "x86_64", target_feature = "avx2"),
38    all(target_arch = "aarch64", target_feature = "neon"),
39    all(target_arch = "wasm32", target_feature = "simd128"),
40))]
41mod packed;
42#[cfg(any(
43    all(target_arch = "x86_64", target_feature = "avx2"),
44    all(target_arch = "aarch64", target_feature = "neon"),
45    all(target_arch = "wasm32", target_feature = "simd128"),
46))]
47pub use packed::PackedFelt;
48
49#[cfg(test)]
50mod tests;
51
52// FELT
53// ================================================================================================
54
55/// A `Felt` backed by Plonky3's Goldilocks field element.
56#[derive(Copy, Clone, Default, serde::Serialize, serde::Deserialize)]
57#[repr(transparent)]
58pub struct Felt(Goldilocks);
59
60impl Felt {
61    /// Order of the field.
62    pub const ORDER: u64 = <Goldilocks as PrimeField64>::ORDER_U64;
63
64    pub const ZERO: Self = Self(Goldilocks::ZERO);
65    pub const ONE: Self = Self(Goldilocks::ONE);
66
67    /// The largest valid field element, equal to `ORDER - 1`.
68    pub const MAX: Self = Self::new_unchecked(Self::ORDER - 1);
69
70    /// The number of bytes which this field element occupies in memory.
71    pub const NUM_BYTES: usize = Goldilocks::NUM_BYTES;
72
73    /// Constructs a new field element from the provided `value`.
74    ///
75    /// # Errors
76    ///
77    /// - [`FeltFromIntError`] if the provided `value` is not a valid input.
78    pub fn new(value: u64) -> Result<Self, FeltFromIntError> {
79        Felt::from_canonical_checked(value).ok_or(FeltFromIntError(value))
80    }
81
82    /// Creates a new field element from any `u64` without performing reduction.
83    ///
84    /// Any `u64` value is accepted. No reduction is performed since Goldilocks uses a
85    /// non-canonical internal representation.
86    #[inline]
87    pub const fn new_unchecked(value: u64) -> Self {
88        Self(Goldilocks::new(value))
89    }
90
91    /// Constructs a field element from a `u8`.
92    #[inline]
93    pub const fn from_u8(value: u8) -> Self {
94        Self::new_unchecked(value as u64)
95    }
96
97    /// Constructs a field element from a `u16`.
98    #[inline]
99    pub const fn from_u16(value: u16) -> Self {
100        Self::new_unchecked(value as u64)
101    }
102
103    /// Constructs a field element from a `u32`.
104    #[inline]
105    pub const fn from_u32(value: u32) -> Self {
106        Self::new_unchecked(value as u64)
107    }
108
109    /// The elementary function `double(a) = 2*a`.
110    #[inline]
111    pub fn double(&self) -> Self {
112        <Self as PrimeCharacteristicRing>::double(self)
113    }
114
115    /// The elementary function `square(a) = a^2`.
116    #[inline]
117    pub fn square(&self) -> Self {
118        <Self as PrimeCharacteristicRing>::square(self)
119    }
120
121    /// Exponentiation by a `u64` power.
122    #[inline]
123    pub fn exp_u64(&self, power: u64) -> Self {
124        <Self as PrimeCharacteristicRing>::exp_u64(self, power)
125    }
126
127    /// Exponentiation by a small constant power.
128    #[inline]
129    pub fn exp_const_u64<const POWER: u64>(&self) -> Self {
130        <Self as PrimeCharacteristicRing>::exp_const_u64::<POWER>(self)
131    }
132
133    /// Return the representative of element in canonical form which lies in the range
134    /// `0 <= x < ORDER`.
135    #[inline]
136    pub fn as_canonical_u64(&self) -> u64 {
137        <Self as PrimeField64>::as_canonical_u64(self)
138    }
139
140    /// Constant-time equivalent of `as_canonical_u64()` using the same reduction logic as
141    /// Plonky3's Goldilocks implementation.
142    #[inline]
143    pub fn as_canonical_u64_ct(&self) -> u64 {
144        let raw = raw_felt_u64(*self);
145        // Mirrors Goldilocks::as_canonical_u64: conditional subtraction of ORDER.
146        // A single subtraction is sufficient for any u64 value since 2*ORDER > u64::MAX.
147        let reduced = raw.wrapping_sub(Self::ORDER);
148        let reduce = !raw.ct_lt(&Self::ORDER);
149        u64::conditional_select(&raw, &reduced, reduce)
150    }
151}
152
153#[inline]
154fn raw_felt_u64(value: Felt) -> u64 {
155    const _: () = {
156        assert!(size_of::<Felt>() == size_of::<u64>());
157        assert!(align_of::<Felt>() == align_of::<u64>());
158        assert!(2u128 * (Felt::ORDER as u128) > u64::MAX as u128);
159    };
160    // SAFETY: Felt is repr(transparent) over Goldilocks, which is repr(transparent) over u64.
161    unsafe { core::mem::transmute_copy(&value) }
162}
163
164/// Reinterprets a `Felt` slice as `Goldilocks`.
165///
166/// # Safety
167///
168/// `Felt` is `#[repr(transparent)]` over `Goldilocks`, so the element layout matches.
169#[inline]
170fn felts_as_goldilocks_slice(s: &[Felt]) -> &[Goldilocks] {
171    // SAFETY: `Felt` is `#[repr(transparent)]` over `Goldilocks`, so the element layout matches.
172    unsafe { core::slice::from_raw_parts(s.as_ptr().cast::<Goldilocks>(), s.len()) }
173}
174
175/// Reinterprets a `Felt` array as `Goldilocks`.
176///
177/// # Safety
178///
179/// `Felt` is `#[repr(transparent)]` over `Goldilocks`, so `[Felt; N]` matches `[Goldilocks; N]`.
180#[inline]
181fn felts_as_goldilocks_array<const N: usize>(a: &[Felt; N]) -> &[Goldilocks; N] {
182    // SAFETY: same layout as `felts_as_goldilocks_slice`, for a fixed `N`.
183    unsafe { &*(a as *const [Felt; N] as *const [Goldilocks; N]) }
184}
185
186impl fmt::Display for Felt {
187    #[inline]
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        fmt::Display::fmt(&self.0, f)
190    }
191}
192
193impl fmt::Debug for Felt {
194    #[inline]
195    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
196        fmt::Debug::fmt(&self.0, f)
197    }
198}
199
200impl Hash for Felt {
201    #[inline]
202    fn hash<H: Hasher>(&self, state: &mut H) {
203        state.write_u64(self.as_canonical_u64());
204    }
205}
206
207// FIELD
208// ================================================================================================
209
210impl Field for Felt {
211    #[cfg(all(target_arch = "x86_64", target_feature = "avx2", not(target_feature = "avx512f")))]
212    type Packing = PackedFelt;
213
214    #[cfg(all(target_arch = "x86_64", target_feature = "avx512f"))]
215    type Packing = PackedFelt;
216
217    #[cfg(all(target_arch = "aarch64", target_feature = "neon"))]
218    type Packing = PackedFelt;
219
220    #[cfg(all(target_arch = "wasm32", target_feature = "simd128"))]
221    type Packing = PackedFelt;
222
223    #[cfg(not(any(
224        all(target_arch = "x86_64", target_feature = "avx2", not(target_feature = "avx512f")),
225        all(target_arch = "x86_64", target_feature = "avx512f"),
226        target_arch = "aarch64",
227        all(target_arch = "wasm32", target_feature = "simd128"),
228    )))]
229    type Packing = Self;
230
231    const GENERATOR: Self = Self(Goldilocks::GENERATOR);
232
233    #[inline]
234    fn is_zero(&self) -> bool {
235        self.0.is_zero()
236    }
237
238    #[inline]
239    fn try_inverse(&self) -> Option<Self> {
240        self.0.try_inverse().map(Self)
241    }
242
243    #[inline]
244    fn order() -> BigUint {
245        <Goldilocks as Field>::order()
246    }
247}
248
249impl Packable for Felt {}
250
251impl PrimeCharacteristicRing for Felt {
252    type PrimeSubfield = Goldilocks;
253
254    const ZERO: Self = Self(Goldilocks::ZERO);
255    const ONE: Self = Self(Goldilocks::ONE);
256    const TWO: Self = Self(Goldilocks::TWO);
257    const NEG_ONE: Self = Self(Goldilocks::NEG_ONE);
258
259    #[inline]
260    fn from_prime_subfield(f: Self::PrimeSubfield) -> Self {
261        Self(f)
262    }
263
264    #[inline]
265    fn from_bool(value: bool) -> Self {
266        Self::new_unchecked(value.into())
267    }
268
269    #[inline]
270    fn halve(&self) -> Self {
271        Self(self.0.halve())
272    }
273
274    #[inline]
275    fn mul_2exp_u64(&self, exp: u64) -> Self {
276        Self(self.0.mul_2exp_u64(exp))
277    }
278
279    #[inline]
280    fn div_2exp_u64(&self, exp: u64) -> Self {
281        Self(self.0.div_2exp_u64(exp))
282    }
283
284    #[inline]
285    fn exp_u64(&self, power: u64) -> Self {
286        self.0.exp_u64(power).into()
287    }
288
289    #[inline]
290    fn sum_array<const N: usize>(input: &[Self]) -> Self {
291        assert_eq!(N, input.len());
292        let g = felts_as_goldilocks_slice(input);
293        Self(Goldilocks::sum_array::<N>(g))
294    }
295
296    #[inline]
297    fn dot_product<const N: usize>(lhs: &[Self; N], rhs: &[Self; N]) -> Self {
298        let lhs_g = felts_as_goldilocks_array(lhs);
299        let rhs_g = felts_as_goldilocks_array(rhs);
300        Self(Goldilocks::dot_product(lhs_g, rhs_g))
301    }
302
303    #[inline]
304    fn zero_vec(len: usize) -> Vec<Self> {
305        // SAFETY:
306        // Due to `#[repr(transparent)]`, Felt, Goldilocks and u64 have the same size,
307        // alignment and memory layout making `flatten_to_base` safe.
308        // This will create a vector of Felt elements with value set to 0.
309        unsafe { flatten_to_base(vec![0u64; len]) }
310    }
311}
312
313quotient_map_small_int!(Felt, u64, [u8, u16, u32]);
314quotient_map_small_int!(Felt, i64, [i8, i16, i32]);
315
316quotient_map_large_uint!(
317    Felt,
318    u64,
319    Felt::ORDER_U64,
320    "`[0, 2^64 - 2^32]`",
321    "`[0, 2^64 - 1]`",
322    [u128]
323);
324quotient_map_large_iint!(
325    Felt,
326    i64,
327    "`[-(2^63 - 2^31), 2^63 - 2^31]`",
328    "`[1 + 2^32 - 2^64, 2^64 - 1]`",
329    [(i128, u128)]
330);
331
332impl QuotientMap<u64> for Felt {
333    #[inline]
334    fn from_int(int: u64) -> Self {
335        Goldilocks::from_int(int).into()
336    }
337
338    #[inline]
339    fn from_canonical_checked(int: u64) -> Option<Self> {
340        Goldilocks::from_canonical_checked(int).map(From::from)
341    }
342
343    #[inline(always)]
344    unsafe fn from_canonical_unchecked(int: u64) -> Self {
345        Goldilocks::new(int).into()
346    }
347}
348
349impl QuotientMap<i64> for Felt {
350    #[inline]
351    fn from_int(int: i64) -> Self {
352        Goldilocks::from_int(int).into()
353    }
354
355    #[inline]
356    fn from_canonical_checked(int: i64) -> Option<Self> {
357        Goldilocks::from_canonical_checked(int).map(From::from)
358    }
359
360    #[inline(always)]
361    unsafe fn from_canonical_unchecked(int: i64) -> Self {
362        unsafe { Goldilocks::from_canonical_unchecked(int).into() }
363    }
364}
365
366impl PrimeField for Felt {
367    #[inline]
368    fn as_canonical_biguint(&self) -> BigUint {
369        <Goldilocks as PrimeField>::as_canonical_biguint(&self.0)
370    }
371}
372
373impl PrimeField64 for Felt {
374    const ORDER_U64: u64 = <Goldilocks as PrimeField64>::ORDER_U64;
375
376    #[inline]
377    fn as_canonical_u64(&self) -> u64 {
378        self.0.as_canonical_u64()
379    }
380}
381
382impl TwoAdicField for Felt {
383    const TWO_ADICITY: usize = <Goldilocks as TwoAdicField>::TWO_ADICITY;
384
385    #[inline]
386    fn two_adic_generator(bits: usize) -> Self {
387        Self(<Goldilocks as TwoAdicField>::two_adic_generator(bits))
388    }
389}
390
391// EXTENSION FIELDS
392// ================================================================================================
393
394impl ExtensionAlgebra<Self, 2, Binomial<Self>> for Felt {
395    #[inline]
396    fn ext_mul(a: &[Self; 2], b: &[Self; 2], res: &mut [Self; 2]) {
397        binomial_mul::<Self, Self, Self, 2>(a, b, res, <Self as BinomiallyExtendable<2>>::W);
398    }
399
400    #[inline]
401    fn ext_square(a: &[Self; 2], res: &mut [Self; 2]) {
402        binomial_square::<Self, Self, 2>(a, res, <Self as BinomiallyExtendable<2>>::W);
403    }
404}
405
406impl BinomiallyExtendable<2> for Felt {
407    const W: Self = Self(<Goldilocks as BinomiallyExtendable<2>>::W);
408
409    const DTH_ROOT: Self = Self(<Goldilocks as BinomiallyExtendable<2>>::DTH_ROOT);
410
411    const EXT_GENERATOR: [Self; 2] = [
412        Self(<Goldilocks as BinomiallyExtendable<2>>::EXT_GENERATOR[0]),
413        Self(<Goldilocks as BinomiallyExtendable<2>>::EXT_GENERATOR[1]),
414    ];
415}
416
417impl HasTwoAdicBinomialExtension<2> for Felt {
418    const EXT_TWO_ADICITY: usize = <Goldilocks as HasTwoAdicBinomialExtension<2>>::EXT_TWO_ADICITY;
419
420    #[inline]
421    fn ext_two_adic_generator(bits: usize) -> [Self; 2] {
422        let [a, b] = <Goldilocks as HasTwoAdicBinomialExtension<2>>::ext_two_adic_generator(bits);
423        [Self(a), Self(b)]
424    }
425}
426
427impl ExtensionAlgebra<Self, 5, Binomial<Self>> for Felt {
428    #[inline]
429    fn ext_mul(a: &[Self; 5], b: &[Self; 5], res: &mut [Self; 5]) {
430        binomial_mul::<Self, Self, Self, 5>(a, b, res, <Self as BinomiallyExtendable<5>>::W);
431    }
432
433    #[inline]
434    fn ext_square(a: &[Self; 5], res: &mut [Self; 5]) {
435        binomial_square::<Self, Self, 5>(a, res, <Self as BinomiallyExtendable<5>>::W);
436    }
437}
438
439impl BinomiallyExtendable<5> for Felt {
440    const W: Self = Self(<Goldilocks as BinomiallyExtendable<5>>::W);
441
442    const DTH_ROOT: Self = Self(<Goldilocks as BinomiallyExtendable<5>>::DTH_ROOT);
443
444    const EXT_GENERATOR: [Self; 5] = [
445        Self(<Goldilocks as BinomiallyExtendable<5>>::EXT_GENERATOR[0]),
446        Self(<Goldilocks as BinomiallyExtendable<5>>::EXT_GENERATOR[1]),
447        Self(<Goldilocks as BinomiallyExtendable<5>>::EXT_GENERATOR[2]),
448        Self(<Goldilocks as BinomiallyExtendable<5>>::EXT_GENERATOR[3]),
449        Self(<Goldilocks as BinomiallyExtendable<5>>::EXT_GENERATOR[4]),
450    ];
451}
452
453impl HasTwoAdicBinomialExtension<5> for Felt {
454    const EXT_TWO_ADICITY: usize = <Goldilocks as HasTwoAdicBinomialExtension<5>>::EXT_TWO_ADICITY;
455
456    #[inline]
457    fn ext_two_adic_generator(bits: usize) -> [Self; 5] {
458        let ext_generator =
459            <Goldilocks as HasTwoAdicBinomialExtension<5>>::ext_two_adic_generator(bits);
460        [
461            Self(ext_generator[0]),
462            Self(ext_generator[1]),
463            Self(ext_generator[2]),
464            Self(ext_generator[3]),
465            Self(ext_generator[4]),
466        ]
467    }
468}
469
470impl RawDataSerializable for Felt {
471    impl_raw_serializable_primefield64!();
472}
473
474impl Distribution<Felt> for StandardUniform {
475    #[inline]
476    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Felt {
477        let inner = <StandardUniform as Distribution<Goldilocks>>::sample(self, rng);
478        Felt(inner)
479    }
480}
481
482impl UniformSamplingField for Felt {
483    const MAX_SINGLE_SAMPLE_BITS: usize =
484        <Goldilocks as UniformSamplingField>::MAX_SINGLE_SAMPLE_BITS;
485    const SAMPLING_BITS_M: [u64; 64] = <Goldilocks as UniformSamplingField>::SAMPLING_BITS_M;
486}
487
488impl InjectiveMonomial<7> for Felt {}
489
490impl PermutationMonomial<7> for Felt {
491    #[inline]
492    fn injective_exp_root_n(&self) -> Self {
493        Self(self.0.injective_exp_root_n())
494    }
495}
496
497// CONVERSIONS
498// ================================================================================================
499
500impl From<u8> for Felt {
501    fn from(int: u8) -> Self {
502        Self::from_u8(int)
503    }
504}
505
506impl From<u16> for Felt {
507    fn from(int: u16) -> Self {
508        Self::from_u16(int)
509    }
510}
511
512impl From<u32> for Felt {
513    fn from(int: u32) -> Self {
514        Self::from_u32(int)
515    }
516}
517
518impl TryFrom<u64> for Felt {
519    type Error = FeltFromIntError;
520
521    fn try_from(int: u64) -> Result<Felt, Self::Error> {
522        Felt::new(int)
523    }
524}
525
526#[derive(Debug, thiserror::Error)]
527#[error("integer {0} is equal to or exceeds the felt modulus {modulus}", modulus = Felt::ORDER)]
528pub struct FeltFromIntError(u64);
529
530impl FeltFromIntError {
531    /// Returns the integer for which the conversion failed.
532    pub fn as_u64(&self) -> u64 {
533        self.0
534    }
535}
536
537impl From<Goldilocks> for Felt {
538    #[inline]
539    fn from(value: Goldilocks) -> Self {
540        Self(value)
541    }
542}
543
544impl From<Felt> for Goldilocks {
545    #[inline]
546    fn from(value: Felt) -> Self {
547        value.0
548    }
549}
550
551// ARITHMETIC OPERATIONS
552// ================================================================================================
553
554impl Add for Felt {
555    type Output = Self;
556
557    #[inline]
558    fn add(self, other: Self) -> Self {
559        Self(self.0 + other.0)
560    }
561}
562
563impl AddAssign for Felt {
564    #[inline]
565    fn add_assign(&mut self, other: Self) {
566        *self = *self + other;
567    }
568}
569
570impl Sub for Felt {
571    type Output = Self;
572
573    #[inline]
574    fn sub(self, other: Self) -> Self {
575        Self(self.0 - other.0)
576    }
577}
578
579impl SubAssign for Felt {
580    #[inline]
581    fn sub_assign(&mut self, other: Self) {
582        *self = *self - other;
583    }
584}
585
586impl Mul for Felt {
587    type Output = Self;
588
589    #[inline]
590    fn mul(self, other: Self) -> Self {
591        Self(self.0 * other.0)
592    }
593}
594
595impl MulAssign for Felt {
596    #[inline]
597    fn mul_assign(&mut self, other: Self) {
598        *self = *self * other;
599    }
600}
601
602impl Div for Felt {
603    type Output = Self;
604
605    #[inline]
606    fn div(self, other: Self) -> Self {
607        Self(self.0 / other.0)
608    }
609}
610
611impl DivAssign for Felt {
612    #[inline]
613    fn div_assign(&mut self, other: Self) {
614        *self = *self / other;
615    }
616}
617
618impl Neg for Felt {
619    type Output = Self;
620
621    #[inline]
622    fn neg(self) -> Self {
623        Self(-self.0)
624    }
625}
626
627impl Sum for Felt {
628    #[inline]
629    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
630        Self(iter.map(|x| x.0).sum())
631    }
632}
633
634impl<'a> Sum<&'a Felt> for Felt {
635    #[inline]
636    fn sum<I: Iterator<Item = &'a Felt>>(iter: I) -> Self {
637        Self(iter.map(|x| x.0).sum())
638    }
639}
640
641impl Product for Felt {
642    #[inline]
643    fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
644        Self(iter.map(|x| x.0).product())
645    }
646}
647
648impl<'a> Product<&'a Felt> for Felt {
649    #[inline]
650    fn product<I: Iterator<Item = &'a Felt>>(iter: I) -> Self {
651        Self(iter.map(|x| x.0).product())
652    }
653}
654
655// EQUALITY AND COMPARISON OPERATIONS
656// ================================================================================================
657
658impl PartialEq for Felt {
659    #[inline]
660    fn eq(&self, other: &Self) -> bool {
661        self.0 == other.0
662    }
663}
664
665impl PartialEq<Goldilocks> for Felt {
666    #[inline]
667    fn eq(&self, other: &Goldilocks) -> bool {
668        self.0 == *other
669    }
670}
671
672impl Eq for Felt {}
673
674impl PartialOrd for Felt {
675    #[inline]
676    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
677        Some(self.cmp(other))
678    }
679}
680
681impl Ord for Felt {
682    #[inline]
683    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
684        self.0.cmp(&other.0)
685    }
686}
687
688// SERIALIZATION
689// ================================================================================================
690
691impl Serializable for Felt {
692    fn write_into<W: ByteWriter>(&self, target: &mut W) {
693        target.write_u64(self.as_canonical_u64());
694    }
695
696    fn get_size_hint(&self) -> usize {
697        size_of::<u64>()
698    }
699}
700
701impl Deserializable for Felt {
702    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
703        let value = source.read_u64()?;
704        Self::from_canonical_checked(value).ok_or_else(|| {
705            DeserializationError::InvalidValue(format!("value {value} is not a valid felt"))
706        })
707    }
708}
709
710// ARBITRARY (proptest)
711// ================================================================================================
712
713#[cfg(all(any(test, feature = "arbitrary"), not(all(target_family = "wasm", miden))))]
714mod arbitrary {
715    use proptest::prelude::*;
716
717    use super::Felt;
718
719    impl Arbitrary for Felt {
720        type Parameters = ();
721        type Strategy = BoxedStrategy<Self>;
722
723        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
724            let canonical = (0u64..Felt::ORDER).prop_map(Felt::new_unchecked).boxed();
725            // Goldilocks uses representation where values above the field order are valid and
726            // represent wrapped field elements. Generate such values 1/5 of the time to exercise
727            // this behavior.
728            let non_canonical = (Felt::ORDER..=u64::MAX).prop_map(Felt::new_unchecked).boxed();
729            prop_oneof![4 => canonical, 1 => non_canonical].no_shrink().boxed()
730        }
731    }
732}