cairo_lang_sierra/simulation/
core.rs

1use std::collections::HashMap;
2
3use cairo_lang_utils::extract_matches;
4use num_bigint::BigInt;
5use num_integer::Integer;
6use num_traits::{One, ToPrimitive, Zero};
7use starknet_types_core::felt::{Felt as Felt252, NonZeroFelt as NonZeroFelt252};
8
9use super::LibfuncSimulationError;
10use super::value::CoreValue;
11use crate::extensions::array::ArrayConcreteLibfunc;
12use crate::extensions::boolean::BoolConcreteLibfunc;
13use crate::extensions::core::CoreConcreteLibfunc;
14use crate::extensions::ec::EcConcreteLibfunc;
15use crate::extensions::enm::{EnumConcreteLibfunc, EnumInitConcreteLibfunc};
16use crate::extensions::felt252::{
17    Felt252BinaryOpConcreteLibfunc, Felt252BinaryOperationConcrete, Felt252BinaryOperator,
18    Felt252Concrete, Felt252ConstConcreteLibfunc, Felt252OperationWithConstConcreteLibfunc,
19};
20use crate::extensions::felt252_dict::Felt252DictConcreteLibfunc;
21use crate::extensions::function_call::SignatureAndFunctionConcreteLibfunc;
22use crate::extensions::gas::GasConcreteLibfunc;
23use crate::extensions::int::unsigned::{
24    Uint8Concrete, Uint16Concrete, Uint32Concrete, Uint64Concrete,
25};
26use crate::extensions::int::unsigned128::Uint128Concrete;
27use crate::extensions::int::{IntConstConcreteLibfunc, IntOperator};
28use crate::extensions::mem::MemConcreteLibfunc;
29use crate::extensions::structure::StructConcreteLibfunc;
30use crate::ids::FunctionId;
31
32/// Helper macro to take the inputs and return an error if the number of inputs is wrong, or the
33/// type of the expected inputs is wrong. Usage:
34///
35/// ```ignore
36/// take_inputs!(let [CoreValue::Felt252(x), CoreValue::Uint128(y), z] = inputs);
37/// ```
38macro_rules! take_inputs {
39    (let $assigned_value:pat = $inputs:ident) => {
40        let $assigned_value = take_inputs($inputs)? else {
41            return Err(LibfuncSimulationError::WrongArgType);
42        };
43    };
44}
45
46/// Simulates the run of a single libfunc. Returns the value representations of the outputs, and
47/// the chosen branch given the inputs.
48///
49/// `simulate_function` is a function that simulates running of a user function. It is provided here
50/// for the case where the extensions need to use it.
51pub fn simulate<
52    GetStatementGasInfo: Fn() -> Option<i64>,
53    SimulateFunction: Fn(&FunctionId, Vec<CoreValue>) -> Result<Vec<CoreValue>, LibfuncSimulationError>,
54>(
55    libfunc: &CoreConcreteLibfunc,
56    inputs: Vec<CoreValue>,
57    get_statement_gas_info: GetStatementGasInfo,
58    simulate_function: SimulateFunction,
59) -> Result<(Vec<CoreValue>, usize), LibfuncSimulationError> {
60    Ok(match libfunc {
61        CoreConcreteLibfunc::Drop(_) => {
62            let [_] = take_inputs(inputs)?;
63            (vec![], 0)
64        }
65        CoreConcreteLibfunc::Dup(_) => {
66            let [value] = take_inputs(inputs)?;
67            (vec![value.clone(), value], 0)
68        }
69        CoreConcreteLibfunc::Ec(libfunc) => {
70            match libfunc {
71                EcConcreteLibfunc::TryNew(_) => {
72                    take_inputs!(let [CoreValue::Felt252(x), CoreValue::Felt252(y)] = inputs);
73                    const BETA: Felt252 = Felt252::from_hex_unchecked(
74                        "0x6f21413efbe40de150e596d72f7a8c5609ad26c15c915c1f4cdfcb99cee9e89",
75                    );
76                    // If the point is on the curve use the fallthrough branch and return the
77                    // point.
78                    if y * y == x * x * x + x + BETA {
79                        (vec![CoreValue::EcPoint(x, y)], 0)
80                    } else {
81                        (vec![], 1)
82                    }
83                }
84                EcConcreteLibfunc::UnwrapPoint(_) => {
85                    take_inputs!(let [CoreValue::EcPoint(x, y)] = inputs);
86                    (vec![CoreValue::Felt252(x), CoreValue::Felt252(y)], 0)
87                }
88                _ => unimplemented!(),
89            }
90        }
91        CoreConcreteLibfunc::FunctionCall(SignatureAndFunctionConcreteLibfunc {
92            function, ..
93        })
94        | CoreConcreteLibfunc::CouponCall(SignatureAndFunctionConcreteLibfunc {
95            function, ..
96        }) => (simulate_function(&function.id, inputs)?, 0),
97        CoreConcreteLibfunc::Gas(GasConcreteLibfunc::WithdrawGas(_)) => {
98            let count = get_statement_gas_info()
99                .ok_or(LibfuncSimulationError::UnresolvedStatementGasInfo)?;
100            take_inputs!(let [CoreValue::RangeCheck, CoreValue::GasBuiltin(gas_counter)] = inputs);
101            if gas_counter >= count {
102                // Have enough gas - return reduced counter and jump to success branch.
103                (vec![CoreValue::RangeCheck, CoreValue::GasBuiltin(gas_counter - count)], 0)
104            } else {
105                // Don't have enough gas - return the same counter and jump to failure branch.
106                (vec![CoreValue::RangeCheck, CoreValue::GasBuiltin(gas_counter)], 1)
107            }
108        }
109        CoreConcreteLibfunc::Gas(GasConcreteLibfunc::RedepositGas(_)) => {
110            let count = get_statement_gas_info()
111                .ok_or(LibfuncSimulationError::UnresolvedStatementGasInfo)?;
112            take_inputs!(let [CoreValue::GasBuiltin(gas_counter)] = inputs);
113            (vec![CoreValue::GasBuiltin(gas_counter + count)], 0)
114        }
115        CoreConcreteLibfunc::Gas(GasConcreteLibfunc::GetAvailableGas(_)) => {
116            take_inputs!(let [CoreValue::GasBuiltin(gas_counter)] = inputs);
117            (vec![CoreValue::GasBuiltin(gas_counter), CoreValue::Uint128(gas_counter as u128)], 0)
118        }
119        CoreConcreteLibfunc::Gas(
120            GasConcreteLibfunc::BuiltinWithdrawGas(_) | GasConcreteLibfunc::GetBuiltinCosts(_),
121        ) => {
122            unimplemented!("Simulation of the builtin cost functionality is not implemented yet.")
123        }
124        CoreConcreteLibfunc::BranchAlign(_) => {
125            let [] = take_inputs(inputs)?;
126            get_statement_gas_info().ok_or(LibfuncSimulationError::UnresolvedStatementGasInfo)?;
127            (vec![], 0)
128        }
129        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::New(_)) => {
130            let [] = take_inputs(inputs)?;
131            (vec![CoreValue::Array(vec![])], 0)
132        }
133        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::SpanFromTuple(_)) => {
134            take_inputs!(let [CoreValue::Struct(members)] = inputs);
135            (vec![CoreValue::Array(members)], 0)
136        }
137        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::TupleFromSpan(_)) => todo!(),
138        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::Append(_)) => {
139            take_inputs!(let [CoreValue::Array(mut arr), element] = inputs);
140            arr.push(element);
141            (vec![CoreValue::Array(arr)], 0)
142        }
143        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::PopFront(_)) => {
144            take_inputs!(let [CoreValue::Array(mut arr)] = inputs);
145            if arr.is_empty() {
146                (vec![CoreValue::Array(arr)], 1)
147            } else {
148                let front = arr.remove(0);
149                (vec![CoreValue::Array(arr), front], 0)
150            }
151        }
152        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::PopFrontConsume(_)) => {
153            take_inputs!(let [CoreValue::Array(mut arr)] = inputs);
154            if arr.is_empty() { (vec![CoreValue::Array(arr)], 1) } else { (vec![arr.remove(0)], 0) }
155        }
156        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::Get(_)) => {
157            take_inputs!(
158                let [CoreValue::RangeCheck, CoreValue::Array(arr), CoreValue::Uint32(idx)] = inputs
159            );
160            match arr.get(idx as usize).cloned() {
161                Some(element) => (vec![CoreValue::RangeCheck, element], 0),
162                None => (vec![CoreValue::RangeCheck], 1),
163            }
164        }
165        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::Slice(_)) => {
166            take_inputs!(let [
167                CoreValue::RangeCheck,
168                CoreValue::Array(arr),
169                CoreValue::Uint32(start),
170                CoreValue::Uint32(length),
171            ] = inputs);
172            match arr.get(start as usize..(start + length) as usize) {
173                Some(elements) => {
174                    (vec![CoreValue::RangeCheck, CoreValue::Array(elements.to_vec())], 0)
175                }
176                None => (vec![CoreValue::RangeCheck], 1),
177            }
178        }
179        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::Len(_)) => {
180            take_inputs!(let [CoreValue::Array(arr)] = inputs);
181            (vec![CoreValue::Uint32(arr.len() as u32)], 0)
182        }
183        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::SnapshotPopFront(_)) => todo!(),
184        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::SnapshotPopBack(_)) => todo!(),
185        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::SnapshotMultiPopFront(_)) => todo!(),
186        CoreConcreteLibfunc::Array(ArrayConcreteLibfunc::SnapshotMultiPopBack(_)) => todo!(),
187        CoreConcreteLibfunc::Uint8(libfunc) => simulate_u8_libfunc(libfunc, inputs)?,
188        CoreConcreteLibfunc::Uint16(libfunc) => simulate_u16_libfunc(libfunc, inputs)?,
189        CoreConcreteLibfunc::Uint32(libfunc) => simulate_u32_libfunc(libfunc, inputs)?,
190        CoreConcreteLibfunc::Uint64(libfunc) => simulate_u64_libfunc(libfunc, inputs)?,
191        CoreConcreteLibfunc::Uint128(libfunc) => simulate_u128_libfunc(libfunc, inputs)?,
192        CoreConcreteLibfunc::Sint8(_)
193        | CoreConcreteLibfunc::Sint16(_)
194        | CoreConcreteLibfunc::Sint32(_)
195        | CoreConcreteLibfunc::Sint64(_)
196        | CoreConcreteLibfunc::Sint128(_) => {
197            unimplemented!("Simulation of signed integer libfuncs is not implemented yet.")
198        }
199        CoreConcreteLibfunc::Bool(libfunc) => simulate_bool_libfunc(libfunc, inputs)?,
200        CoreConcreteLibfunc::Felt252(libfunc) => simulate_felt252_libfunc(libfunc, inputs)?,
201        CoreConcreteLibfunc::UnwrapNonZero(_) => (inputs, 0),
202        CoreConcreteLibfunc::Mem(
203            MemConcreteLibfunc::Rename(_) | MemConcreteLibfunc::StoreTemp(_),
204        )
205        | CoreConcreteLibfunc::Box(_) => {
206            let [value] = take_inputs(inputs)?;
207            (vec![value], 0)
208        }
209        CoreConcreteLibfunc::Mem(MemConcreteLibfunc::FinalizeLocals(_))
210        | CoreConcreteLibfunc::UnconditionalJump(_)
211        | CoreConcreteLibfunc::ApTracking(_) => {
212            let [] = take_inputs(inputs)?;
213            (vec![], 0)
214        }
215        CoreConcreteLibfunc::Mem(MemConcreteLibfunc::StoreLocal(_)) => {
216            take_inputs!(let [CoreValue::Uninitialized, value] = inputs);
217            (vec![value], 0)
218        }
219        CoreConcreteLibfunc::Mem(MemConcreteLibfunc::AllocLocal(_)) => {
220            let [] = take_inputs(inputs)?;
221            (vec![CoreValue::Uninitialized], 0)
222        }
223        CoreConcreteLibfunc::Enum(EnumConcreteLibfunc::Init(EnumInitConcreteLibfunc {
224            index,
225            ..
226        })) => {
227            let [value] = take_inputs(inputs)?;
228            // We don't verify here that the input type matches the signature.
229            (vec![CoreValue::Enum { value: Box::new(value), index: *index }], 0)
230        }
231        CoreConcreteLibfunc::Enum(
232            EnumConcreteLibfunc::Match(_) | EnumConcreteLibfunc::SnapshotMatch(_),
233        ) => {
234            take_inputs!(let [CoreValue::Enum { value, index }] = inputs);
235            (vec![*value], index)
236        }
237        CoreConcreteLibfunc::Enum(EnumConcreteLibfunc::FromBoundedInt(_)) => todo!(),
238        CoreConcreteLibfunc::Struct(StructConcreteLibfunc::Construct(_)) => {
239            (vec![CoreValue::Struct(inputs)], 0)
240        }
241        CoreConcreteLibfunc::Struct(
242            StructConcreteLibfunc::Deconstruct(_) | StructConcreteLibfunc::SnapshotDeconstruct(_),
243        ) => {
244            take_inputs!(let [CoreValue::Struct(members)] = inputs);
245            (members, 0)
246        }
247        CoreConcreteLibfunc::Felt252Dict(Felt252DictConcreteLibfunc::New(_)) => {
248            let [] = take_inputs(inputs)?;
249            (vec![CoreValue::Dict(HashMap::new())], 0)
250        }
251        CoreConcreteLibfunc::Felt252Dict(Felt252DictConcreteLibfunc::Squash(_)) => {
252            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Dict(dict)] = inputs);
253            // Returning the same dict since it is exactly the same as the squashed one.
254            (vec![CoreValue::RangeCheck, CoreValue::Dict(dict)], 0)
255        }
256        CoreConcreteLibfunc::Pedersen(_) => {
257            unimplemented!("Simulation of the Pedersen hash function is not implemented yet.");
258        }
259        CoreConcreteLibfunc::Poseidon(_) => {
260            unimplemented!("Simulation of the Poseidon hash function is not implemented yet.");
261        }
262        CoreConcreteLibfunc::StarkNet(_) => {
263            unimplemented!("Simulation of the StarkNet functionalities is not implemented yet.")
264        }
265        CoreConcreteLibfunc::Nullable(_) => {
266            unimplemented!("Simulation of nullable is not implemented yet.")
267        }
268        CoreConcreteLibfunc::Debug(_) => {
269            take_inputs!(let [CoreValue::Array(arr)] = inputs);
270            let mut bytes = Vec::new();
271            for limb in arr {
272                let limb = extract_matches!(limb, CoreValue::Felt252);
273                bytes.extend(limb.to_bytes_be());
274            }
275            if let Ok(s) = String::from_utf8(bytes) {
276                print!("{s}");
277            } else {
278                println!("Not utf8");
279            }
280            (vec![], 0)
281        }
282        CoreConcreteLibfunc::SnapshotTake(_) => {
283            let [value] = take_inputs(inputs)?;
284            (vec![value.clone(), value], 0)
285        }
286        CoreConcreteLibfunc::Cast(_) => unimplemented!(),
287        CoreConcreteLibfunc::Felt252DictEntry(_) => unimplemented!(),
288        CoreConcreteLibfunc::Uint256(_) => unimplemented!(),
289        CoreConcreteLibfunc::Uint512(_) => unimplemented!(),
290        CoreConcreteLibfunc::Bytes31(_) => unimplemented!(),
291        CoreConcreteLibfunc::Const(_) => unimplemented!(),
292        CoreConcreteLibfunc::Coupon(_) => unimplemented!(),
293        CoreConcreteLibfunc::BoundedInt(_) => unimplemented!(),
294        CoreConcreteLibfunc::Circuit(_) => unimplemented!(),
295        CoreConcreteLibfunc::IntRange(_) => unimplemented!(),
296    })
297}
298
299/// Simulate boolean library functions.
300fn simulate_bool_libfunc(
301    libfunc: &BoolConcreteLibfunc,
302    inputs: Vec<CoreValue>,
303) -> Result<(Vec<CoreValue>, usize), LibfuncSimulationError> {
304    Ok(match libfunc {
305        BoolConcreteLibfunc::And(_) => {
306            take_inputs!(let [
307                CoreValue::Enum { index: a_index, .. },
308                CoreValue::Enum { index: b_index, .. },
309            ] = inputs);
310            (
311                vec![CoreValue::Enum {
312                    value: Box::new(CoreValue::Struct(vec![])),
313                    index: usize::from(a_index == 1 && b_index == 1),
314                }],
315                0,
316            )
317        }
318        BoolConcreteLibfunc::Not(_) => {
319            take_inputs!(let [CoreValue::Enum { index, .. }] = inputs);
320            (
321                vec![CoreValue::Enum {
322                    value: Box::new(CoreValue::Struct(vec![])),
323                    index: 1_usize - index,
324                }],
325                0,
326            )
327        }
328        BoolConcreteLibfunc::Xor(_) => {
329            take_inputs!(let [
330                CoreValue::Enum { index: a_index, .. },
331                CoreValue::Enum { index: b_index, .. },
332            ] = inputs);
333            (
334                vec![CoreValue::Enum {
335                    value: Box::new(CoreValue::Struct(vec![])),
336                    index: usize::from(a_index != b_index),
337                }],
338                0,
339            )
340        }
341        BoolConcreteLibfunc::Or(_) => {
342            take_inputs!(let [
343                CoreValue::Enum { index: a_index, .. },
344                CoreValue::Enum { index: b_index, .. },
345            ] = inputs);
346            (
347                vec![CoreValue::Enum {
348                    value: Box::new(CoreValue::Struct(vec![])),
349                    index: usize::from(a_index + b_index > 0),
350                }],
351                0,
352            )
353        }
354        BoolConcreteLibfunc::ToFelt252(_) => {
355            take_inputs!(let [CoreValue::Enum { index, .. }] = inputs);
356            (vec![CoreValue::Felt252(Felt252::from(index))], 0)
357        }
358    })
359}
360
361/// Simulate u128 library functions.
362fn simulate_u128_libfunc(
363    libfunc: &Uint128Concrete,
364    inputs: Vec<CoreValue>,
365) -> Result<(Vec<CoreValue>, usize), LibfuncSimulationError> {
366    Ok(match libfunc {
367        Uint128Concrete::Const(IntConstConcreteLibfunc { c, .. }) => {
368            let [] = take_inputs(inputs)?;
369            (vec![CoreValue::Uint128(*c)], 0)
370        }
371        Uint128Concrete::FromFelt252(_) => {
372            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Felt252(value)] = inputs);
373            match value.to_u128() {
374                Some(value) => (vec![CoreValue::RangeCheck, CoreValue::Uint128(value)], 0),
375                None => (vec![CoreValue::RangeCheck], 1),
376            }
377        }
378        Uint128Concrete::ToFelt252(_) => {
379            take_inputs!(let [CoreValue::Uint128(value)] = inputs);
380            (vec![CoreValue::Felt252(Felt252::from(value))], 0)
381        }
382        Uint128Concrete::Operation(libfunc) => {
383            take_inputs!(let [
384                CoreValue::RangeCheck, CoreValue::Uint128(lhs), CoreValue::Uint128(rhs)
385            ] = inputs);
386            let (value, overflow) = match libfunc.operator {
387                IntOperator::OverflowingAdd => lhs.overflowing_add(rhs),
388                IntOperator::OverflowingSub => lhs.overflowing_sub(rhs),
389            };
390            (vec![CoreValue::RangeCheck, CoreValue::Uint128(value)], usize::from(overflow))
391        }
392        Uint128Concrete::Divmod(_) => {
393            take_inputs!(let [
394                CoreValue::RangeCheck, CoreValue::Uint128(lhs), CoreValue::Uint128(rhs)
395            ] = inputs);
396            (
397                vec![
398                    CoreValue::RangeCheck,
399                    CoreValue::Uint128(lhs / rhs),
400                    CoreValue::Uint128(lhs % rhs),
401                ],
402                0,
403            )
404        }
405        Uint128Concrete::GuaranteeMul(_) => {
406            take_inputs!(let [CoreValue::Uint128(lhs), CoreValue::Uint128(rhs)] = inputs);
407            let (limb1, limb0) = (BigInt::from(lhs) * rhs).div_rem(&BigInt::one().pow(128));
408            (
409                vec![
410                    CoreValue::Uint128(limb0.to_u128().unwrap()),
411                    CoreValue::Uint128(limb1.to_u128().unwrap()),
412                    CoreValue::U128MulGuarantee,
413                ],
414                0,
415            )
416        }
417        Uint128Concrete::MulGuaranteeVerify(_) => {
418            take_inputs!(let [CoreValue::RangeCheck, CoreValue::U128MulGuarantee] = inputs);
419            (vec![CoreValue::RangeCheck], 0)
420        }
421        Uint128Concrete::IsZero(_) => {
422            take_inputs!(let [CoreValue::Uint128(value)] = inputs);
423            if value.is_zero() { (vec![], 0) } else { (vec![CoreValue::Uint128(value)], 1) }
424        }
425        Uint128Concrete::SquareRoot(_) => {
426            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Uint128(value)] = inputs);
427            let root = BigInt::from(value).sqrt();
428            (vec![CoreValue::RangeCheck, CoreValue::Uint64(root.to_u64().unwrap())], 0)
429        }
430        Uint128Concrete::Equal(_) => {
431            take_inputs!(let [CoreValue::Uint128(lhs), CoreValue::Uint128(rhs)] = inputs);
432            // "False" branch (branch 0) is the case a != b.
433            // "True" branch (branch 1) is the case a == b.
434            (vec![], usize::from(lhs == rhs))
435        }
436        Uint128Concrete::ByteReverse(_) => todo!("ByteReverse"),
437        Uint128Concrete::Bitwise(_) => {
438            take_inputs!(let [
439                CoreValue::Bitwise, CoreValue::Uint128(lhs), CoreValue::Uint128(rhs)
440            ] = inputs);
441            (
442                vec![
443                    CoreValue::Bitwise,
444                    CoreValue::Uint128(lhs & rhs),
445                    CoreValue::Uint128(lhs | rhs),
446                    CoreValue::Uint128(lhs ^ rhs),
447                ],
448                0,
449            )
450        }
451    })
452}
453
454/// Simulate u8 library functions.
455fn simulate_u8_libfunc(
456    libfunc: &Uint8Concrete,
457    inputs: Vec<CoreValue>,
458) -> Result<(Vec<CoreValue>, usize), LibfuncSimulationError> {
459    Ok(match libfunc {
460        Uint8Concrete::Const(IntConstConcreteLibfunc { c, .. }) => {
461            let [] = take_inputs(inputs)?;
462            (vec![CoreValue::Uint8(*c)], 0)
463        }
464        Uint8Concrete::Operation(libfunc) => {
465            take_inputs!(
466                let [CoreValue::RangeCheck, CoreValue::Uint8(lhs), CoreValue::Uint8(rhs)] = inputs
467            );
468            let (value, overflow) = match libfunc.operator {
469                IntOperator::OverflowingAdd => lhs.overflowing_add(rhs),
470                IntOperator::OverflowingSub => lhs.overflowing_sub(rhs),
471            };
472            (vec![CoreValue::RangeCheck, CoreValue::Uint8(value)], usize::from(overflow))
473        }
474        Uint8Concrete::SquareRoot(_) => {
475            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Uint8(value)] = inputs);
476            let root = BigInt::from(value).sqrt();
477            (vec![CoreValue::RangeCheck, CoreValue::Uint8(root.to_u8().unwrap())], 0)
478        }
479        Uint8Concrete::Equal(_) => {
480            take_inputs!(let [CoreValue::Uint8(lhs), CoreValue::Uint8(rhs)] = inputs);
481            // "False" branch (branch 0) is the case a != b.
482            // "True" branch (branch 1) is the case a == b.
483            (vec![], usize::from(lhs == rhs))
484        }
485        Uint8Concrete::ToFelt252(_) => {
486            take_inputs!(let [CoreValue::Uint8(value)] = inputs);
487            (vec![CoreValue::Felt252(Felt252::from(value))], 0)
488        }
489        Uint8Concrete::FromFelt252(_) => {
490            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Uint8(value)] = inputs);
491            match value.to_u8() {
492                Some(value) => (vec![CoreValue::RangeCheck, CoreValue::Uint8(value)], 0),
493                None => (vec![CoreValue::RangeCheck], 1),
494            }
495        }
496        Uint8Concrete::IsZero(_) => unimplemented!(),
497        Uint8Concrete::Divmod(_) => unimplemented!(),
498        Uint8Concrete::Bitwise(_) => unimplemented!(),
499        Uint8Concrete::WideMul(_) => {
500            take_inputs!(let [CoreValue::Uint8(lhs), CoreValue::Uint8(rhs)] = inputs);
501            (vec![CoreValue::Uint16(u16::from(lhs) * u16::from(rhs))], 0)
502        }
503    })
504}
505
506/// Simulate u16 library functions.
507fn simulate_u16_libfunc(
508    libfunc: &Uint16Concrete,
509    inputs: Vec<CoreValue>,
510) -> Result<(Vec<CoreValue>, usize), LibfuncSimulationError> {
511    Ok(match libfunc {
512        Uint16Concrete::Const(IntConstConcreteLibfunc { c, .. }) => {
513            let [] = take_inputs(inputs)?;
514            (vec![CoreValue::Uint16(*c)], 0)
515        }
516        Uint16Concrete::Operation(libfunc) => {
517            take_inputs!(let [
518                CoreValue::RangeCheck, CoreValue::Uint16(lhs), CoreValue::Uint16(rhs)
519            ] = inputs);
520            let (value, overflow) = match libfunc.operator {
521                IntOperator::OverflowingAdd => lhs.overflowing_add(rhs),
522                IntOperator::OverflowingSub => lhs.overflowing_sub(rhs),
523            };
524            (vec![CoreValue::RangeCheck, CoreValue::Uint16(value)], usize::from(overflow))
525        }
526        Uint16Concrete::SquareRoot(_) => {
527            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Uint16(value)] = inputs);
528            let root = BigInt::from(value).sqrt();
529            (vec![CoreValue::RangeCheck, CoreValue::Uint8(root.to_u8().unwrap())], 0)
530        }
531        Uint16Concrete::Equal(_) => {
532            take_inputs!(let [CoreValue::Uint16(lhs), CoreValue::Uint16(rhs)] = inputs);
533            // "False" branch (branch 0) is the case a != b.
534            // "True" branch (branch 1) is the case a == b.
535            (vec![], usize::from(lhs == rhs))
536        }
537        Uint16Concrete::ToFelt252(_) => {
538            take_inputs!(let [CoreValue::Uint16(value)] = inputs);
539            (vec![CoreValue::Felt252(Felt252::from(value))], 0)
540        }
541        Uint16Concrete::FromFelt252(_) => {
542            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Uint16(value)] = inputs);
543            match value.to_u16() {
544                Some(value) => (vec![CoreValue::RangeCheck, CoreValue::Uint16(value)], 0),
545                None => (vec![CoreValue::RangeCheck], 1),
546            }
547        }
548        Uint16Concrete::IsZero(_) => unimplemented!(),
549        Uint16Concrete::Divmod(_) => unimplemented!(),
550        Uint16Concrete::Bitwise(_) => unimplemented!(),
551        Uint16Concrete::WideMul(_) => {
552            take_inputs!(let [CoreValue::Uint16(lhs), CoreValue::Uint16(rhs)] = inputs);
553            (vec![CoreValue::Uint32(u32::from(lhs) * u32::from(rhs))], 0)
554        }
555    })
556}
557
558/// Simulate u32 library functions.
559fn simulate_u32_libfunc(
560    libfunc: &Uint32Concrete,
561    inputs: Vec<CoreValue>,
562) -> Result<(Vec<CoreValue>, usize), LibfuncSimulationError> {
563    Ok(match libfunc {
564        Uint32Concrete::Const(IntConstConcreteLibfunc { c, .. }) => {
565            let [] = take_inputs(inputs)?;
566            (vec![CoreValue::Uint32(*c)], 0)
567        }
568        Uint32Concrete::Operation(libfunc) => {
569            take_inputs!(let [
570                CoreValue::RangeCheck, CoreValue::Uint32(lhs), CoreValue::Uint32(rhs)
571            ] = inputs);
572            let (value, overflow) = match libfunc.operator {
573                IntOperator::OverflowingAdd => lhs.overflowing_add(rhs),
574                IntOperator::OverflowingSub => lhs.overflowing_sub(rhs),
575            };
576            (vec![CoreValue::RangeCheck, CoreValue::Uint32(value)], usize::from(overflow))
577        }
578        Uint32Concrete::SquareRoot(_) => {
579            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Uint32(value)] = inputs);
580            let root = BigInt::from(value).sqrt();
581            (vec![CoreValue::RangeCheck, CoreValue::Uint16(root.to_u16().unwrap())], 0)
582        }
583        Uint32Concrete::Equal(_) => {
584            take_inputs!(let [CoreValue::Uint32(lhs), CoreValue::Uint32(rhs)] = inputs);
585            // "False" branch (branch 0) is the case a != b.
586            // "True" branch (branch 1) is the case a == b.
587            (vec![], usize::from(lhs == rhs))
588        }
589        Uint32Concrete::ToFelt252(_) => {
590            take_inputs!(let [CoreValue::Uint32(value)] = inputs);
591            (vec![CoreValue::Felt252(Felt252::from(value))], 0)
592        }
593        Uint32Concrete::FromFelt252(_) => {
594            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Felt252(value)] = inputs);
595            match value.to_u32() {
596                Some(value) => (vec![CoreValue::RangeCheck, CoreValue::Uint32(value)], 0),
597                None => (vec![CoreValue::RangeCheck], 1),
598            }
599        }
600        Uint32Concrete::IsZero(_) => unimplemented!(),
601        Uint32Concrete::Divmod(_) => unimplemented!(),
602        Uint32Concrete::Bitwise(_) => unimplemented!(),
603        Uint32Concrete::WideMul(_) => {
604            take_inputs!(let [CoreValue::Uint32(lhs), CoreValue::Uint32(rhs)] = inputs);
605            (vec![CoreValue::Uint64(u64::from(lhs) * u64::from(rhs))], 0)
606        }
607    })
608}
609
610/// Simulate u64 library functions.
611fn simulate_u64_libfunc(
612    libfunc: &Uint64Concrete,
613    inputs: Vec<CoreValue>,
614) -> Result<(Vec<CoreValue>, usize), LibfuncSimulationError> {
615    Ok(match libfunc {
616        Uint64Concrete::Const(IntConstConcreteLibfunc { c, .. }) => {
617            let [] = take_inputs(inputs)?;
618            (vec![CoreValue::Uint64(*c)], 0)
619        }
620        Uint64Concrete::Operation(libfunc) => {
621            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Uint64(lhs), CoreValue::Uint64(rhs)] = inputs);
622            let (value, overflow) = match libfunc.operator {
623                IntOperator::OverflowingAdd => lhs.overflowing_add(rhs),
624                IntOperator::OverflowingSub => lhs.overflowing_sub(rhs),
625            };
626            (vec![CoreValue::RangeCheck, CoreValue::Uint64(value)], usize::from(overflow))
627        }
628        Uint64Concrete::SquareRoot(_) => {
629            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Uint64(value)] = inputs);
630            let root = BigInt::from(value).sqrt();
631            (vec![CoreValue::RangeCheck, CoreValue::Uint32(root.to_u32().unwrap())], 0)
632        }
633        Uint64Concrete::Equal(_) => {
634            take_inputs!(let [CoreValue::Uint64(lhs), CoreValue::Uint64(rhs)] = inputs);
635            // "False" branch (branch 0) is the case a != b.
636            // "True" branch (branch 1) is the case a == b.
637            (vec![], usize::from(lhs == rhs))
638        }
639        Uint64Concrete::ToFelt252(_) => {
640            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Uint64(value)] = inputs);
641            (vec![CoreValue::Felt252(Felt252::from(value))], 0)
642        }
643        Uint64Concrete::FromFelt252(_) => {
644            take_inputs!(let [CoreValue::RangeCheck, CoreValue::Felt252(value)] = inputs);
645            match value.to_u64() {
646                Some(value) => (vec![CoreValue::RangeCheck, CoreValue::Uint64(value)], 0),
647                None => (vec![CoreValue::RangeCheck], 1),
648            }
649        }
650        Uint64Concrete::IsZero(_) => unimplemented!(),
651        Uint64Concrete::Divmod(_) => unimplemented!(),
652        Uint64Concrete::Bitwise(_) => unimplemented!(),
653        Uint64Concrete::WideMul(_) => {
654            take_inputs!(let [CoreValue::Uint64(lhs), CoreValue::Uint64(rhs)] = inputs);
655            (vec![CoreValue::Uint128(u128::from(lhs) * u128::from(rhs))], 0)
656        }
657    })
658}
659
660/// Simulate felt252 library functions.
661fn simulate_felt252_libfunc(
662    libfunc: &Felt252Concrete,
663    inputs: Vec<CoreValue>,
664) -> Result<(Vec<CoreValue>, usize), LibfuncSimulationError> {
665    Ok(match libfunc {
666        Felt252Concrete::Const(Felt252ConstConcreteLibfunc { c, .. }) => {
667            let [] = take_inputs(inputs)?;
668            (vec![CoreValue::Felt252(c.into())], 0)
669        }
670        Felt252Concrete::BinaryOperation(Felt252BinaryOperationConcrete::WithVar(
671            Felt252BinaryOpConcreteLibfunc { operator, .. },
672        )) => {
673            take_inputs!(let [CoreValue::Felt252(lhs), CoreValue::Felt252(rhs)] = inputs);
674            (
675                vec![CoreValue::Felt252(match operator {
676                    Felt252BinaryOperator::Add => lhs + rhs,
677                    Felt252BinaryOperator::Sub => lhs - rhs,
678                    Felt252BinaryOperator::Mul => lhs * rhs,
679                    Felt252BinaryOperator::Div => {
680                        lhs.field_div(&NonZeroFelt252::from_felt_unchecked(rhs))
681                    }
682                })],
683                0,
684            )
685        }
686        Felt252Concrete::BinaryOperation(Felt252BinaryOperationConcrete::WithConst(
687            Felt252OperationWithConstConcreteLibfunc { operator, c, .. },
688        )) => {
689            take_inputs!(let [CoreValue::Felt252(value)] = inputs);
690            (
691                vec![CoreValue::Felt252(match operator {
692                    Felt252BinaryOperator::Add => value + Felt252::from(c),
693                    Felt252BinaryOperator::Sub => value - Felt252::from(c),
694                    Felt252BinaryOperator::Mul => value * Felt252::from(c),
695                    Felt252BinaryOperator::Div => {
696                        value.field_div(&NonZeroFelt252::from_felt_unchecked(Felt252::from(c)))
697                    }
698                })],
699                0,
700            )
701        }
702        Felt252Concrete::IsZero(_) => {
703            take_inputs!(let [CoreValue::Felt252(value)] = inputs);
704            if value.is_zero() {
705                // Zero - jumping to the failure branch.
706                (vec![], 0)
707            } else {
708                // Non-zero - jumping to the success branch and providing a NonZero wrap to the
709                // given value.
710                (vec![CoreValue::Felt252(value)], 1)
711            }
712        }
713    })
714}
715
716/// Takes the inputs and returns an array of the correct size, or an error if the number of inputs
717/// is wrong.
718fn take_inputs<const COUNT: usize>(
719    inputs: Vec<CoreValue>,
720) -> Result<[CoreValue; COUNT], LibfuncSimulationError> {
721    TryFrom::try_from(inputs).map_err(|_| LibfuncSimulationError::WrongNumberOfArgs)
722}