Skip to main content

midenc_hir/ir/
immediates.rs

1use core::{
2    fmt,
3    hash::{Hash, Hasher},
4};
5
6pub use miden_core::Felt;
7
8use super::{AttrPrinter, parse::ParserExt};
9use crate::{
10    Type,
11    attributes::{AttrParser, InferAttributeType, IntegerLikeAttr},
12    derive::DialectAttribute,
13    dialects::builtin::BuiltinDialect,
14    formatter::PrettyPrint,
15};
16
17#[derive(DialectAttribute, Debug, Copy, Clone)]
18#[attribute(name = "number", dialect = BuiltinDialect, implements(AttrPrinter, IntegerLikeAttr))]
19pub enum Immediate {
20    I1(bool),
21    U8(u8),
22    I8(i8),
23    U16(u16),
24    I16(i16),
25    U32(u32),
26    I32(i32),
27    U64(u64),
28    I64(i64),
29    U128(u128),
30    I128(i128),
31    F64(f64),
32    Felt(Felt),
33}
34
35impl Default for Immediate {
36    fn default() -> Self {
37        // We choose this as a default as it represents a zero value in both Wasm and MASM
38        Self::I32(0)
39    }
40}
41
42impl IntegerLikeAttr for ImmediateAttr {
43    #[inline(always)]
44    fn as_immediate(&self) -> Immediate {
45        self.value
46    }
47
48    #[inline(always)]
49    fn set_from_immediate_lossy(&mut self, value: Immediate) {
50        self.value = value;
51    }
52}
53
54impl AttrPrinter for ImmediateAttr {
55    fn print(&self, printer: &mut super::print::AsmPrinter<'_>) {
56        match self.value {
57            Immediate::F64(_) => todo!("IR assembly support for floats"),
58            value => {
59                printer.print_decimal_integer(value);
60            }
61        }
62    }
63}
64
65impl AttrParser for ImmediateAttr {
66    fn parse(
67        parser: &mut dyn super::parse::Parser<'_>,
68    ) -> super::parse::ParseResult<crate::AttributeRef> {
69        use crate::parse::Token;
70
71        let imm = if parser.token_stream_mut().is_next(|tok| matches!(tok, Token::Minus)) {
72            Immediate::I128(parser.parse_decimal_integer::<i128>()?.into_inner())
73        } else {
74            Immediate::U128(parser.parse_decimal_integer::<u128>()?.into_inner())
75        };
76
77        Ok(parser.context_rc().create_attribute::<ImmediateAttr, _>(imm))
78    }
79}
80
81impl InferAttributeType for ImmediateAttr {
82    fn infer_type() -> Type {
83        // We cannot know the type of this attribute until we have a concrete value
84        Type::Unknown
85    }
86
87    #[inline]
88    fn infer_type_from_value(value: &<Self as crate::AttributeRegistration>::Value) -> Type {
89        value.ty()
90    }
91}
92
93impl Immediate {
94    pub fn ty(&self) -> Type {
95        match self {
96            Self::I1(_) => Type::I1,
97            Self::U8(_) => Type::U8,
98            Self::I8(_) => Type::I8,
99            Self::U16(_) => Type::U16,
100            Self::I16(_) => Type::I16,
101            Self::U32(_) => Type::U32,
102            Self::I32(_) => Type::I32,
103            Self::U64(_) => Type::U64,
104            Self::I64(_) => Type::I64,
105            Self::U128(_) => Type::U128,
106            Self::I128(_) => Type::I128,
107            Self::F64(_) => Type::F64,
108            Self::Felt(_) => Type::Felt,
109        }
110    }
111
112    /// Converts this immediate to the representation matching `ty`, if the value fits.
113    ///
114    /// Returns `None` when `ty` is not a representable numeric type, or the value is out of
115    /// range for it.
116    pub fn coerced_to(self, ty: &Type) -> Option<Self> {
117        match ty {
118            Type::I1 => self.as_bool().map(Self::I1),
119            Type::U8 => self.as_u8().map(Self::U8),
120            Type::I8 => self.as_i8().map(Self::I8),
121            Type::U16 => self.as_u16().map(Self::U16),
122            Type::I16 => self.as_i16().map(Self::I16),
123            Type::U32 => self.as_u32().map(Self::U32),
124            Type::I32 => self.as_i32().map(Self::I32),
125            Type::U64 => self.as_u64().map(Self::U64),
126            Type::I64 => self.as_i64().map(Self::I64),
127            Type::U128 => self.as_u128().map(Self::U128),
128            Type::I128 => self.as_i128().map(Self::I128),
129            Type::Felt => self.as_felt().map(Self::Felt),
130            _ => None,
131        }
132    }
133
134    /// Returns true if this immediate is a non-negative value
135    pub fn is_non_negative(&self) -> bool {
136        match self {
137            Self::I1(i) => *i,
138            Self::I8(i) => *i > 0,
139            Self::U8(i) => *i > 0,
140            Self::I16(i) => *i > 0,
141            Self::U16(i) => *i > 0,
142            Self::I32(i) => *i > 0,
143            Self::U32(i) => *i > 0,
144            Self::I64(i) => *i > 0,
145            Self::U64(i) => *i > 0,
146            Self::U128(i) => *i > 0,
147            Self::I128(i) => *i > 0,
148            Self::F64(f) => f.is_sign_positive(),
149            Self::Felt(_) => true,
150        }
151    }
152
153    /// Returns true if this immediate can represent negative values
154    pub fn is_signed(&self) -> bool {
155        matches!(
156            self,
157            Self::I8(_) | Self::I16(_) | Self::I32(_) | Self::I64(_) | Self::I128(_) | Self::F64(_)
158        )
159    }
160
161    /// Returns true if this immediate can only represent non-negative values
162    pub fn is_unsigned(&self) -> bool {
163        matches!(
164            self,
165            Self::I1(_)
166                | Self::U8(_)
167                | Self::U16(_)
168                | Self::U32(_)
169                | Self::U64(_)
170                | Self::U128(_)
171                | Self::Felt(_)
172        )
173    }
174
175    /// Returns true if this immediate is an odd integer, otherwise false
176    ///
177    /// If the immediate is not an integer, returns `None`
178    pub fn is_odd(&self) -> Option<bool> {
179        match self {
180            Self::I1(b) => Some(*b),
181            Self::U8(i) => Some((*i).is_multiple_of(2)),
182            Self::I8(i) => Some(*i % 2 == 0),
183            Self::U16(i) => Some((*i).is_multiple_of(2)),
184            Self::I16(i) => Some(*i % 2 == 0),
185            Self::U32(i) => Some((*i).is_multiple_of(2)),
186            Self::I32(i) => Some(*i % 2 == 0),
187            Self::U64(i) => Some((*i).is_multiple_of(2)),
188            Self::I64(i) => Some(*i % 2 == 0),
189            Self::Felt(i) => Some(i.as_canonical_u64().is_multiple_of(2)),
190            Self::U128(i) => Some((*i).is_multiple_of(2)),
191            Self::I128(i) => Some(*i % 2 == 0),
192            Self::F64(_) => None,
193        }
194    }
195
196    /// Returns true if this immediate is a non-zero integer, otherwise false
197    ///
198    /// If the immediate is not an integer, returns `None`
199    pub fn as_bool(self) -> Option<bool> {
200        match self {
201            Self::I1(b) => Some(b),
202            Self::U8(i) => Some(i != 0),
203            Self::I8(i) => Some(i != 0),
204            Self::U16(i) => Some(i != 0),
205            Self::I16(i) => Some(i != 0),
206            Self::U32(i) => Some(i != 0),
207            Self::I32(i) => Some(i != 0),
208            Self::U64(i) => Some(i != 0),
209            Self::I64(i) => Some(i != 0),
210            Self::Felt(i) => Some(i.as_canonical_u64() != 0),
211            Self::U128(i) => Some(i != 0),
212            Self::I128(i) => Some(i != 0),
213            Self::F64(_) => None,
214        }
215    }
216
217    /// Attempts to convert this value to a u8 regardless of signedness
218    pub fn bitcast_u8(self) -> Option<u8> {
219        match self {
220            Self::I1(b) => Some(b as u8),
221            Self::U8(b) => Some(b),
222            Self::I8(b) => Some(b as u8),
223            Self::U16(b) => u8::try_from(b).ok(),
224            Self::I16(b) => i8::try_from(b).ok().map(|v| v as u8),
225            Self::U32(b) => u8::try_from(b).ok(),
226            Self::I32(b) => i8::try_from(b).ok().map(|v| v as u8),
227            Self::U64(b) => u8::try_from(b).ok(),
228            Self::I64(b) => i8::try_from(b).ok().map(|v| v as u8),
229            Self::Felt(i) => u8::try_from(i.as_canonical_u64()).ok(),
230            Self::U128(b) if b <= (u8::MAX as u128) => Some(b as u8),
231            Self::U128(_) => None,
232            Self::I128(b) if b < (i8::MIN as i128) || b > (i8::MAX as i128) => None,
233            Self::I128(b) => Some(b as u8),
234            Self::F64(f) => FloatToInt::<u8>::to_int(f).ok(),
235        }
236    }
237
238    /// Attempts to convert this value to a i8 regardless of signedness
239    pub fn bitcast_i8(self) -> Option<i8> {
240        match self {
241            Self::I1(b) => Some(b as i8),
242            Self::U8(b) => Some(b as i8),
243            Self::I8(b) => Some(b),
244            Self::U16(b) => i8::try_from(b as i16).ok(),
245            Self::I16(b) => i8::try_from(b).ok(),
246            Self::U32(b) => i8::try_from(b as i32).ok(),
247            Self::I32(b) => i8::try_from(b).ok(),
248            Self::U64(b) => i8::try_from(b as i64).ok(),
249            Self::I64(b) => i8::try_from(b).ok(),
250            Self::Felt(i) => i8::try_from(i.as_canonical_u64() as i64).ok(),
251            Self::U128(b) if b <= (u8::MAX as u128) => Some(b as u8 as i8),
252            Self::U128(_) => None,
253            Self::I128(b) if b < (i8::MIN as i128) || b > (i8::MAX as i128) => None,
254            Self::I128(b) => Some(b as i8),
255            Self::F64(f) => FloatToInt::<i8>::to_int(f).ok(),
256        }
257    }
258
259    /// Attempts to convert this value to a u16 regardless of signedness
260    pub fn bitcast_u16(self) -> Option<u16> {
261        match self {
262            Self::I1(b) => Some(b as u16),
263            Self::U8(b) => Some(b as u16),
264            Self::I8(b) => Some(b as u16),
265            Self::U16(b) => Some(b),
266            Self::I16(b) => Some(b as u16),
267            Self::U32(b) => u16::try_from(b).ok(),
268            Self::I32(b) => i16::try_from(b).ok().map(|v| v as u16),
269            Self::U64(b) => u16::try_from(b).ok(),
270            Self::I64(b) => i16::try_from(b).ok().map(|v| v as u16),
271            Self::Felt(i) => u16::try_from(i.as_canonical_u64()).ok(),
272            Self::U128(b) if b <= (u16::MAX as u128) => Some(b as u16),
273            Self::U128(_) => None,
274            Self::I128(b) if b < (i16::MIN as i128) || b > (i16::MAX as i128) => None,
275            Self::I128(b) => Some(b as u16),
276            Self::F64(f) => FloatToInt::<u16>::to_int(f).ok(),
277        }
278    }
279
280    /// Attempts to convert this value to a i16 regardless of signedness
281    pub fn bitcast_i16(self) -> Option<i16> {
282        match self {
283            Self::I1(b) => Some(b as i16),
284            Self::U8(b) => Some(b as i16),
285            Self::I8(b) => Some(b as i16),
286            Self::U16(b) => Some(b as i16),
287            Self::I16(b) => Some(b),
288            Self::U32(b) => u16::try_from(b).ok().map(|v| v as i16),
289            Self::I32(b) => i16::try_from(b).ok(),
290            Self::U64(b) => u16::try_from(b).ok().map(|v| v as i16),
291            Self::I64(b) => i16::try_from(b).ok(),
292            Self::Felt(i) => u16::try_from(i.as_canonical_u64()).ok().map(|v| v as i16),
293            Self::U128(b) if b <= (u16::MAX as u128) => Some(b as i16),
294            Self::U128(_) => None,
295            Self::I128(b) if b < (i16::MIN as i128) || b > (i16::MAX as i128) => None,
296            Self::I128(b) => Some(b as i16),
297            Self::F64(f) => FloatToInt::<i16>::to_int(f).ok(),
298        }
299    }
300
301    /// Attempts to convert this value to a u32 regardless of signedness
302    pub fn bitcast_u32(self) -> Option<u32> {
303        match self {
304            Self::I1(b) => Some(b as u32),
305            Self::U8(b) => Some(b as u32),
306            Self::I8(b) => Some(b as u32),
307            Self::U16(b) => Some(b as u32),
308            Self::I16(b) => Some(b as u32),
309            Self::U32(b) => Some(b),
310            Self::I32(b) => Some(b as u32),
311            Self::U64(b) => u32::try_from(b).ok(),
312            Self::I64(b) => i32::try_from(b).ok().map(|v| v as u32),
313            Self::Felt(i) => u32::try_from(i.as_canonical_u64()).ok(),
314            Self::U128(b) if b <= (u32::MAX as u128) => Some(b as u32),
315            Self::U128(_) => None,
316            Self::I128(b) if b < (i32::MIN as i128) || b > (i32::MAX as i128) => None,
317            Self::I128(b) => Some(b as u32),
318            Self::F64(f) => FloatToInt::<u32>::to_int(f).ok(),
319        }
320    }
321
322    /// Attempts to convert this value to a i32 regardless of signedness
323    pub fn bitcast_i32(self) -> Option<i32> {
324        match self {
325            Self::I1(b) => Some(b as i32),
326            Self::U8(b) => Some(b as i32),
327            Self::I8(b) => Some(b as i32),
328            Self::U16(b) => Some(b as i32),
329            Self::I16(b) => Some(b as i32),
330            Self::U32(b) => Some(b as i32),
331            Self::I32(b) => Some(b),
332            Self::U64(b) => u32::try_from(b).ok().map(|v| v as i32),
333            Self::I64(b) => i32::try_from(b).ok(),
334            Self::Felt(i) => u32::try_from(i.as_canonical_u64()).ok().map(|v| v as i32),
335            Self::U128(b) if b <= (u32::MAX as u128) => Some(b as i32),
336            Self::U128(_) => None,
337            Self::I128(b) if b < (i32::MIN as i128) || b > (i32::MAX as i128) => None,
338            Self::I128(b) => Some(b as i32),
339            Self::F64(f) => FloatToInt::<i32>::to_int(f).ok(),
340        }
341    }
342
343    /// Attempts to convert this value to a u64 regardless of signedness
344    pub fn bitcast_u64(self) -> Option<u64> {
345        match self {
346            Self::I1(b) => Some(b as u64),
347            Self::U8(b) => Some(b as u64),
348            Self::I8(b) => Some(b as u64),
349            Self::U16(b) => Some(b as u64),
350            Self::I16(b) => Some(b as u64),
351            Self::U32(b) => Some(b as u64),
352            Self::I32(b) => Some(b as u64),
353            Self::U64(b) => Some(b),
354            Self::I64(b) => Some(b as u64),
355            Self::Felt(i) => Some(i.as_canonical_u64()),
356            Self::U128(b) if b <= (u64::MAX as u128) => Some(b as u64),
357            Self::U128(_) => None,
358            Self::I128(b) if b < (i64::MIN as i128) || b > (i64::MAX as i128) => None,
359            Self::I128(b) => Some(b as u64),
360            Self::F64(f) => FloatToInt::<u64>::to_int(f).ok(),
361        }
362    }
363
364    /// Attempts to convert this value to a i64 regardless of signedness
365    pub fn bitcast_i64(self) -> Option<i64> {
366        match self {
367            Self::I1(b) => Some(b as i64),
368            Self::U8(b) => Some(b as i64),
369            Self::I8(b) => Some(b as i64),
370            Self::U16(b) => Some(b as i64),
371            Self::I16(b) => Some(b as i64),
372            Self::U32(b) => Some(b as i64),
373            Self::I32(b) => Some(b as i64),
374            Self::U64(b) => Some(b as i64),
375            Self::I64(b) => Some(b),
376            Self::Felt(i) => Some(i.as_canonical_u64() as i64),
377            Self::U128(b) if b <= (u64::MAX as u128) => Some(b as i64),
378            Self::U128(_) => None,
379            Self::I128(b) if b < (i64::MIN as i128) || b > (i64::MAX as i128) => None,
380            Self::I128(b) => Some(b as i64),
381            Self::F64(f) => FloatToInt::<i64>::to_int(f).ok(),
382        }
383    }
384
385    /// Attempts to convert this value to a u128 regardless of signedness
386    pub fn bitcast_u128(self) -> Option<u128> {
387        match self {
388            Self::I1(b) => Some(b as u128),
389            Self::U8(b) => Some(b as u128),
390            Self::I8(b) => Some(b as u128),
391            Self::U16(b) => Some(b as u128),
392            Self::I16(b) => Some(b as u128),
393            Self::U32(b) => Some(b as u128),
394            Self::I32(b) => Some(b as u128),
395            Self::U64(b) => Some(b as u128),
396            Self::I64(b) => Some(b as u128),
397            Self::Felt(i) => Some(i.as_canonical_u64() as u128),
398            Self::U128(b) => Some(b),
399            Self::I128(b) => Some(b as u128),
400            Self::F64(f) => FloatToInt::<u128>::to_int(f).ok(),
401        }
402    }
403
404    /// Attempts to convert this value to a i128 regardless of signedness
405    pub fn bitcast_i128(self) -> Option<i128> {
406        match self {
407            Self::I1(b) => Some(b as i128),
408            Self::U8(b) => Some(b as i128),
409            Self::I8(b) => Some(b as i128),
410            Self::U16(b) => Some(b as i128),
411            Self::I16(b) => Some(b as i128),
412            Self::U32(b) => Some(b as i128),
413            Self::I32(b) => Some(b as i128),
414            Self::U64(b) => Some(b as i128),
415            Self::I64(b) => Some(b as i128),
416            Self::Felt(i) => Some(i.as_canonical_u64() as i128),
417            Self::U128(b) => Some(b as i128),
418            Self::I128(b) => Some(b),
419            Self::F64(f) => FloatToInt::<i128>::to_int(f).ok(),
420        }
421    }
422
423    /// Attempts to convert this value to a felt regardless of signedness
424    pub fn bitcast_felt(self) -> Option<Felt> {
425        match self {
426            Self::Felt(value) => Some(value),
427            imm => imm.bitcast_u64().and_then(|value| Felt::new(value).ok()),
428        }
429    }
430
431    /// Attempts to convert this value to a f64
432    pub fn bitcast_f64(self) -> Option<f64> {
433        match self {
434            Self::I1(b) => Some(f64::from(b as u32)),
435            Self::U8(b) => Some(f64::from(b as u32)),
436            Self::I8(b) => Some(f64::from(b as i32)),
437            Self::U16(b) => Some(f64::from(b as u32)),
438            Self::I16(b) => Some(f64::from(b as i32)),
439            Self::U32(b) => Some(f64::from(b)),
440            Self::I32(b) => Some(f64::from(b)),
441            Self::U64(b) => Some(b as f64),
442            Self::I64(b) => Some(b as f64),
443            Self::Felt(i) => Some(i.as_canonical_u64() as f64),
444            Self::U128(b) => Some(b as f64),
445            Self::I128(b) => Some(b as f64),
446            Self::F64(f) => Some(f),
447        }
448    }
449
450    /// Attempts to convert this value to a u8
451    pub fn as_u8(self) -> Option<u8> {
452        match self {
453            Self::I1(b) => Some(b as u8),
454            Self::U8(b) => Some(b),
455            Self::I8(b) if b >= 0 => Some(b as u8),
456            Self::I8(_) => None,
457            Self::U16(b) => b.try_into().ok(),
458            Self::I16(b) if b >= 0 && b <= (u8::MAX as i16) => Some(b as u16 as u8),
459            Self::I16(_) => None,
460            Self::U32(b) => b.try_into().ok(),
461            Self::I32(b) if b >= 0 && b <= (u8::MAX as i32) => Some(b as u32 as u8),
462            Self::I32(_) => None,
463            Self::U64(b) => b.try_into().ok(),
464            Self::I64(b) if b >= 0 && b <= (u8::MAX as i64) => Some(b as u64 as u8),
465            Self::I64(_) => None,
466            Self::Felt(i) => i.as_canonical_u64().try_into().ok(),
467            Self::U128(b) => b.try_into().ok(),
468            Self::I128(b) if b >= 0 && b <= (u8::MAX as i128) => Some(b as u8),
469            Self::I128(_) => None,
470            Self::F64(f) => FloatToInt::<u8>::to_int(f).ok(),
471        }
472    }
473
474    /// Attempts to convert this value to i8
475    pub fn as_i8(self) -> Option<i8> {
476        match self {
477            Self::I1(i) => Some(i as u8 as i8),
478            Self::U8(i) => i.try_into().ok(),
479            Self::I8(i) => Some(i),
480            Self::U16(i) if i <= (i8::MAX as u16) => Some(i as i8),
481            Self::U16(_) => None,
482            Self::I16(i) => i.try_into().ok(),
483            Self::U32(i) if i <= (i8::MAX as u32) => Some(i as i8),
484            Self::U32(_) => None,
485            Self::I32(i) => i.try_into().ok(),
486            Self::U64(i) if i <= (i8::MAX as u64) => Some(i as i8),
487            Self::U64(_) => None,
488            Self::I64(i) => i.try_into().ok(),
489            Self::Felt(i) => i.as_canonical_u64().try_into().ok(),
490            Self::U128(i) if i <= (i8::MAX as u128) => Some(i as i8),
491            Self::U128(_) => None,
492            Self::I128(i) if i >= (i8::MIN as i128) && i <= (i8::MAX as i128) => Some(i as i8),
493            Self::I128(_) => None,
494            Self::F64(f) => FloatToInt::<i8>::to_int(f).ok(),
495        }
496    }
497
498    /// Attempts to convert this value to a u16
499    pub fn as_u16(self) -> Option<u16> {
500        match self {
501            Self::I1(b) => Some(b as u16),
502            Self::U8(b) => Some(b as u16),
503            Self::I8(b) if b >= 0 => Some(b as u16),
504            Self::I8(_) => None,
505            Self::U16(b) => Some(b),
506            Self::I16(b) if b >= 0 => Some(b as u16),
507            Self::I16(_) => None,
508            Self::U32(b) => b.try_into().ok(),
509            Self::I32(b) if b >= 0 && b <= (u16::MAX as i32) => Some(b as u32 as u16),
510            Self::I32(_) => None,
511            Self::U64(b) => b.try_into().ok(),
512            Self::I64(b) if b >= 0 => u64::try_from(b).ok()?.try_into().ok(),
513            Self::I64(_) => None,
514            Self::Felt(i) => i.as_canonical_u64().try_into().ok(),
515            Self::U128(b) if b <= (u16::MAX as u64 as u128) => Some(b as u16),
516            Self::U128(_) => None,
517            Self::I128(b) if b >= 0 && b <= (u16::MAX as i128) => Some(b as u16),
518            Self::I128(_) => None,
519            Self::F64(f) => FloatToInt::<u16>::to_int(f).ok(),
520        }
521    }
522
523    /// Attempts to convert this value to i16
524    pub fn as_i16(self) -> Option<i16> {
525        match self {
526            Self::I1(b) => Some(b as u16 as i16),
527            Self::U8(i) => Some(i as i16),
528            Self::I8(i) => Some(i as i16),
529            Self::U16(i) if i <= (i16::MAX as u16) => Some(i as i16),
530            Self::U16(_) => None,
531            Self::I16(i) => Some(i),
532            Self::U32(i) if i <= (i16::MAX as u32) => Some(i as i16),
533            Self::U32(_) => None,
534            Self::I32(i) => i.try_into().ok(),
535            Self::U64(i) if i <= (i16::MAX as u64) => Some(i as i16),
536            Self::U64(_) => None,
537            Self::I64(i) => i.try_into().ok(),
538            Self::Felt(i) => i.as_canonical_u64().try_into().ok(),
539            Self::U128(i) if i <= (i16::MAX as u16 as u128) => Some(i as u16 as i16),
540            Self::U128(_) => None,
541            Self::I128(i) if i >= (i16::MIN as i128) && i <= (i16::MAX as i128) => Some(i as i16),
542            Self::I128(_) => None,
543            Self::F64(f) => FloatToInt::<i16>::to_int(f).ok(),
544        }
545    }
546
547    /// Attempts to convert this value to a u32
548    pub fn as_u32(self) -> Option<u32> {
549        match self {
550            Self::I1(b) => Some(b as u32),
551            Self::U8(b) => Some(b as u32),
552            Self::I8(b) if b >= 0 => Some(b as u32),
553            Self::I8(_) => None,
554            Self::U16(b) => Some(b as u32),
555            Self::I16(b) if b >= 0 => Some(b as u32),
556            Self::I16(_) => None,
557            Self::U32(b) => Some(b),
558            Self::I32(b) if b >= 0 => Some(b as u32),
559            Self::I32(_) => None,
560            Self::U64(b) => u32::try_from(b).ok(),
561            Self::I64(b) if b >= 0 => u32::try_from(b as u64).ok(),
562            Self::I64(_) => None,
563            Self::Felt(i) => u32::try_from(i.as_canonical_u64()).ok(),
564            Self::U128(b) if b <= (u32::MAX as u64 as u128) => Some(b as u32),
565            Self::U128(_) => None,
566            Self::I128(b) if b >= 0 && b <= (u32::MAX as u64 as i128) => Some(b as u32),
567            Self::I128(_) => None,
568            Self::F64(f) => FloatToInt::<u32>::to_int(f).ok(),
569        }
570    }
571
572    /// Attempts to convert this value to i32
573    pub fn as_i32(self) -> Option<i32> {
574        match self {
575            Self::I1(b) => Some(b as u32 as i32),
576            Self::U8(i) => Some(i as i32),
577            Self::I8(i) => Some(i as i32),
578            Self::U16(i) => Some(i as i32),
579            Self::I16(i) => Some(i as i32),
580            Self::U32(i) => i.try_into().ok(),
581            Self::I32(i) => Some(i),
582            Self::U64(i) => i.try_into().ok(),
583            Self::I64(i) => i.try_into().ok(),
584            Self::Felt(i) => i.as_canonical_u64().try_into().ok(),
585            Self::U128(i) if i <= (i32::MAX as u32 as u128) => Some(i as u32 as i32),
586            Self::U128(_) => None,
587            Self::I128(i) if i >= (i32::MIN as i128) && i <= (i32::MAX as i128) => Some(i as i32),
588            Self::I128(_) => None,
589            Self::F64(f) => FloatToInt::<i32>::to_int(f).ok(),
590        }
591    }
592
593    /// Attempts to convert this value to a field element
594    pub fn as_felt(self) -> Option<Felt> {
595        match self {
596            Self::I1(b) => Felt::new(b as u64).ok(),
597            Self::U8(b) => Felt::new(b as u64).ok(),
598            Self::I8(b) => u64::try_from(b).ok().and_then(|value| Felt::new(value).ok()),
599            Self::U16(b) => Felt::new(b as u64).ok(),
600            Self::I16(b) => u64::try_from(b).ok().and_then(|value| Felt::new(value).ok()),
601            Self::U32(b) => Felt::new(b as u64).ok(),
602            Self::I32(b) => u64::try_from(b).ok().and_then(|value| Felt::new(value).ok()),
603            Self::U64(b) => Felt::new(b).ok(),
604            Self::I64(b) => u64::try_from(b).ok().and_then(|value| Felt::new(value).ok()),
605            Self::Felt(i) => Some(i),
606            Self::U128(b) => u64::try_from(b).ok().and_then(|value| Felt::new(value).ok()),
607            Self::I128(b) => u64::try_from(b).ok().and_then(|value| Felt::new(value).ok()),
608            Self::F64(f) => FloatToInt::<Felt>::to_int(f).ok(),
609        }
610    }
611
612    /// Attempts to convert this value to u64
613    pub fn as_u64(self) -> Option<u64> {
614        match self {
615            Self::I1(b) => Some(b as u64),
616            Self::U8(i) => Some(i as u64),
617            Self::I8(i) if i >= 0 => Some(i as u64),
618            Self::I8(_) => None,
619            Self::U16(i) => Some(i as u64),
620            Self::I16(i) if i >= 0 => Some(i as u16 as u64),
621            Self::I16(_) => None,
622            Self::U32(i) => Some(i as u64),
623            Self::I32(i) if i >= 0 => Some(i as u32 as u64),
624            Self::I32(_) => None,
625            Self::U64(i) => Some(i),
626            Self::I64(i) if i >= 0 => Some(i as u64),
627            Self::I64(_) => None,
628            Self::Felt(i) => Some(i.as_canonical_u64()),
629            Self::U128(i) => (i).try_into().ok(),
630            Self::I128(i) if i >= 0 => (i).try_into().ok(),
631            Self::I128(_) => None,
632            Self::F64(f) => FloatToInt::<u64>::to_int(f).ok(),
633        }
634    }
635
636    /// Attempts to convert this value to i64
637    pub fn as_i64(self) -> Option<i64> {
638        match self {
639            Self::I1(b) => Some(b as i64),
640            Self::U8(i) => Some(i as i64),
641            Self::I8(i) => Some(i as i64),
642            Self::U16(i) => Some(i as i64),
643            Self::I16(i) => Some(i as i64),
644            Self::U32(i) => Some(i as i64),
645            Self::I32(i) => Some(i as i64),
646            Self::U64(i) => (i).try_into().ok(),
647            Self::I64(i) => Some(i),
648            Self::Felt(i) => i.as_canonical_u64().try_into().ok(),
649            Self::U128(i) if i <= i64::MAX as u128 => Some(i as u64 as i64),
650            Self::U128(_) => None,
651            Self::I128(i) => (i).try_into().ok(),
652            Self::F64(f) => FloatToInt::<i64>::to_int(f).ok(),
653        }
654    }
655
656    /// Attempts to convert this value to u128
657    pub fn as_u128(self) -> Option<u128> {
658        match self {
659            Self::I1(b) => Some(b as u128),
660            Self::U8(i) => Some(i as u128),
661            Self::I8(i) if i >= 0 => Some(i as u128),
662            Self::I8(_) => None,
663            Self::U16(i) => Some(i as u128),
664            Self::I16(i) if i >= 0 => Some(i as u16 as u128),
665            Self::I16(_) => None,
666            Self::U32(i) => Some(i as u128),
667            Self::I32(i) if i >= 0 => Some(i as u32 as u128),
668            Self::I32(_) => None,
669            Self::U64(i) => Some(i as u128),
670            Self::I64(i) if i >= 0 => Some(i as u128),
671            Self::I64(_) => None,
672            Self::Felt(i) => Some(i.as_canonical_u64() as u128),
673            Self::U128(i) => Some(i),
674            Self::I128(i) if i >= 0 => (i).try_into().ok(),
675            Self::I128(_) => None,
676            Self::F64(f) => FloatToInt::<u128>::to_int(f).ok(),
677        }
678    }
679
680    /// Attempts to convert this value to i128
681    pub fn as_i128(self) -> Option<i128> {
682        match self {
683            Self::I1(b) => Some(b as i128),
684            Self::U8(i) => Some(i as i128),
685            Self::I8(i) => Some(i as i128),
686            Self::U16(i) => Some(i as i128),
687            Self::I16(i) => Some(i as i128),
688            Self::U32(i) => Some(i as i128),
689            Self::I32(i) => Some(i as i128),
690            Self::U64(i) => Some(i as i128),
691            Self::I64(i) => Some(i as i128),
692            Self::Felt(i) => Some(i.as_canonical_u64() as i128),
693            Self::U128(i) if i <= i128::MAX as u128 => Some(i as i128),
694            Self::U128(_) => None,
695            Self::I128(i) => Some(i),
696            Self::F64(f) => FloatToInt::<i128>::to_int(f).ok(),
697        }
698    }
699}
700impl fmt::Display for Immediate {
701    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
702        match self {
703            Self::I1(i) => write!(f, "{i}"),
704            Self::U8(i) => write!(f, "{i}"),
705            Self::I8(i) => write!(f, "{i}"),
706            Self::U16(i) => write!(f, "{i}"),
707            Self::I16(i) => write!(f, "{i}"),
708            Self::U32(i) => write!(f, "{i}"),
709            Self::I32(i) => write!(f, "{i}"),
710            Self::U64(i) => write!(f, "{i}"),
711            Self::I64(i) => write!(f, "{i}"),
712            Self::U128(i) => write!(f, "{i}"),
713            Self::I128(i) => write!(f, "{i}"),
714            Self::F64(n) => write!(f, "{n}"),
715            Self::Felt(i) => write!(f, "{i}"),
716        }
717    }
718}
719impl PrettyPrint for Immediate {
720    fn render(&self) -> crate::formatter::Document {
721        use crate::formatter::*;
722        display(self)
723    }
724}
725impl Hash for Immediate {
726    fn hash<H: Hasher>(&self, state: &mut H) {
727        let d = core::mem::discriminant(self);
728        d.hash(state);
729        match self {
730            Self::I1(i) => i.hash(state),
731            Self::U8(i) => i.hash(state),
732            Self::I8(i) => i.hash(state),
733            Self::U16(i) => i.hash(state),
734            Self::I16(i) => i.hash(state),
735            Self::U32(i) => i.hash(state),
736            Self::I32(i) => i.hash(state),
737            Self::U64(i) => i.hash(state),
738            Self::I64(i) => i.hash(state),
739            Self::U128(i) => i.hash(state),
740            Self::I128(i) => i.hash(state),
741            Self::F64(f) => {
742                let bytes = f.to_be_bytes();
743                bytes.hash(state)
744            }
745            Self::Felt(i) => i.as_canonical_u64().hash(state),
746        }
747    }
748}
749impl Eq for Immediate {}
750impl PartialEq for Immediate {
751    fn eq(&self, other: &Self) -> bool {
752        match (*self, *other) {
753            (Self::I1(x), Self::I1(y)) => x == y,
754            (Self::I8(x), Self::I8(y)) => x == y,
755            (Self::U8(x), Self::U8(y)) => x == y,
756            (Self::U16(x), Self::U16(y)) => x == y,
757            (Self::I16(x), Self::I16(y)) => x == y,
758            (Self::U32(x), Self::U32(y)) => x == y,
759            (Self::I32(x), Self::I32(y)) => x == y,
760            (Self::U64(x), Self::U64(y)) => x == y,
761            (Self::I64(x), Self::I64(y)) => x == y,
762            (Self::U128(x), Self::U128(y)) => x == y,
763            (Self::I128(x), Self::I128(y)) => x == y,
764            (Self::F64(x), Self::F64(y)) => x == y,
765            (Self::Felt(x), Self::Felt(y)) => x == y,
766            _ => false,
767        }
768    }
769}
770impl PartialEq<isize> for Immediate {
771    fn eq(&self, other: &isize) -> bool {
772        let y = *other;
773        match *self {
774            Self::I1(x) => x == (y == 1),
775            Self::U8(_) if y < 0 => false,
776            Self::U8(x) => x as isize == y,
777            Self::I8(x) => x as isize == y,
778            Self::U16(_) if y < 0 => false,
779            Self::U16(x) => x as isize == y,
780            Self::I16(x) => x as isize == y,
781            Self::U32(_) if y < 0 => false,
782            Self::U32(x) => x as isize == y,
783            Self::I32(x) => x as isize == y,
784            Self::U64(_) if y < 0 => false,
785            Self::U64(x) => x == y as i64 as u64,
786            Self::I64(x) => x == y as i64,
787            Self::U128(_) if y < 0 => false,
788            Self::U128(x) => x == y as i128 as u128,
789            Self::I128(x) => x == y as i128,
790            Self::F64(_) => false,
791            Self::Felt(_) if y < 0 => false,
792            Self::Felt(x) => x.as_canonical_u64() == y as i64 as u64,
793        }
794    }
795}
796impl PartialOrd for Immediate {
797    fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
798        Some(self.cmp(other))
799    }
800}
801impl Ord for Immediate {
802    fn cmp(&self, other: &Self) -> core::cmp::Ordering {
803        use core::cmp::Ordering;
804
805        match (self, other) {
806            // Floats require special treatment
807            (Self::F64(x), Self::F64(y)) => x.total_cmp(y),
808            // Here we're attempting to compare against any integer immediate,
809            // so we must attempt to convert the float to the largest possible
810            // integer representation, i128, and then promote the integer immediate
811            // to i128 for comparison
812            //
813            // If the float is not an integer value, truncate it and compare, then
814            // adjust the result to account for the truncation
815            (Self::F64(x), y) => {
816                let y = y
817                    .as_i128()
818                    .expect("expected rhs to be an integer capable of fitting in an i128");
819                if let Ok(x) = FloatToInt::<i128>::to_int(*x) {
820                    x.cmp(&y)
821                } else {
822                    let is_positive = x.is_sign_positive();
823                    if let Ok(x) = FloatToInt::<i128>::to_int((*x).trunc()) {
824                        // Edge case for equality: the float must be bigger due to truncation
825                        match x.cmp(&y) {
826                            Ordering::Equal if is_positive => Ordering::Greater,
827                            Ordering::Equal => Ordering::Less,
828                            o => o,
829                        }
830                    } else {
831                        // The float is larger than i128 can represent, the sign tells us in what
832                        // direction
833                        if is_positive {
834                            Ordering::Greater
835                        } else {
836                            Ordering::Less
837                        }
838                    }
839                }
840            }
841            (x, y @ Self::F64(_)) => y.cmp(x).reverse(),
842            // u128 immediates require separate treatment
843            (Self::U128(x), Self::U128(y)) => x.cmp(y),
844            (Self::U128(x), y) => {
845                let y = y.as_u128().expect("expected rhs to be an integer in the range of u128");
846                x.cmp(&y)
847            }
848            (x, Self::U128(y)) => {
849                let x = x.as_u128().expect("expected lhs to be an integer in the range of u128");
850                x.cmp(y)
851            }
852            // i128 immediates require separate treatment
853            (Self::I128(x), Self::I128(y)) => x.cmp(y),
854            // We're only comparing against values here which are u64, i64, or smaller than 64-bits
855            (Self::I128(x), y) => {
856                let y = y.as_i128().expect("expected rhs to be an integer smaller than i128");
857                x.cmp(&y)
858            }
859            (x, Self::I128(y)) => {
860                let x = x.as_i128().expect("expected lhs to be an integer smaller than i128");
861                x.cmp(y)
862            }
863            // u64 immediates may not fit in an i64
864            (Self::U64(x), Self::U64(y)) => x.cmp(y),
865            // We're only comparing against values here which are i64, or smaller than 64-bits
866            (Self::U64(x), y) => {
867                let y =
868                    y.as_i64().expect("expected rhs to be an integer capable of fitting in an i64")
869                        as u64;
870                x.cmp(&y)
871            }
872            (x, Self::U64(y)) => {
873                let x =
874                    x.as_i64().expect("expected lhs to be an integer capable of fitting in an i64")
875                        as u64;
876                x.cmp(y)
877            }
878            // All immediates at this point are i64 or smaller
879            (x, y) => {
880                let x =
881                    x.as_i64().expect("expected lhs to be an integer capable of fitting in an i64");
882                let y =
883                    y.as_i64().expect("expected rhs to be an integer capable of fitting in an i64");
884                x.cmp(&y)
885            }
886        }
887    }
888}
889impl From<Immediate> for Type {
890    #[inline]
891    fn from(imm: Immediate) -> Self {
892        imm.ty()
893    }
894}
895impl From<&Immediate> for Type {
896    #[inline(always)]
897    fn from(imm: &Immediate) -> Self {
898        imm.ty()
899    }
900}
901impl From<bool> for Immediate {
902    #[inline(always)]
903    fn from(value: bool) -> Self {
904        Self::I1(value)
905    }
906}
907impl From<i8> for Immediate {
908    #[inline(always)]
909    fn from(value: i8) -> Self {
910        Self::I8(value)
911    }
912}
913impl From<u8> for Immediate {
914    #[inline(always)]
915    fn from(value: u8) -> Self {
916        Self::U8(value)
917    }
918}
919impl From<i16> for Immediate {
920    #[inline(always)]
921    fn from(value: i16) -> Self {
922        Self::I16(value)
923    }
924}
925impl From<u16> for Immediate {
926    #[inline(always)]
927    fn from(value: u16) -> Self {
928        Self::U16(value)
929    }
930}
931impl From<i32> for Immediate {
932    #[inline(always)]
933    fn from(value: i32) -> Self {
934        Self::I32(value)
935    }
936}
937impl From<u32> for Immediate {
938    #[inline(always)]
939    fn from(value: u32) -> Self {
940        Self::U32(value)
941    }
942}
943impl From<i64> for Immediate {
944    #[inline(always)]
945    fn from(value: i64) -> Self {
946        Self::I64(value)
947    }
948}
949impl From<u64> for Immediate {
950    #[inline(always)]
951    fn from(value: u64) -> Self {
952        Self::U64(value)
953    }
954}
955impl From<u128> for Immediate {
956    #[inline(always)]
957    fn from(value: u128) -> Self {
958        Self::U128(value)
959    }
960}
961impl From<i128> for Immediate {
962    #[inline(always)]
963    fn from(value: i128) -> Self {
964        Self::I128(value)
965    }
966}
967impl From<usize> for Immediate {
968    #[inline(always)]
969    fn from(value: usize) -> Self {
970        Self::U64(value as u64)
971    }
972}
973impl From<isize> for Immediate {
974    #[inline(always)]
975    fn from(value: isize) -> Self {
976        Self::I64(value as i64)
977    }
978}
979impl From<f64> for Immediate {
980    #[inline(always)]
981    fn from(value: f64) -> Self {
982        Self::F64(value)
983    }
984}
985impl From<char> for Immediate {
986    #[inline(always)]
987    fn from(value: char) -> Self {
988        Self::I32(value as u32 as i32)
989    }
990}
991impl From<Felt> for Immediate {
992    #[inline(always)]
993    fn from(value: Felt) -> Self {
994        Self::Felt(value)
995    }
996}
997
998trait FloatToInt<T: Sized + Copy>: Sized {
999    const ZERO: T;
1000
1001    fn upper_bound() -> Self;
1002    fn lower_bound() -> Self;
1003    fn to_int(self) -> Result<T, ()>;
1004    unsafe fn to_int_unchecked(self) -> T;
1005}
1006impl FloatToInt<i8> for f64 {
1007    const ZERO: i8 = 0;
1008
1009    fn upper_bound() -> Self {
1010        f64::from(i8::MAX) + 1.0
1011    }
1012
1013    fn lower_bound() -> Self {
1014        f64::from(i8::MIN) - 1.0
1015    }
1016
1017    fn to_int(self) -> Result<i8, ()> {
1018        float_to_int(self)
1019    }
1020
1021    unsafe fn to_int_unchecked(self) -> i8 {
1022        unsafe { f64::to_int_unchecked(self) }
1023    }
1024}
1025impl FloatToInt<u8> for f64 {
1026    const ZERO: u8 = 0;
1027
1028    fn upper_bound() -> Self {
1029        f64::from(u8::MAX) + 1.0
1030    }
1031
1032    fn lower_bound() -> Self {
1033        0.0
1034    }
1035
1036    fn to_int(self) -> Result<u8, ()> {
1037        float_to_int(self)
1038    }
1039
1040    unsafe fn to_int_unchecked(self) -> u8 {
1041        unsafe { f64::to_int_unchecked(self) }
1042    }
1043}
1044impl FloatToInt<i16> for f64 {
1045    const ZERO: i16 = 0;
1046
1047    fn upper_bound() -> Self {
1048        f64::from(i16::MAX) + 1.0
1049    }
1050
1051    fn lower_bound() -> Self {
1052        f64::from(i16::MIN) - 1.0
1053    }
1054
1055    fn to_int(self) -> Result<i16, ()> {
1056        float_to_int(self)
1057    }
1058
1059    unsafe fn to_int_unchecked(self) -> i16 {
1060        unsafe { f64::to_int_unchecked(self) }
1061    }
1062}
1063impl FloatToInt<u16> for f64 {
1064    const ZERO: u16 = 0;
1065
1066    fn upper_bound() -> Self {
1067        f64::from(u16::MAX) + 1.0
1068    }
1069
1070    fn lower_bound() -> Self {
1071        0.0
1072    }
1073
1074    fn to_int(self) -> Result<u16, ()> {
1075        float_to_int(self)
1076    }
1077
1078    unsafe fn to_int_unchecked(self) -> u16 {
1079        unsafe { f64::to_int_unchecked(self) }
1080    }
1081}
1082impl FloatToInt<i32> for f64 {
1083    const ZERO: i32 = 0;
1084
1085    fn upper_bound() -> Self {
1086        f64::from(i32::MAX) + 1.0
1087    }
1088
1089    fn lower_bound() -> Self {
1090        f64::from(i32::MIN) - 1.0
1091    }
1092
1093    fn to_int(self) -> Result<i32, ()> {
1094        float_to_int(self)
1095    }
1096
1097    unsafe fn to_int_unchecked(self) -> i32 {
1098        unsafe { f64::to_int_unchecked(self) }
1099    }
1100}
1101impl FloatToInt<u32> for f64 {
1102    const ZERO: u32 = 0;
1103
1104    fn upper_bound() -> Self {
1105        f64::from(u32::MAX) + 1.0
1106    }
1107
1108    fn lower_bound() -> Self {
1109        0.0
1110    }
1111
1112    fn to_int(self) -> Result<u32, ()> {
1113        float_to_int(self)
1114    }
1115
1116    unsafe fn to_int_unchecked(self) -> u32 {
1117        unsafe { f64::to_int_unchecked(self) }
1118    }
1119}
1120impl FloatToInt<i64> for f64 {
1121    const ZERO: i64 = 0;
1122
1123    fn upper_bound() -> Self {
1124        63.0f64.exp2()
1125    }
1126
1127    fn lower_bound() -> Self {
1128        -63.0f64.exp2() - 1.0
1129    }
1130
1131    fn to_int(self) -> Result<i64, ()> {
1132        float_to_int(self)
1133    }
1134
1135    unsafe fn to_int_unchecked(self) -> i64 {
1136        unsafe { f64::to_int_unchecked(self) }
1137    }
1138}
1139impl FloatToInt<u64> for f64 {
1140    const ZERO: u64 = 0;
1141
1142    fn upper_bound() -> Self {
1143        64.0f64.exp2()
1144    }
1145
1146    fn lower_bound() -> Self {
1147        0.0
1148    }
1149
1150    fn to_int(self) -> Result<u64, ()> {
1151        float_to_int(self)
1152    }
1153
1154    unsafe fn to_int_unchecked(self) -> u64 {
1155        unsafe { f64::to_int_unchecked(self) }
1156    }
1157}
1158impl FloatToInt<Felt> for f64 {
1159    const ZERO: Felt = Felt::ZERO;
1160
1161    fn upper_bound() -> Self {
1162        64.0f64.exp2() - 32.0f64.exp2() + 1.0
1163    }
1164
1165    fn lower_bound() -> Self {
1166        0.0
1167    }
1168
1169    fn to_int(self) -> Result<Felt, ()> {
1170        float_to_int(self).and_then(|value| Felt::new(value).map_err(|_| ()))
1171    }
1172
1173    unsafe fn to_int_unchecked(self) -> Felt {
1174        Felt::new_unchecked(unsafe { f64::to_int_unchecked::<u64>(self) })
1175    }
1176}
1177impl FloatToInt<u128> for f64 {
1178    const ZERO: u128 = 0;
1179
1180    fn upper_bound() -> Self {
1181        128.0f64.exp2()
1182    }
1183
1184    fn lower_bound() -> Self {
1185        0.0
1186    }
1187
1188    fn to_int(self) -> Result<u128, ()> {
1189        float_to_int(self)
1190    }
1191
1192    unsafe fn to_int_unchecked(self) -> u128 {
1193        unsafe { f64::to_int_unchecked(self) }
1194    }
1195}
1196impl FloatToInt<i128> for f64 {
1197    const ZERO: i128 = 0;
1198
1199    fn upper_bound() -> Self {
1200        f64::from(i128::BITS - 1).exp2()
1201    }
1202
1203    fn lower_bound() -> Self {
1204        (-f64::from(i128::BITS - 1)).exp2() - 1.0
1205    }
1206
1207    fn to_int(self) -> Result<i128, ()> {
1208        float_to_int(self)
1209    }
1210
1211    unsafe fn to_int_unchecked(self) -> i128 {
1212        unsafe { f64::to_int_unchecked(self) }
1213    }
1214}
1215
1216fn float_to_int<I>(f: f64) -> Result<I, ()>
1217where
1218    I: Copy,
1219    f64: FloatToInt<I>,
1220{
1221    use core::num::FpCategory;
1222    match f.classify() {
1223        FpCategory::Nan | FpCategory::Infinite | FpCategory::Subnormal => Err(()),
1224        FpCategory::Zero => Ok(<f64 as FloatToInt<I>>::ZERO),
1225        FpCategory::Normal => {
1226            if f == f.trunc()
1227                && f > <f64 as FloatToInt<I>>::lower_bound()
1228                && f < <f64 as FloatToInt<I>>::upper_bound()
1229            {
1230                // SAFETY: We know that x must be integral, and within the bounds of its type
1231                Ok(unsafe { <f64 as FloatToInt<I>>::to_int_unchecked(f) })
1232            } else {
1233                Err(())
1234            }
1235        }
1236    }
1237}