cairo_vm/hint_processor/builtin_hint_processor/
uint256_utils.rs

1use crate::Felt252;
2use crate::{
3    hint_processor::builtin_hint_processor::hint_utils::{
4        get_integer_from_var_name, get_relocatable_from_var_name, insert_value_from_var_name,
5        insert_value_into_ap,
6    },
7    hint_processor::hint_processor_definition::HintReference,
8    math_utils::{isqrt, pow2_const, pow2_const_nz},
9    serde::deserialize_program::ApTracking,
10    stdlib::{
11        borrow::Cow,
12        boxed::Box,
13        collections::HashMap,
14        ops::{Shl, Shr},
15        prelude::*,
16    },
17    types::{errors::math_errors::MathError, relocatable::Relocatable},
18    vm::{errors::hint_errors::HintError, vm_core::VirtualMachine},
19};
20use num_bigint::BigUint;
21use num_integer::{div_rem, Integer};
22use num_traits::{One, Zero};
23
24// TODO: use this type in all uint256 functions
25pub(crate) struct Uint256<'a> {
26    pub low: Cow<'a, Felt252>,
27    pub high: Cow<'a, Felt252>,
28}
29
30impl<'a> Uint256<'a> {
31    pub(crate) fn from_base_addr(
32        addr: Relocatable,
33        name: &str,
34        vm: &'a VirtualMachine,
35    ) -> Result<Self, HintError> {
36        Ok(Self {
37            low: vm.get_integer(addr).map_err(|_| {
38                HintError::IdentifierHasNoMember(Box::new((name.to_string(), "low".to_string())))
39            })?,
40            high: vm.get_integer((addr + 1)?).map_err(|_| {
41                HintError::IdentifierHasNoMember(Box::new((name.to_string(), "high".to_string())))
42            })?,
43        })
44    }
45
46    pub(crate) fn from_var_name(
47        name: &str,
48        vm: &'a VirtualMachine,
49        ids_data: &HashMap<String, HintReference>,
50        ap_tracking: &ApTracking,
51    ) -> Result<Self, HintError> {
52        let base_addr = get_relocatable_from_var_name(name, vm, ids_data, ap_tracking)?;
53        Self::from_base_addr(base_addr, name, vm)
54    }
55
56    pub(crate) fn from_values(low: Felt252, high: Felt252) -> Self {
57        let low = Cow::Owned(low);
58        let high = Cow::Owned(high);
59        Self { low, high }
60    }
61
62    pub(crate) fn insert_from_var_name(
63        self,
64        var_name: &str,
65        vm: &mut VirtualMachine,
66        ids_data: &HashMap<String, HintReference>,
67        ap_tracking: &ApTracking,
68    ) -> Result<(), HintError> {
69        let addr = get_relocatable_from_var_name(var_name, vm, ids_data, ap_tracking)?;
70
71        vm.insert_value(addr, self.low.into_owned())?;
72        vm.insert_value((addr + 1)?, self.high.into_owned())?;
73
74        Ok(())
75    }
76
77    pub(crate) fn pack(self) -> BigUint {
78        (self.high.to_biguint() << 128) + self.low.to_biguint()
79    }
80
81    pub(crate) fn split(num: &BigUint) -> Self {
82        let mask_low: BigUint = u128::MAX.into();
83        let low = Felt252::from(&(num & mask_low));
84        let high = Felt252::from(&(num >> 128));
85        Self::from_values(low, high)
86    }
87}
88
89impl<'a> From<&BigUint> for Uint256<'a> {
90    fn from(value: &BigUint) -> Self {
91        Self::split(value)
92    }
93}
94
95impl<'a> From<Felt252> for Uint256<'a> {
96    fn from(value: Felt252) -> Self {
97        let (high, low) = value.div_rem(pow2_const_nz(128));
98        Self::from_values(low, high)
99    }
100}
101
102/*
103Implements hints:
104%{
105    sum_low = ids.a.low + ids.b.low
106    ids.carry_low = 1 if sum_low >= ids.SHIFT else 0
107    sum_high = ids.a.high + ids.b.high + ids.carry_low
108    ids.carry_high = 1 if sum_high >= ids.SHIFT else 0
109%}
110%{
111    sum_low = ids.a.low + ids.b.low
112    ids.carry_low = 1 if sum_low >= ids.SHIFT else 0
113%}
114*/
115pub fn uint256_add(
116    vm: &mut VirtualMachine,
117    ids_data: &HashMap<String, HintReference>,
118    ap_tracking: &ApTracking,
119    low_only: bool,
120) -> Result<(), HintError> {
121    let shift = pow2_const(128);
122
123    let a = Uint256::from_var_name("a", vm, ids_data, ap_tracking)?;
124    let b = Uint256::from_var_name("b", vm, ids_data, ap_tracking)?;
125    let a_low = a.low.as_ref();
126    let b_low = b.low.as_ref();
127
128    // Main logic
129    // sum_low = ids.a.low + ids.b.low
130    // ids.carry_low = 1 if sum_low >= ids.SHIFT else 0
131    let carry_low = Felt252::from((a_low + b_low >= shift) as u8);
132
133    if !low_only {
134        let a_high = a.high.as_ref();
135        let b_high = b.high.as_ref();
136
137        // Main logic
138        // sum_high = ids.a.high + ids.b.high + ids.carry_low
139        // ids.carry_high = 1 if sum_high >= ids.SHIFT else 0
140        let carry_high = Felt252::from((a_high + b_high + carry_low >= shift) as u8);
141
142        insert_value_from_var_name("carry_high", carry_high, vm, ids_data, ap_tracking)?;
143    }
144
145    insert_value_from_var_name("carry_low", carry_low, vm, ids_data, ap_tracking)
146}
147
148/*
149Implements hint:
150%{
151    res = ids.a + ids.b
152    ids.carry = 1 if res >= ids.SHIFT else 0
153%}
154*/
155pub fn uint128_add(
156    vm: &mut VirtualMachine,
157    ids_data: &HashMap<String, HintReference>,
158    ap_tracking: &ApTracking,
159) -> Result<(), HintError> {
160    let shift = pow2_const(128);
161    let a = get_integer_from_var_name("a", vm, ids_data, ap_tracking)?;
162    let b = get_integer_from_var_name("b", vm, ids_data, ap_tracking)?;
163    let a = a.as_ref();
164    let b = b.as_ref();
165
166    // Main logic
167    // res = ids.a + ids.b
168    // ids.carry = 1 if res >= ids.SHIFT else 0
169    let carry = Felt252::from((a + b >= shift) as u8);
170
171    insert_value_from_var_name("carry", carry, vm, ids_data, ap_tracking)
172}
173
174/*
175Implements hint:
176%{
177    def split(num: int, num_bits_shift: int = 128, length: int = 2):
178        a = []
179        for _ in range(length):
180            a.append( num & ((1 << num_bits_shift) - 1) )
181            num = num >> num_bits_shift
182        return tuple(a)
183
184    def pack(z, num_bits_shift: int = 128) -> int:
185        limbs = (z.low, z.high)
186        return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))
187
188    a = pack(ids.a)
189    b = pack(ids.b)
190    res = (a - b)%2**256
191    res_split = split(res)
192    ids.res.low = res_split[0]
193    ids.res.high = res_split[1]
194%}
195*/
196
197pub fn uint256_sub(
198    vm: &mut VirtualMachine,
199    ids_data: &HashMap<String, HintReference>,
200    ap_tracking: &ApTracking,
201) -> Result<(), HintError> {
202    let a = Uint256::from_var_name("a", vm, ids_data, ap_tracking)?.pack();
203    let b = Uint256::from_var_name("b", vm, ids_data, ap_tracking)?.pack();
204
205    // Main logic:
206    // res = (a - b)%2**256
207    let res = if a >= b {
208        a - b
209    } else {
210        // wrapped a - b
211        // b is limited to (CAIRO_PRIME - 1) << 128 which is 1 << (251 + 128 + 1)
212        //                                         251: most significant felt bit
213        //                                         128:     high field left shift
214        //                                           1:       extra bit for limit
215        let mod_256 = BigUint::one() << 256;
216        if mod_256 >= b {
217            mod_256 - b + a
218        } else {
219            let lowered_b = b.mod_floor(&mod_256);
220            // Repeat the logic from before
221            if a >= lowered_b {
222                a - lowered_b
223            } else {
224                mod_256 - lowered_b + a
225            }
226        }
227    };
228
229    let res = Uint256::split(&res);
230
231    res.insert_from_var_name("res", vm, ids_data, ap_tracking)
232}
233
234/*
235Implements hint:
236%{
237    ids.low = ids.a & ((1<<64) - 1)
238    ids.high = ids.a >> 64
239%}
240*/
241pub fn split_64(
242    vm: &mut VirtualMachine,
243    ids_data: &HashMap<String, HintReference>,
244    ap_tracking: &ApTracking,
245) -> Result<(), HintError> {
246    let a = get_integer_from_var_name("a", vm, ids_data, ap_tracking)?;
247    let digits = a.to_le_digits();
248    let mut bytes = [0u8; 32];
249    bytes[..8].copy_from_slice(digits[1].to_le_bytes().as_slice());
250    bytes[8..16].copy_from_slice(digits[2].to_le_bytes().as_slice());
251    bytes[16..24].copy_from_slice(digits[3].to_le_bytes().as_slice());
252
253    let low = Felt252::from(digits[0]);
254    let high = Felt252::from_bytes_le(&bytes);
255    insert_value_from_var_name("high", high, vm, ids_data, ap_tracking)?;
256    insert_value_from_var_name("low", low, vm, ids_data, ap_tracking)
257}
258
259/*
260Implements hint:
261%{
262    from starkware.python.math_utils import isqrt
263    n = (ids.n.high << 128) + ids.n.low
264    root = isqrt(n)
265    assert 0 <= root < 2 ** 128
266    ids.root.low = root
267    ids.root.high = 0
268%}
269*/
270pub fn uint256_sqrt(
271    vm: &mut VirtualMachine,
272    ids_data: &HashMap<String, HintReference>,
273    ap_tracking: &ApTracking,
274    only_low: bool,
275) -> Result<(), HintError> {
276    let n = Uint256::from_var_name("n", vm, ids_data, ap_tracking)?.pack();
277
278    // Main logic
279    // from starkware.python.math_utils import isqrt
280    // n = (ids.n.high << 128) + ids.n.low
281    // root = isqrt(n)
282    // assert 0 <= root < 2 ** 128
283    // ids.root.low = root
284    // ids.root.high = 0
285
286    let root = isqrt(&n)?;
287
288    if root.bits() > 128 {
289        return Err(HintError::AssertionFailed(
290            format!("assert 0 <= {} < 2 ** 128", &root).into_boxed_str(),
291        ));
292    }
293
294    let root = Felt252::from(&root);
295
296    if only_low {
297        insert_value_from_var_name("root", root, vm, ids_data, ap_tracking)?;
298    } else {
299        let root_u256 = Uint256::from_values(root, Felt252::ZERO);
300        root_u256.insert_from_var_name("root", vm, ids_data, ap_tracking)?;
301    }
302    Ok(())
303}
304
305/*
306Implements hint:
307%{ memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0 %}
308*/
309pub fn uint256_signed_nn(
310    vm: &mut VirtualMachine,
311    ids_data: &HashMap<String, HintReference>,
312    ap_tracking: &ApTracking,
313) -> Result<(), HintError> {
314    let a_addr = get_relocatable_from_var_name("a", vm, ids_data, ap_tracking)?;
315    let a_high = vm.get_integer((a_addr + 1_usize)?)?;
316    //Main logic
317    //memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0
318    let result: Felt252 =
319        if *a_high >= Felt252::ZERO && a_high.as_ref() <= &Felt252::from(i128::MAX) {
320            Felt252::ONE
321        } else {
322            Felt252::ZERO
323        };
324    insert_value_into_ap(vm, result)
325}
326
327/*
328Implements hint:
329%{
330    a = (ids.a.high << 128) + ids.a.low
331    div = (ids.div.high << 128) + ids.div.low
332    quotient, remainder = divmod(a, div)
333
334    ids.quotient.low = quotient & ((1 << 128) - 1)
335    ids.quotient.high = quotient >> 128
336    ids.remainder.low = remainder & ((1 << 128) - 1)
337    ids.remainder.high = remainder >> 128
338%}
339*/
340pub fn uint256_unsigned_div_rem(
341    vm: &mut VirtualMachine,
342    ids_data: &HashMap<String, HintReference>,
343    ap_tracking: &ApTracking,
344) -> Result<(), HintError> {
345    uint256_offseted_unsigned_div_rem(vm, ids_data, ap_tracking, 0, 1)
346}
347
348/*
349Implements hint:
350%{
351    a = (ids.a.high << 128) + ids.a.low
352    div = (ids.div.b23 << 128) + ids.div.b01
353    quotient, remainder = divmod(a, div)
354
355    ids.quotient.low = quotient & ((1 << 128) - 1)
356    ids.quotient.high = quotient >> 128
357    ids.remainder.low = remainder & ((1 << 128) - 1)
358    ids.remainder.high = remainder >> 128
359%}
360*/
361pub fn uint256_expanded_unsigned_div_rem(
362    vm: &mut VirtualMachine,
363    ids_data: &HashMap<String, HintReference>,
364    ap_tracking: &ApTracking,
365) -> Result<(), HintError> {
366    uint256_offseted_unsigned_div_rem(vm, ids_data, ap_tracking, 1, 3)
367}
368
369pub fn uint256_offseted_unsigned_div_rem(
370    vm: &mut VirtualMachine,
371    ids_data: &HashMap<String, HintReference>,
372    ap_tracking: &ApTracking,
373    div_offset_low: usize,
374    div_offset_high: usize,
375) -> Result<(), HintError> {
376    let a = Uint256::from_var_name("a", vm, ids_data, ap_tracking)?;
377    let a_low = a.low.as_ref();
378    let a_high = a.high.as_ref();
379
380    let div_addr = get_relocatable_from_var_name("div", vm, ids_data, ap_tracking)?;
381    let div_low = vm.get_integer((div_addr + div_offset_low)?)?;
382    let div_high = vm.get_integer((div_addr + div_offset_high)?)?;
383    let div_low = div_low.as_ref();
384    let div_high = div_high.as_ref();
385
386    //Main logic
387    //a = (ids.a.high << 128) + ids.a.low
388    //div = (ids.div.high << 128) + ids.div.low
389    //quotient, remainder = divmod(a, div)
390
391    //ids.quotient.low = quotient & ((1 << 128) - 1)
392    //ids.quotient.high = quotient >> 128
393    //ids.remainder.low = remainder & ((1 << 128) - 1)
394    //ids.remainder.high = remainder >> 128
395
396    let a = (a_high.to_biguint() << 128_u32) + a_low.to_biguint();
397    let div = (div_high.to_biguint() << 128_u32) + div_low.to_biguint();
398    //a and div will always be positive numbers
399    //Then, Rust div_rem equals Python divmod
400    let (quotient, remainder) = div_rem(a, div);
401
402    let quotient = Uint256::from(&quotient);
403    let remainder = Uint256::from(&remainder);
404
405    quotient.insert_from_var_name("quotient", vm, ids_data, ap_tracking)?;
406    remainder.insert_from_var_name("remainder", vm, ids_data, ap_tracking)?;
407
408    Ok(())
409}
410
411/* Implements Hint:
412%{
413a = (ids.a.high << 128) + ids.a.low
414b = (ids.b.high << 128) + ids.b.low
415div = (ids.div.high << 128) + ids.div.low
416quotient, remainder = divmod(a * b, div)
417
418ids.quotient_low.low = quotient & ((1 << 128) - 1)
419ids.quotient_low.high = (quotient >> 128) & ((1 << 128) - 1)
420ids.quotient_high.low = (quotient >> 256) & ((1 << 128) - 1)
421ids.quotient_high.high = quotient >> 384
422ids.remainder.low = remainder & ((1 << 128) - 1)
423ids.remainder.high = remainder >> 128
424%}
425*/
426pub fn uint256_mul_div_mod(
427    vm: &mut VirtualMachine,
428    ids_data: &HashMap<String, HintReference>,
429    ap_tracking: &ApTracking,
430) -> Result<(), HintError> {
431    // Extract variables
432    let a_addr = get_relocatable_from_var_name("a", vm, ids_data, ap_tracking)?;
433    let b_addr = get_relocatable_from_var_name("b", vm, ids_data, ap_tracking)?;
434    let div_addr = get_relocatable_from_var_name("div", vm, ids_data, ap_tracking)?;
435    let quotient_low_addr =
436        get_relocatable_from_var_name("quotient_low", vm, ids_data, ap_tracking)?;
437    let quotient_high_addr =
438        get_relocatable_from_var_name("quotient_high", vm, ids_data, ap_tracking)?;
439    let remainder_addr = get_relocatable_from_var_name("remainder", vm, ids_data, ap_tracking)?;
440
441    let a_low = vm.get_integer(a_addr)?;
442    let a_high = vm.get_integer((a_addr + 1_usize)?)?;
443    let b_low = vm.get_integer(b_addr)?;
444    let b_high = vm.get_integer((b_addr + 1_usize)?)?;
445    let div_low = vm.get_integer(div_addr)?;
446    let div_high = vm.get_integer((div_addr + 1_usize)?)?;
447    let a_low = a_low.as_ref();
448    let a_high = a_high.as_ref();
449    let b_low = b_low.as_ref();
450    let b_high = b_high.as_ref();
451    let div_low = div_low.as_ref();
452    let div_high = div_high.as_ref();
453
454    // Main Logic
455    let a = a_high.to_biguint().shl(128_usize) + a_low.to_biguint();
456    let b = b_high.to_biguint().shl(128_usize) + b_low.to_biguint();
457    let div = div_high.to_biguint().shl(128_usize) + div_low.to_biguint();
458    if div.is_zero() {
459        return Err(MathError::DividedByZero.into());
460    }
461    let (quotient, remainder) = (a * b).div_mod_floor(&div);
462
463    // ids.quotient_low.low
464    vm.insert_value(
465        quotient_low_addr,
466        Felt252::from(&(&quotient & &BigUint::from(u128::MAX))),
467    )?;
468    // ids.quotient_low.high
469    vm.insert_value(
470        (quotient_low_addr + 1)?,
471        Felt252::from(&((&quotient).shr(128_u32) & &BigUint::from(u128::MAX))),
472    )?;
473    // ids.quotient_high.low
474    vm.insert_value(
475        quotient_high_addr,
476        Felt252::from(&((&quotient).shr(256_u32) & &BigUint::from(u128::MAX))),
477    )?;
478    // ids.quotient_high.high
479    vm.insert_value(
480        (quotient_high_addr + 1)?,
481        Felt252::from(&((&quotient).shr(384_u32))),
482    )?;
483    //ids.remainder.low
484    vm.insert_value(
485        remainder_addr,
486        Felt252::from(&(&remainder & &BigUint::from(u128::MAX))),
487    )?;
488    //ids.remainder.high
489    vm.insert_value(
490        (remainder_addr + 1)?,
491        Felt252::from(&remainder.shr(128_u32)),
492    )?;
493
494    Ok(())
495}
496
497#[cfg(test)]
498mod tests {
499    use super::*;
500    use crate::felt_str;
501    use crate::{
502        any_box,
503        hint_processor::{
504            builtin_hint_processor::{
505                builtin_hint_processor_definition::{BuiltinHintProcessor, HintProcessorData},
506                hint_code,
507            },
508            hint_processor_definition::HintProcessorLogic,
509        },
510        types::relocatable::{MaybeRelocatable, Relocatable},
511        utils::test_utils::*,
512        vm::{errors::memory_errors::MemoryError, vm_core::VirtualMachine},
513    };
514    use assert_matches::assert_matches;
515
516    #[cfg(target_arch = "wasm32")]
517    use wasm_bindgen_test::*;
518
519    #[test]
520    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
521    fn run_uint256_add_ok() {
522        let hint_code = hint_code::UINT256_ADD;
523        let mut vm = vm_with_range_check!();
524        //Initialize fp
525        vm.run_context.fp = 10;
526        //Create hint_data
527        let ids_data =
528            non_continuous_ids_data![("a", -6), ("b", -4), ("carry_low", 2), ("carry_high", 3)];
529        vm.segments = segments![
530            ((1, 4), 2),
531            ((1, 5), 3),
532            ((1, 6), 4),
533            ((1, 7), ("340282366920938463463374607431768211455", 10))
534        ];
535        //Execute the hint
536        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
537        //Check hint memory inserts
538        check_memory![vm.segments.memory, ((1, 12), 0), ((1, 13), 1)];
539    }
540
541    #[test]
542    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
543    fn run_uint256_add_low_only_ok() {
544        let hint_code =
545            "sum_low = ids.a.low + ids.b.low\nids.carry_low = 1 if sum_low >= ids.SHIFT else 0";
546        let mut vm = vm_with_range_check!();
547        //Initialize fp
548        vm.run_context.fp = 10;
549        //Create hint_data
550        let ids_data = non_continuous_ids_data![("a", -6), ("b", -4), ("carry_low", 2)];
551        vm.segments = segments![
552            ((1, 4), 2),
553            ((1, 5), 3),
554            ((1, 6), 4),
555            ((1, 7), ("340282366920938463463374607431768211455", 10))
556        ];
557        //Execute the hint
558        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
559        //Check hint memory inserts
560        check_memory![vm.segments.memory, ((1, 12), 0)];
561    }
562
563    #[test]
564    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
565    fn run_uint128_add_ok() {
566        let hint_code = hint_code::UINT128_ADD;
567        let mut vm = vm_with_range_check!();
568        // Initialize fp
569        vm.run_context.fp = 0;
570        // Create hint_data
571        let ids_data = non_continuous_ids_data![("a", 0), ("b", 1), ("carry", 2)];
572        vm.segments = segments![
573            ((1, 0), 180141183460469231731687303715884105727_u128),
574            ((1, 1), 180141183460469231731687303715884105727_u128),
575        ];
576        // Execute the hint
577        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
578        // Check hint memory inserts
579        check_memory![vm.segments.memory, ((1, 2), 1)];
580    }
581
582    #[test]
583    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
584    fn run_uint256_add_fail_inserts() {
585        let hint_code = hint_code::UINT256_ADD;
586        let mut vm = vm_with_range_check!();
587        //Initialize fp
588        vm.run_context.fp = 10;
589        //Create hint_data
590        let ids_data =
591            non_continuous_ids_data![("a", -6), ("b", -4), ("carry_high", 3), ("carry_low", 2)];
592        //Insert ids into memory
593        vm.segments = segments![
594            ((1, 4), 2),
595            ((1, 5), 3),
596            ((1, 6), 4),
597            ((1, 7), 2),
598            ((1, 12), 2)
599        ];
600        //Execute the hint
601        assert_matches!(
602            run_hint!(vm, ids_data, hint_code),
603            Err(HintError::Memory(
604                MemoryError::InconsistentMemory(bx)
605            )) if *bx == (Relocatable::from((1, 12)),
606                    MaybeRelocatable::from(Felt252::from(2)),
607                    MaybeRelocatable::from(Felt252::ZERO))
608        );
609    }
610
611    #[test]
612    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
613    fn run_uint256_sub_nonnegative_ok() {
614        let hint_code = hint_code::UINT256_SUB;
615        let mut vm = vm_with_range_check!();
616        //Initialize fp
617        vm.run_context.fp = 0;
618        //Create hint_data
619        let ids_data = non_continuous_ids_data![("a", 0), ("b", 2), ("res", 4)];
620        vm.segments = segments![
621            ((1, 0), 12179),
622            ((1, 1), 13044),
623            ((1, 2), 1001),
624            ((1, 3), 6687),
625        ];
626        //Execute the hint
627        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
628        //Check hint memory inserts
629        check_memory![vm.segments.memory, ((1, 4), 11178), ((1, 5), 6357)];
630    }
631
632    #[test]
633    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
634    fn run_uint256_sub_negative_ok() {
635        let hint_code = hint_code::UINT256_SUB;
636        let mut vm = vm_with_range_check!();
637        //Initialize fp
638        vm.run_context.fp = 0;
639        //Create hint_data
640        let ids_data = non_continuous_ids_data![("a", 0), ("b", 2), ("res", 4)];
641        vm.segments = segments![
642            ((1, 0), 1001),
643            ((1, 1), 6687),
644            ((1, 2), 12179),
645            ((1, 3), 13044),
646        ];
647        //Execute the hint
648        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
649        //Check hint memory inserts
650        check_memory![
651            vm.segments.memory,
652            ((1, 4), ("340282366920938463463374607431768200278", 10)),
653            ((1, 5), ("340282366920938463463374607431768205098", 10))
654        ];
655    }
656
657    #[test]
658    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
659    fn run_uint256_sub_b_high_gt_256_lte_a() {
660        let hint_code = hint_code::UINT256_SUB;
661        let mut vm = vm_with_range_check!();
662        //Initialize fp
663        vm.run_context.fp = 0;
664        //Create hint_data
665        let ids_data = non_continuous_ids_data![("a", 0), ("b", 2), ("res", 4)];
666        vm.segments = segments![
667            ((1, 0), ("340282366920938463463374607431768211456", 10)),
668            ((1, 1), 0),
669            ((1, 2), 0),
670            ((1, 3), ("340282366920938463463374607431768211457", 10)),
671        ];
672        //Execute the hint
673        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
674        //Check hint memory inserts
675        check_memory![vm.segments.memory, ((1, 4), 0), ((1, 5), 0)];
676    }
677
678    #[test]
679    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
680    fn run_uint256_sub_b_high_gt_256_gt_a() {
681        let hint_code = hint_code::UINT256_SUB;
682        let mut vm = vm_with_range_check!();
683        //Initialize fp
684        vm.run_context.fp = 0;
685        //Create hint_data
686        let ids_data = non_continuous_ids_data![("a", 0), ("b", 2), ("res", 4)];
687        vm.segments = segments![
688            ((1, 0), 1),
689            ((1, 1), 0),
690            ((1, 2), 0),
691            ((1, 3), ("340282366920938463463374607431768211457", 10)),
692        ];
693        //Execute the hint
694        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
695        //Check hint memory inserts
696        check_memory![
697            vm.segments.memory,
698            ((1, 4), ("1", 10)),
699            ((1, 5), ("340282366920938463463374607431768211455", 10))
700        ];
701    }
702
703    #[test]
704    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
705    fn run_uint256_sub_missing_member() {
706        let hint_code = hint_code::UINT256_SUB;
707        let mut vm = vm_with_range_check!();
708        //Initialize fp
709        vm.run_context.fp = 0;
710        //Create hint_data
711        let ids_data = non_continuous_ids_data![("a", 0)];
712        //Execute the hint
713        assert_matches!(run_hint!(vm, ids_data.clone(), hint_code),
714            Err(HintError::IdentifierHasNoMember(bx)) if *bx == ("a".to_string(), "low".to_string())
715        );
716        vm.segments = segments![((1, 0), 1001)];
717        //Execute the hint
718        assert_matches!(run_hint!(vm, ids_data, hint_code),
719            Err(HintError::IdentifierHasNoMember(bx)) if *bx == ("a".to_string(), "high".to_string())
720        );
721    }
722
723    #[test]
724    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
725    fn run_split_64_ok() {
726        let hint_code = "ids.low = ids.a & ((1<<64) - 1)\nids.high = ids.a >> 64";
727        let mut vm = vm_with_range_check!();
728        //Initialize fp
729        vm.run_context.fp = 10;
730        //Create hint_data
731        let ids_data = non_continuous_ids_data![("a", -3), ("high", 1), ("low", 0)];
732        //Insert ids.a into memory
733        vm.segments = segments![((1, 7), ("850981239023189021389081239089023", 10))];
734        //Execute the hint
735        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
736        //Check hint memory inserts
737        //ids.low, ids.high
738        check_memory![
739            vm.segments.memory,
740            ((1, 10), 7249717543555297151_u64),
741            ((1, 11), 46131785404667_u64)
742        ];
743    }
744
745    #[test]
746    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
747    fn run_split_64_with_big_a() {
748        let hint_code = "ids.low = ids.a & ((1<<64) - 1)\nids.high = ids.a >> 64";
749        let mut vm = vm_with_range_check!();
750        //Initialize fp
751        vm.run_context.fp = 10;
752        //Create ids_data
753        let ids_data = non_continuous_ids_data![("a", -3), ("high", 1), ("low", 0)];
754        //Insert ids.a into memory
755        vm.segments = segments![((1, 7), ("400066369019890261321163226850167045262", 10))];
756        //Execute the hint
757        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
758
759        //Check hint memory inserts
760        //ids.low, ids.high
761        check_memory![
762            vm.segments.memory,
763            ((1, 10), 2279400676465785998_u64),
764            ((1, 11), 21687641321487626429_u128)
765        ];
766    }
767
768    #[test]
769    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
770    fn run_split_64_memory_error() {
771        let hint_code = "ids.low = ids.a & ((1<<64) - 1)\nids.high = ids.a >> 64";
772        let mut vm = vm_with_range_check!();
773        //Initialize fp
774        vm.run_context.fp = 10;
775        //Create hint_data
776        let ids_data = non_continuous_ids_data![("a", -3), ("high", 1), ("low", 0)];
777        //Insert ids.a into memory
778        vm.segments = segments![
779            ((1, 7), ("850981239023189021389081239089023", 10)),
780            ((1, 10), 0)
781        ];
782        //Execute the hint
783        assert_matches!(
784            run_hint!(vm, ids_data, hint_code),
785            Err(HintError::Memory(
786                MemoryError::InconsistentMemory(bx)
787            )) if *bx == (Relocatable::from((1, 10)),
788                    MaybeRelocatable::from(Felt252::ZERO),
789                    MaybeRelocatable::from(felt_str!("7249717543555297151")))
790        );
791    }
792
793    #[test]
794    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
795    fn run_uint256_sqrt_ok() {
796        let hint_code = hint_code::UINT256_SQRT;
797        let mut vm = vm_with_range_check!();
798        //Initialize fp
799        vm.run_context.fp = 5;
800        //Create hint_data
801        let ids_data = non_continuous_ids_data![("n", -5), ("root", 0)];
802        vm.segments = segments![((1, 0), 17), ((1, 1), 7)];
803        //Execute the hint
804        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
805        //Check hint memory inserts
806        //ids.root.low, ids.root.high
807        check_memory![
808            vm.segments.memory,
809            ((1, 5), 48805497317890012913_u128),
810            ((1, 6), 0)
811        ];
812    }
813
814    #[test]
815    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
816    fn run_uint256_sqrt_felt_ok() {
817        let hint_code = "from starkware.python.math_utils import isqrt\nn = (ids.n.high << 128) + ids.n.low\nroot = isqrt(n)\nassert 0 <= root < 2 ** 128\nids.root = root";
818        let mut vm = vm_with_range_check!();
819        //Initialize fp
820        vm.run_context.fp = 0;
821        //Create hint_data
822        let ids_data = non_continuous_ids_data![("n", 0), ("root", 2)];
823        vm.segments = segments![((1, 0), 879232), ((1, 1), 135906)];
824        //Execute the hint
825        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
826        //Check hint memory inserts
827        //ids.root
828        check_memory![vm.segments.memory, ((1, 2), 6800471701195223914689)];
829    }
830
831    #[test]
832    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
833    fn run_uint256_sqrt_assert_error() {
834        let hint_code = "from starkware.python.math_utils import isqrt\nn = (ids.n.high << 128) + ids.n.low\nroot = isqrt(n)\nassert 0 <= root < 2 ** 128\nids.root.low = root\nids.root.high = 0";
835        let mut vm = vm_with_range_check!();
836        //Initialize fp
837        vm.run_context.fp = 5;
838        //Create hint_data
839        let ids_data = non_continuous_ids_data![("n", -5), ("root", 0)];
840        vm.segments = segments![
841            ((1, 0), 0),
842            ((1, 1), ("340282366920938463463374607431768211458", 10))
843        ];
844        //Execute the hint
845        assert_matches!(
846            run_hint!(vm, ids_data, hint_code),
847            Err(HintError::AssertionFailed(bx)) if bx.as_ref() == "assert 0 <= 340282366920938463463374607431768211456 < 2 ** 128"
848        );
849    }
850
851    #[test]
852    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
853    fn run_uint256_invalid_memory_insert() {
854        let hint_code = "from starkware.python.math_utils import isqrt\nn = (ids.n.high << 128) + ids.n.low\nroot = isqrt(n)\nassert 0 <= root < 2 ** 128\nids.root.low = root\nids.root.high = 0";
855        let mut vm = vm_with_range_check!();
856        //Initialize fp
857        vm.run_context.fp = 5;
858        //Create hint_data
859        let ids_data = non_continuous_ids_data![("n", -5), ("root", 0)];
860        //Insert  ids.n.low into memory
861        vm.segments = segments![((1, 0), 17), ((1, 1), 7), ((1, 5), 1)];
862        //Execute the hint
863        assert_matches!(
864            run_hint!(vm, ids_data, hint_code),
865            Err(HintError::Memory(
866                MemoryError::InconsistentMemory(bx)
867            )) if *bx == (Relocatable::from((1, 5)),
868                    MaybeRelocatable::from(Felt252::ONE),
869                    MaybeRelocatable::from(felt_str!("48805497317890012913")))
870        );
871    }
872
873    #[test]
874    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
875    fn run_signed_nn_ok_result_one() {
876        let hint_code = "memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0";
877        let mut vm = vm_with_range_check!();
878        //Initialize run_context
879        run_context!(vm, 0, 5, 4);
880        //Create hint_data
881        let ids_data = non_continuous_ids_data![("a", -4)];
882        //Insert ids.a.high into memory
883        vm.segments = segments![(
884            (1, 1),
885            (
886                "3618502788666131213697322783095070105793248398792065931704779359851756126208",
887                10
888            )
889        )];
890        //Execute the hint
891        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
892        //Check hint memory insert
893        //memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0
894        check_memory![vm.segments.memory, ((1, 5), 1)];
895    }
896
897    #[test]
898    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
899    fn run_signed_nn_ok_result_zero() {
900        let hint_code = "memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0";
901        let mut vm = vm_with_range_check!();
902        //Initialize run_context
903        run_context!(vm, 0, 5, 4);
904        //Create hint_data
905        let ids_data = non_continuous_ids_data![("a", -4)];
906        //Insert ids.a.high into memory
907        vm.segments = segments![(
908            (1, 1),
909            (
910                "3618502788666131213697322783095070105793248398792065931704779359851756126209",
911                10
912            )
913        )];
914        //Execute the hint
915        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
916        //Check hint memory insert
917        //memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0
918        check_memory![vm.segments.memory, ((1, 5), 0)];
919    }
920
921    #[test]
922    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
923    fn run_signed_nn_ok_invalid_memory_insert() {
924        let hint_code = "memory[ap] = 1 if 0 <= (ids.a.high % PRIME) < 2 ** 127 else 0";
925        let mut vm = vm_with_range_check!();
926        //Initialize run_context
927        run_context!(vm, 0, 5, 4);
928        //Create hint_data
929        let ids_data = non_continuous_ids_data![("a", -4)];
930        vm.segments = segments![((1, 1), 1), ((1, 5), 55)];
931        //Execute the hint
932        assert_matches!(
933            run_hint!(vm, ids_data, hint_code),
934            Err(HintError::Memory(
935                MemoryError::InconsistentMemory(bx)
936            )) if *bx == (Relocatable::from((1, 5)),
937                    MaybeRelocatable::from(Felt252::from(55)),
938                    MaybeRelocatable::from(Felt252::ONE))
939        );
940    }
941
942    #[test]
943    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
944    fn run_unsigned_div_rem_ok() {
945        let hint_code = hint_code::UINT256_UNSIGNED_DIV_REM;
946        let mut vm = vm_with_range_check!();
947        //Initialize fp
948        vm.run_context.fp = 10;
949        //Create hint_data
950        let ids_data =
951            non_continuous_ids_data![("a", -6), ("div", -4), ("quotient", 0), ("remainder", 2)];
952        //Insert ids into memory
953        vm.segments = segments![((1, 4), 89), ((1, 5), 72), ((1, 6), 3), ((1, 7), 7)];
954        //Execute the hint
955        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
956        //Check hint memory inserts
957        //ids.quotient.low, ids.quotient.high, ids.remainder.low, ids.remainder.high
958        check_memory![
959            vm.segments.memory,
960            ((1, 10), 10),
961            ((1, 11), 0),
962            ((1, 12), 59),
963            ((1, 13), 2)
964        ];
965    }
966
967    #[test]
968    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
969    fn run_unsigned_div_rem_expanded_ok() {
970        let hint_code = hint_code::UINT256_EXPANDED_UNSIGNED_DIV_REM;
971        let mut vm = vm_with_range_check!();
972        //Initialize fp
973        vm.run_context.fp = 0;
974        //Create hint_data
975        let ids_data =
976            non_continuous_ids_data![("a", 0), ("div", 2), ("quotient", 7), ("remainder", 9)];
977        //Insert ids into memory
978        vm.segments = segments![
979            // (72 << 128) + 89
980            ((1, 0), 89),
981            ((1, 1), 72),
982            // uint256_expand((7 << 128) + 3)
983            ((1, 2), 55340232221128654848),
984            ((1, 3), 3),
985            ((1, 4), 129127208515966861312),
986            ((1, 5), 7),
987            ((1, 6), 0),
988        ];
989        //Execute the hint
990        assert_matches!(run_hint!(vm, ids_data, hint_code), Ok(()));
991        //Check hint memory inserts
992        //ids.quotient.low, ids.quotient.high, ids.remainder.low, ids.remainder.high
993        check_memory![
994            vm.segments.memory,
995            ((1, 7), 10),
996            ((1, 8), 0),
997            ((1, 9), 59),
998            ((1, 10), 2),
999        ];
1000    }
1001
1002    #[test]
1003    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1004    fn run_unsigned_div_rem_invalid_memory_insert() {
1005        let hint_code = hint_code::UINT256_UNSIGNED_DIV_REM;
1006        let mut vm = vm_with_range_check!();
1007        //Initialize fp
1008        vm.run_context.fp = 10;
1009        //Create hint_data
1010        let ids_data =
1011            non_continuous_ids_data![("a", -6), ("div", -4), ("quotient", 0), ("remainder", 2)];
1012        //Insert ids into memory
1013        vm.segments = segments![
1014            ((1, 4), 89),
1015            ((1, 5), 72),
1016            ((1, 6), 3),
1017            ((1, 7), 7),
1018            ((1, 10), 0)
1019        ];
1020        //Execute the hint
1021        assert_matches!(
1022            run_hint!(vm, ids_data, hint_code),
1023            Err(HintError::Memory(
1024                MemoryError::InconsistentMemory(bx)
1025            )) if *bx == (Relocatable::from((1, 10)),
1026                    MaybeRelocatable::from(Felt252::ZERO),
1027                    MaybeRelocatable::from(Felt252::from(10)))
1028        );
1029    }
1030
1031    #[test]
1032    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1033    fn run_unsigned_div_rem_invalid_memory_insert_2() {
1034        let hint_code = hint_code::UINT256_UNSIGNED_DIV_REM;
1035        let mut vm = vm_with_range_check!();
1036        //Initialize fp
1037        vm.run_context.fp = 10;
1038        //Create hint_data
1039        let ids_data =
1040            non_continuous_ids_data![("a", -6), ("div", -4), ("quotient", 0), ("remainder", 2)];
1041        //Insert ids into memory
1042        vm.segments = segments![
1043            ((1, 4), 89),
1044            ((1, 5), 72),
1045            ((1, 6), 3),
1046            ((1, 7), 7),
1047            ((1, 11), 1)
1048        ];
1049        //Execute the hint
1050        assert_matches!(
1051            run_hint!(vm, ids_data, hint_code),
1052            Err(HintError::Memory(
1053                MemoryError::InconsistentMemory(bx)
1054            )) if *bx == (Relocatable::from((1, 11)),
1055                    MaybeRelocatable::from(Felt252::ONE),
1056                    MaybeRelocatable::from(Felt252::ZERO))
1057        );
1058    }
1059
1060    #[test]
1061    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1062    fn run_mul_div_mod_ok() {
1063        let mut vm = vm_with_range_check!();
1064        //Initialize fp
1065        vm.run_context.fp = 10;
1066        //Create hint_data
1067        let ids_data = non_continuous_ids_data![
1068            ("a", -8),
1069            ("b", -6),
1070            ("div", -4),
1071            ("quotient_low", 0),
1072            ("quotient_high", 2),
1073            ("remainder", 4)
1074        ];
1075        //Insert ids into memory
1076        vm.segments = segments![
1077            ((1, 2), 89),
1078            ((1, 3), 72),
1079            ((1, 4), 3),
1080            ((1, 5), 7),
1081            ((1, 6), 107),
1082            ((1, 7), 114)
1083        ];
1084        //Execute the hint
1085        assert_matches!(
1086            run_hint!(vm, ids_data, hint_code::UINT256_MUL_DIV_MOD),
1087            Ok(())
1088        );
1089        //Check hint memory inserts
1090        //ids.quotient.low, ids.quotient.high, ids.remainder.low, ids.remainder.high
1091        check_memory![
1092            vm.segments.memory,
1093            ((1, 10), 143276786071974089879315624181797141668),
1094            ((1, 11), 4),
1095            ((1, 12), 0),
1096            ((1, 13), 0),
1097            //((1, 14), 322372768661941702228460154409043568767),
1098            ((1, 15), 101)
1099        ];
1100        assert_eq!(
1101            vm.segments
1102                .memory
1103                .get_integer((1, 14).into())
1104                .unwrap()
1105                .as_ref(),
1106            &felt_str!("322372768661941702228460154409043568767")
1107        )
1108    }
1109
1110    #[test]
1111    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
1112    fn run_mul_div_mod_missing_ids() {
1113        let mut vm = vm_with_range_check!();
1114        //Initialize fp
1115        vm.run_context.fp = 10;
1116        //Create hint_data
1117        let ids_data = non_continuous_ids_data![
1118            ("a", -8),
1119            ("b", -6),
1120            ("div", -4),
1121            ("quotient", 0),
1122            ("remainder", 2)
1123        ];
1124        //Insert ids into memory
1125        vm.segments = segments![
1126            ((1, 2), 89),
1127            ((1, 3), 72),
1128            ((1, 4), 3),
1129            ((1, 5), 7),
1130            ((1, 6), 107),
1131            ((1, 7), 114)
1132        ];
1133        //Execute the hint
1134        assert_matches!(
1135            run_hint!(vm, ids_data, hint_code::UINT256_MUL_DIV_MOD),
1136            Err(HintError::UnknownIdentifier(bx)) if bx.as_ref() == "quotient_low"
1137        );
1138    }
1139}