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