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