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