miden_stdlib_sys/intrinsics/
felt.rs

1#![allow(clippy::transmute_int_to_float)]
2
3use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
4
5#[link(wasm_import_module = "miden:core-intrinsics/intrinsics-felt@1.0.0")]
6extern "C" {
7    #[link_name = "from-u64-unchecked"]
8    fn extern_from_u64_unchecked(value: u64) -> Felt;
9
10    #[link_name = "from-u32"]
11    fn extern_from_u32(value: u32) -> Felt;
12
13    #[link_name = "as_u64"]
14    fn extern_as_u64(felt: Felt) -> u64;
15
16    #[link_name = "add"]
17    fn extern_add(a: Felt, b: Felt) -> Felt;
18
19    #[link_name = "sub"]
20    fn extern_sub(a: Felt, b: Felt) -> Felt;
21
22    #[link_name = "mul"]
23    fn extern_mul(a: Felt, b: Felt) -> Felt;
24
25    #[link_name = "div"]
26    fn extern_div(a: Felt, b: Felt) -> Felt;
27
28    #[link_name = "neg"]
29    fn extern_neg(a: Felt) -> Felt;
30
31    #[link_name = "inv"]
32    fn extern_inv(a: Felt) -> Felt;
33
34    #[link_name = "pow2"]
35    fn extern_pow2(a: Felt) -> Felt;
36
37    #[link_name = "exp"]
38    fn extern_exp(a: Felt, b: Felt) -> Felt;
39
40    #[link_name = "eq"]
41    fn extern_eq(a: Felt, b: Felt) -> i32;
42
43    #[link_name = "gt"]
44    fn extern_gt(a: Felt, b: Felt) -> i32;
45
46    #[link_name = "lt"]
47    fn extern_lt(a: Felt, b: Felt) -> i32;
48
49    #[link_name = "ge"]
50    fn extern_ge(a: Felt, b: Felt) -> i32;
51
52    #[link_name = "le"]
53    fn extern_le(a: Felt, b: Felt) -> i32;
54
55    #[link_name = "is-odd"]
56    fn extern_is_odd(a: Felt) -> i32;
57
58    #[link_name = "assert"]
59    fn extern_assert(a: Felt);
60
61    #[link_name = "assertz"]
62    fn extern_assertz(a: Felt);
63
64    #[link_name = "assert-eq"]
65    fn extern_assert_eq(a: Felt, b: Felt);
66}
67
68/// Creates a `Felt` from an integer constant checking that it is within the
69/// valid range at compile time.
70#[macro_export]
71macro_rules! felt {
72    // Trigger a compile-time error if the value is not a constant
73    ($value:literal) => {{
74        const VALUE: u64 = $value as u64;
75        // assert!(VALUE <= Felt::M, "Invalid Felt value, must be >= 0 and <= 2^64 - 2^32 + 1");
76        // Temporarily switch to `from_u32` to use `bitcast` and avoid checks.
77        // see https://github.com/0xMiden/compiler/issues/361
78        assert!(VALUE <= u32::MAX as u64, "Invalid value, must be >= 0 and <= 2^32");
79        const VALUE_U32: u32 = $value as u32;
80        Felt::from_u32(VALUE_U32)
81    }};
82}
83
84#[derive(Debug)]
85pub enum FeltError {
86    InvalidValue,
87}
88
89#[repr(transparent)]
90#[derive(Copy, Clone, Debug)]
91pub struct Felt {
92    pub inner: f32,
93}
94
95impl Felt {
96    /// Field modulus = 2^64 - 2^32 + 1
97    pub const M: u64 = 0xffffffff00000001;
98
99    #[inline(always)]
100    pub fn from_u64_unchecked(value: u64) -> Self {
101        unsafe { extern_from_u64_unchecked(value) }
102    }
103
104    #[inline(always)]
105    pub fn from_u32(value: u32) -> Self {
106        unsafe { extern_from_u32(value) }
107    }
108
109    #[inline(always)]
110    pub fn new(value: u64) -> Result<Self, FeltError> {
111        if value > Self::M {
112            Err(FeltError::InvalidValue)
113        } else {
114            Ok(Self::from_u64_unchecked(value))
115        }
116    }
117
118    #[inline(always)]
119    pub fn as_u64(self) -> u64 {
120        unsafe { extern_as_u64(self) }
121    }
122
123    /// Returns true if x is odd and false if x is even
124    #[inline(always)]
125    pub fn is_odd(self) -> bool {
126        unsafe { extern_is_odd(self) != 0 }
127    }
128
129    /// Returns x^-1
130    /// Fails if a=0
131    #[inline(always)]
132    pub fn inv(self) -> Felt {
133        unsafe { extern_inv(self) }
134    }
135
136    /// Returns 2^x
137    /// Fails if x > 63
138    #[inline(always)]
139    pub fn pow2(self) -> Felt {
140        unsafe { extern_pow2(self) }
141    }
142
143    /// Returns a^b
144    #[inline(always)]
145    pub fn exp(self, other: Felt) -> Felt {
146        unsafe { extern_exp(self, other) }
147    }
148}
149
150impl From<Felt> for u64 {
151    fn from(felt: Felt) -> u64 {
152        felt.as_u64()
153    }
154}
155
156impl From<u32> for Felt {
157    fn from(value: u32) -> Self {
158        Self {
159            inner: unsafe { core::mem::transmute::<u32, f32>(value) },
160        }
161    }
162}
163
164impl From<u16> for Felt {
165    fn from(value: u16) -> Self {
166        Self {
167            inner: unsafe { core::mem::transmute::<u32, f32>(value as u32) },
168        }
169    }
170}
171
172impl From<u8> for Felt {
173    fn from(value: u8) -> Self {
174        Self {
175            inner: unsafe { core::mem::transmute::<u32, f32>(value as u32) },
176        }
177    }
178}
179
180#[cfg(target_pointer_width = "32")]
181impl From<usize> for Felt {
182    fn from(value: usize) -> Self {
183        Self {
184            inner: unsafe { core::mem::transmute(value as u32) },
185        }
186    }
187}
188
189impl Add for Felt {
190    type Output = Self;
191
192    #[inline(always)]
193    fn add(self, other: Self) -> Self {
194        unsafe { extern_add(self, other) }
195    }
196}
197
198impl AddAssign for Felt {
199    #[inline(always)]
200    fn add_assign(&mut self, other: Self) {
201        *self = *self + other;
202    }
203}
204
205impl Sub for Felt {
206    type Output = Self;
207
208    #[inline(always)]
209    fn sub(self, other: Self) -> Self {
210        unsafe { extern_sub(self, other) }
211    }
212}
213
214impl SubAssign for Felt {
215    #[inline(always)]
216    fn sub_assign(&mut self, other: Self) {
217        *self = *self - other;
218    }
219}
220
221impl Mul for Felt {
222    type Output = Self;
223
224    #[inline(always)]
225    fn mul(self, other: Self) -> Self {
226        unsafe { extern_mul(self, other) }
227    }
228}
229
230impl MulAssign for Felt {
231    #[inline(always)]
232    fn mul_assign(&mut self, other: Self) {
233        *self = *self * other;
234    }
235}
236
237impl Div for Felt {
238    type Output = Self;
239
240    #[inline(always)]
241    fn div(self, other: Self) -> Self {
242        unsafe { extern_div(self, other) }
243    }
244}
245
246impl DivAssign for Felt {
247    #[inline(always)]
248    fn div_assign(&mut self, other: Self) {
249        *self = *self / other;
250    }
251}
252
253impl Neg for Felt {
254    type Output = Self;
255
256    #[inline(always)]
257    fn neg(self) -> Self {
258        unsafe { extern_neg(self) }
259    }
260}
261
262impl PartialEq for Felt {
263    #[inline(always)]
264    fn eq(&self, other: &Self) -> bool {
265        unsafe { extern_eq(*self, *other) == 1 }
266    }
267}
268
269impl Eq for Felt {}
270
271impl PartialOrd for Felt {
272    #[inline(always)]
273    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
274        Some(self.cmp(other))
275    }
276
277    #[inline(always)]
278    fn gt(&self, other: &Self) -> bool {
279        unsafe { extern_gt(*self, *other) != 0 }
280    }
281
282    #[inline(always)]
283    fn ge(&self, other: &Self) -> bool {
284        unsafe { extern_ge(*self, *other) != 0 }
285    }
286
287    #[inline(always)]
288    fn lt(&self, other: &Self) -> bool {
289        unsafe { extern_lt(*other, *self) != 0 }
290    }
291
292    #[inline(always)]
293    fn le(&self, other: &Self) -> bool {
294        unsafe { extern_le(*other, *self) != 0 }
295    }
296}
297
298impl Ord for Felt {
299    #[inline(always)]
300    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
301        if self.lt(other) {
302            core::cmp::Ordering::Less
303        } else if self.gt(other) {
304            core::cmp::Ordering::Greater
305        } else {
306            core::cmp::Ordering::Equal
307        }
308    }
309}
310
311/// If `a` == 1, removes it from the stack.  Fails if `a` != 1
312#[inline(always)]
313pub fn assert(a: Felt) {
314    unsafe {
315        extern_assert(a);
316    }
317}
318
319/// If `a` == 0, removes it from the stack.  Fails if `a` != 0
320#[inline(always)]
321pub fn assertz(a: Felt) {
322    unsafe {
323        extern_assertz(a);
324    }
325}
326
327/// If `a` == `b`, removes them from the stack.  Fails if `a` != `b`
328#[inline(always)]
329pub fn assert_eq(a: Felt, b: Felt) {
330    unsafe {
331        extern_assert_eq(a, b);
332    }
333}
334
335// #[cfg(test)]
336// mod tests {
337//     use super::*;
338
339//     #[test]
340//     fn felt_macro_smoke_test() {
341//         let _ = felt!(1);
342//     }
343// }