From ada5a2b3df53364b34e7d6500562d189757564c2 Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 11 Apr 2023 17:02:04 -0300 Subject: [PATCH 01/30] Add hint code for UINT348_UNSIGNED_DIV_REM --- .../builtin_hint_processor/hint_code.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 5ee26e2deb..d0231194d7 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -608,5 +608,34 @@ from starkware.python.math_utils import recover_y ids.p.x = ids.x # This raises an exception if `x` is not on the curve. ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME)"; +// The following hints support the lib https://github.com/NethermindEth/research-basic-Cairo-operations-big-integers/blob/main/lib/uint384.cairo +pub(crate) const UINT348_UNSIGNED_DIV_REM: &str = + "def split(num: int, num_bits_shift: int, length: int): +a = [] +for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift +return tuple(a) + +def pack(z, num_bits_shift: int) -> int: +limbs = (z.d0, z.d1, z.d2) +return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + +a = pack(ids.a, num_bits_shift = 128) +div = pack(ids.div, num_bits_shift = 128) +quotient, remainder = divmod(a, div) + +quotient_split = split(quotient, num_bits_shift=128, length=3) +assert len(quotient_split) == 3 + +ids.quotient.d0 = quotient_split[0] +ids.quotient.d1 = quotient_split[1] +ids.quotient.d2 = quotient_split[2] + +remainder_split = split(remainder, num_bits_shift=128, length=3) +ids.remainder.d0 = remainder_split[0] +ids.remainder.d1 = remainder_split[1] +ids.remainder.d2 = remainder_split[2]"; + #[cfg(feature = "skip_next_instruction_hint")] pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; From d1b7e842194bb0c1f319f7774989323022fdabbc Mon Sep 17 00:00:00 2001 From: Federica Date: Tue, 11 Apr 2023 17:14:20 -0300 Subject: [PATCH 02/30] Add file for uint348 files --- .../builtin_hint_processor/mod.rs | 1 + .../builtin_hint_processor/uint348.rs | 45 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 src/hint_processor/builtin_hint_processor/uint348.rs diff --git a/src/hint_processor/builtin_hint_processor/mod.rs b/src/hint_processor/builtin_hint_processor/mod.rs index 75abd3a6e9..14abe764e5 100644 --- a/src/hint_processor/builtin_hint_processor/mod.rs +++ b/src/hint_processor/builtin_hint_processor/mod.rs @@ -24,4 +24,5 @@ pub mod signature; pub mod skip_next_instruction; pub mod squash_dict_utils; pub mod uint256_utils; +pub mod uint348; pub mod usort; diff --git a/src/hint_processor/builtin_hint_processor/uint348.rs b/src/hint_processor/builtin_hint_processor/uint348.rs new file mode 100644 index 0000000000..6c6f50638c --- /dev/null +++ b/src/hint_processor/builtin_hint_processor/uint348.rs @@ -0,0 +1,45 @@ +use crate::stdlib::{collections::HashMap, prelude::*}; +use crate::{ + hint_processor::hint_processor_definition::HintReference, + serde::deserialize_program::ApTracking, + vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, +}; + +// Notes: Hints in this lib use the type Uint348, which is equal to common lib's BigInt3 +/* Implements Hint: + %{ + def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a, num_bits_shift = 128) + div = pack(ids.div, num_bits_shift = 128) + quotient, remainder = divmod(a, div) + + quotient_split = split(quotient, num_bits_shift=128, length=3) + assert len(quotient_split) == 3 + + ids.quotient.d0 = quotient_split[0] + ids.quotient.d1 = quotient_split[1] + ids.quotient.d2 = quotient_split[2] + + remainder_split = split(remainder, num_bits_shift=128, length=3) + ids.remainder.d0 = remainder_split[0] + ids.remainder.d1 = remainder_split[1] + ids.remainder.d2 = remainder_split[2] + %} +*/ +pub fn uint348_unsigned_div_rem( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + Ok(()) +} From c409d015c6e504ef2247ed83c7eb8c1d1880c419 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 11:11:44 -0300 Subject: [PATCH 03/30] Add pack & split for uint348 --- .../builtin_hint_processor/uint348.rs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/hint_processor/builtin_hint_processor/uint348.rs b/src/hint_processor/builtin_hint_processor/uint348.rs index 6c6f50638c..c33ffb3165 100644 --- a/src/hint_processor/builtin_hint_processor/uint348.rs +++ b/src/hint_processor/builtin_hint_processor/uint348.rs @@ -1,3 +1,9 @@ +use core::ops::Shl; + +use felt::Felt252; +use num_bigint::BigUint; +use num_traits::One; + use crate::stdlib::{collections::HashMap, prelude::*}; use crate::{ hint_processor::hint_processor_definition::HintReference, @@ -5,6 +11,27 @@ use crate::{ vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; +use super::secp::bigint_utils::BigInt3; + +fn split(num: &Felt252, num_bits_shift: u32) -> [Felt252; T] { + let mut num = num.clone(); + [0; T].map(|_| { + let a = &num & &((Felt252::one() << num_bits_shift) - 1_u32); + num = &num >> num_bits_shift; + a + }) +} + +fn pack(num: BigInt3, num_bits_shift: usize) -> BigUint { + let limbs = vec![num.d0, num.d1, num.d2]; + #[allow(deprecated)] + limbs + .into_iter() + .enumerate() + .map(|(idx, value)| value.to_biguint().shl(idx * num_bits_shift)) + .sum() +} + // Notes: Hints in this lib use the type Uint348, which is equal to common lib's BigInt3 /* Implements Hint: %{ From 93c6f4e2a28947962b829604d785061fabaaa69d Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 11:12:29 -0300 Subject: [PATCH 04/30] Move comment --- src/hint_processor/builtin_hint_processor/uint348.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/uint348.rs b/src/hint_processor/builtin_hint_processor/uint348.rs index c33ffb3165..c1a291fd9f 100644 --- a/src/hint_processor/builtin_hint_processor/uint348.rs +++ b/src/hint_processor/builtin_hint_processor/uint348.rs @@ -12,6 +12,7 @@ use crate::{ }; use super::secp::bigint_utils::BigInt3; +// Notes: Hints in this lib use the type Uint348, which is equal to common lib's BigInt3 fn split(num: &Felt252, num_bits_shift: u32) -> [Felt252; T] { let mut num = num.clone(); @@ -31,8 +32,6 @@ fn pack(num: BigInt3, num_bits_shift: usize) -> BigUint { .map(|(idx, value)| value.to_biguint().shl(idx * num_bits_shift)) .sum() } - -// Notes: Hints in this lib use the type Uint348, which is equal to common lib's BigInt3 /* Implements Hint: %{ def split(num: int, num_bits_shift: int, length: int): From 82dbf2bc9c9a26ff0ec7ff1f440d18c84b0f848c Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 11:25:01 -0300 Subject: [PATCH 05/30] Implement uint348_unsigned_div_rem hint --- .../builtin_hint_processor/uint348.rs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/uint348.rs b/src/hint_processor/builtin_hint_processor/uint348.rs index c1a291fd9f..e16bd57bab 100644 --- a/src/hint_processor/builtin_hint_processor/uint348.rs +++ b/src/hint_processor/builtin_hint_processor/uint348.rs @@ -1,7 +1,7 @@ use core::ops::Shl; - use felt::Felt252; use num_bigint::BigUint; +use num_integer::Integer; use num_traits::One; use crate::stdlib::{collections::HashMap, prelude::*}; @@ -11,13 +11,14 @@ use crate::{ vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; +use super::hint_utils::get_relocatable_from_var_name; use super::secp::bigint_utils::BigInt3; // Notes: Hints in this lib use the type Uint348, which is equal to common lib's BigInt3 -fn split(num: &Felt252, num_bits_shift: u32) -> [Felt252; T] { +fn split(num: &BigUint, num_bits_shift: u32) -> [BigUint; T] { let mut num = num.clone(); [0; T].map(|_| { - let a = &num & &((Felt252::one() << num_bits_shift) - 1_u32); + let a = &num & &((BigUint::one() << num_bits_shift) - 1_u32); num = &num >> num_bits_shift; a }) @@ -67,5 +68,21 @@ pub fn uint348_unsigned_div_rem( ids_data: &HashMap, ap_tracking: &ApTracking, ) -> Result<(), HintError> { + let a = pack(BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?, 128); + let div = pack( + BigInt3::from_var_name("div", vm, ids_data, ap_tracking)?, + 128, + ); + let quotient_addr = get_relocatable_from_var_name("quotient", vm, ids_data, ap_tracking)?; + let remainder_addr = get_relocatable_from_var_name("quotient", vm, ids_data, ap_tracking)?; + let (quotient, remainder) = a.div_mod_floor(&div); + let quotient_split = split::<3>("ient, 128); + for (i, quotient_split) in quotient_split.iter().enumerate() { + vm.insert_value((quotient_addr + i)?, Felt252::from(quotient_split))?; + } + let remainder_split = split::<3>(&remainder, 128); + for (i, remainder_split) in remainder_split.iter().enumerate() { + vm.insert_value((remainder_addr + i)?, Felt252::from(remainder_split))?; + } Ok(()) } From df9deb2f98bd1626e4b4167922e3b4971cc12814 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 12:14:54 -0300 Subject: [PATCH 06/30] Add integration test --- cairo_programs/uint384.cairo | 250 ++++++++++++++++++ .../builtin_hint_processor_definition.rs | 4 + .../builtin_hint_processor/hint_code.rs | 28 +- .../builtin_hint_processor/mod.rs | 2 +- .../{uint348.rs => uint384.rs} | 6 +- 5 files changed, 259 insertions(+), 31 deletions(-) create mode 100644 cairo_programs/uint384.cairo rename src/hint_processor/builtin_hint_processor/{uint348.rs => uint384.rs} (94%) diff --git a/cairo_programs/uint384.cairo b/cairo_programs/uint384.cairo new file mode 100644 index 0000000000..e6b443893d --- /dev/null +++ b/cairo_programs/uint384.cairo @@ -0,0 +1,250 @@ +%builtins range_check +// Code taken from https://github.com/NethermindEth/research-basic-Cairo-operations-big-integers/blob/main/lib/uint384.cairo +from starkware.cairo.common.bitwise import bitwise_and, bitwise_or, bitwise_xor +from starkware.cairo.common.cairo_builtins import BitwiseBuiltin +from starkware.cairo.common.math import assert_in_range, assert_le, assert_nn_le, assert_not_zero +from starkware.cairo.common.math import unsigned_div_rem as frem +from starkware.cairo.common.math_cmp import is_le +from starkware.cairo.common.uint256 import Uint256, uint256_add, word_reverse_endian +from starkware.cairo.common.pow import pow +from starkware.cairo.common.registers import get_ap, get_fp_and_pc + +// This library is adapted from Cairo's common library Uint256 and it follows it as closely as possible. +// The library implements basic operations between 384-bit integers. +// Most operations use unsigned integers. Only a few operations are implemented for signed integers + +// Represents an integer in the range [0, 2^384). +struct Uint384 { + // The low 128 bits of the value. + d0: felt, + // The middle 128 bits of the value. + d1: felt, + // The # 128 bits of the value. + d2: felt, +} + +const SHIFT = 2 ** 128; +const ALL_ONES = 2 ** 128 - 1; +const HALF_SHIFT = 2 ** 64; + +namespace uint384_lib { + // Verifies that the given integer is valid. + func check{range_check_ptr}(a: Uint384) { + [range_check_ptr] = a.d0; + [range_check_ptr + 1] = a.d1; + [range_check_ptr + 2] = a.d2; + let range_check_ptr = range_check_ptr + 3; + return (); + } + + // Adds two integers. Returns the result as a 384-bit integer and the (1-bit) carry. + // Doesn't verify that the result is a proper Uint384, that's now the responsibility of the calling function + func _add_no_uint384_check{range_check_ptr}(a: Uint384, b: Uint384) -> (res: Uint384, carry: felt) { + alloc_locals; + local res: Uint384; + local carry_d0: felt; + local carry_d1: felt; + local carry_d2: felt; + %{ + sum_d0 = ids.a.d0 + ids.b.d0 + ids.carry_d0 = 1 if sum_d0 >= ids.SHIFT else 0 + sum_d1 = ids.a.d1 + ids.b.d1 + ids.carry_d0 + ids.carry_d1 = 1 if sum_d1 >= ids.SHIFT else 0 + sum_d2 = ids.a.d2 + ids.b.d2 + ids.carry_d1 + ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0 + %} + + // Either 0 or 1 + assert carry_d0 * carry_d0 = carry_d0; + assert carry_d1 * carry_d1 = carry_d1; + assert carry_d2 * carry_d2 = carry_d2; + + assert res.d0 = a.d0 + b.d0 - carry_d0 * SHIFT; + assert res.d1 = a.d1 + b.d1 + carry_d0 - carry_d1 * SHIFT; + assert res.d2 = a.d2 + b.d2 + carry_d1 - carry_d2 * SHIFT; + + return (res, carry_d2); + } + + // Splits a field element in the range [0, 2^192) to its low 64-bit and high 128-bit parts. + func split_64{range_check_ptr}(a: felt) -> (low: felt, high: felt) { + alloc_locals; + local low: felt; + local high: felt; + + %{ + ids.low = ids.a & ((1<<64) - 1) + ids.high = ids.a >> 64 + %} + assert a = low + high * HALF_SHIFT; + assert [range_check_ptr + 0] = low; + assert [range_check_ptr + 1] = HALF_SHIFT - 1 - low; + assert [range_check_ptr + 2] = high; + let range_check_ptr = range_check_ptr + 3; + return (low, high); + } + + // Splits a field element in the range [0, 2^224) to its low 128-bit and high 96-bit parts. + func split_128{range_check_ptr}(a: felt) -> (low: felt, high: felt) { + alloc_locals; + const UPPER_BOUND = 2 ** 224; + const HIGH_BOUND = UPPER_BOUND / SHIFT; + local low: felt; + local high: felt; + + %{ + ids.low = ids.a & ((1<<128) - 1) + ids.high = ids.a >> 128 + %} + assert a = low + high * SHIFT; + assert [range_check_ptr + 0] = high; + assert [range_check_ptr + 1] = HIGH_BOUND - 1 - high; + assert [range_check_ptr + 2] = low; + let range_check_ptr = range_check_ptr + 3; + return (low, high); + } + + // Multiplies two integers. Returns the result as two 384-bit integers: the result has 2*384 bits, + // the returned integers represent the lower 384-bits and the higher 384-bits, respectively. + func mul{range_check_ptr}(a: Uint384, b: Uint384) -> (low: Uint384, high: Uint384) { + let (a0, a1) = split_64(a.d0); + let (a2, a3) = split_64(a.d1); + let (a4, a5) = split_64(a.d2); + let (b0, b1) = split_64(b.d0); + let (b2, b3) = split_64(b.d1); + let (b4, b5) = split_64(b.d2); + + let (res0, carry) = split_64(a0 * b0); + let (res1, carry) = split_64(a1 * b0 + a0 * b1 + carry); + let (res2, carry) = split_64(a2 * b0 + a1 * b1 + a0 * b2 + carry); + let (res3, carry) = split_64(a3 * b0 + a2 * b1 + a1 * b2 + a0 * b3 + carry); + let (res4, carry) = split_64(a4 * b0 + a3 * b1 + a2 * b2 + a1 * b3 + a0 * b4 + carry); + let (res5, carry) = split_64( + a5 * b0 + a4 * b1 + a3 * b2 + a2 * b3 + a1 * b4 + a0 * b5 + carry + ); + let (res6, carry) = split_64(a5 * b1 + a4 * b2 + a3 * b3 + a2 * b4 + a1 * b5 + carry); + let (res7, carry) = split_64(a5 * b2 + a4 * b3 + a3 * b4 + a2 * b5 + carry); + let (res8, carry) = split_64(a5 * b3 + a4 * b4 + a3 * b5 + carry); + let (res9, carry) = split_64(a5 * b4 + a4 * b5 + carry); + let (res10, carry) = split_64(a5 * b5 + carry); + + return ( + low=Uint384(d0=res0 + HALF_SHIFT * res1, d1=res2 + HALF_SHIFT * res3, d2=res4 + HALF_SHIFT * res5), + high=Uint384(d0=res6 + HALF_SHIFT * res7, d1=res8 + HALF_SHIFT * res9, d2=res10 + HALF_SHIFT * carry), + ); + } + + func mul_d{range_check_ptr}(a: Uint384, b: Uint384) -> (low: Uint384, high: Uint384) { + alloc_locals; + let (a0, a1) = split_64(a.d0); + let (a2, a3) = split_64(a.d1); + let (a4, a5) = split_64(a.d2); + let (b0, b1) = split_64(b.d0); + let (b2, b3) = split_64(b.d1); + let (b4, b5) = split_64(b.d2); + + local B0 = b0*HALF_SHIFT; + local b12 = b1 + b2*HALF_SHIFT; + local b34 = b3 + b4*HALF_SHIFT; + + let (res0, carry) = split_128(a1 * B0 + a0 * b.d0); + let (res2, carry) = split_128( + a3 * B0 + a2 * b.d0 + a1 * b12 + a0 * b.d1 + carry, + ); + let (res4, carry) = split_128( + a5 * B0 + a4 * b.d0 + a3 * b12 + a2 * b.d1 + a1 * b34 + a0 * b.d2 + carry, + ); + let (res6, carry) = split_128( + a5 * b12 + a4 * b.d1 + a3 * b34 + a2 * b.d2 + a1 * b5 + carry, + ); + let (res8, carry) = split_128( + a5 * b34 + a4 * b.d2 + a3 * b5 + carry + ); + // let (res10, carry) = split_64(a5 * b5 + carry) + + return ( + low=Uint384(d0=res0, d1=res2, d2=res4), + high=Uint384(d0=res6, d1=res8, d2=a5 * b5 + carry), + ); + } + + func lt{range_check_ptr}(a: Uint384, b: Uint384) -> (res: felt) { + if (a.d2 == b.d2) { + if (a.d1 == b.d1) { + return (is_le(a.d0 + 1, b.d0),); + } + return (is_le(a.d1 + 1, b.d1),); + } + return (is_le(a.d2 + 1, b.d2),); + } + + // Unsigned integer division between two integers. Returns the quotient and the remainder. + // Conforms to EVM specifications: division by 0 yields 0. + func unsigned_div_rem{range_check_ptr}(a: Uint384, div: Uint384) -> ( + quotient: Uint384, remainder: Uint384 + ) { + alloc_locals; + local quotient: Uint384; + local remainder: Uint384; + + // If div == 0, return (0, 0, 0). + if (div.d0 + div.d1 + div.d2 == 0) { + return (quotient=Uint384(0, 0, 0), remainder=Uint384(0, 0, 0)); + } + + %{ + def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a, num_bits_shift = 128) + div = pack(ids.div, num_bits_shift = 128) + quotient, remainder = divmod(a, div) + + quotient_split = split(quotient, num_bits_shift=128, length=3) + assert len(quotient_split) == 3 + + ids.quotient.d0 = quotient_split[0] + ids.quotient.d1 = quotient_split[1] + ids.quotient.d2 = quotient_split[2] + + remainder_split = split(remainder, num_bits_shift=128, length=3) + ids.remainder.d0 = remainder_split[0] + ids.remainder.d1 = remainder_split[1] + ids.remainder.d2 = remainder_split[2] + %} + check(quotient); + check(remainder); + let (res_mul: Uint384, carry: Uint384) = mul_d(quotient, div); + assert carry = Uint384(0, 0, 0); + + let (check_val: Uint384, add_carry: felt) = _add_no_uint384_check(res_mul, remainder); + assert check_val = a; + assert add_carry = 0; + + let (is_valid) = lt(remainder, div); + assert is_valid = 1; + return (quotient=quotient, remainder=remainder); + } +} + +func main{range_check_ptr: felt}(){ + let a = Uint384(83434123481193248,82349321849739284, 839243219401320423); + let b = Uint384(9283430921839492319493, 313248123482483248, 3790328402913840); + let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem(a, b); + assert quotient.d0 = 221; + assert quotient.d1 = 0; + assert quotient.d2 = 0; + + assert remainder.d0 = 340282366920936411825224315027446796751; + assert remainder.d1 = 340282366920938463394229121463989152931; + assert remainder.d2 = 1580642357361782; + return (); +} diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index 519c35b312..ad72aec8b2 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -71,6 +71,7 @@ use felt::Felt252; use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_next_instruction; use super::ec_utils::{chained_ec_op_random_ec_point_hint, random_ec_point_hint, recover_y_hint}; +use super::uint384::uint384_unsigned_div_rem; pub struct HintProcessorData { pub code: String, @@ -449,6 +450,9 @@ impl HintProcessor for BuiltinHintProcessor { chained_ec_op_random_ec_point_hint(vm, &hint_data.ids_data, &hint_data.ap_tracking) } hint_code::RECOVER_Y => recover_y_hint(vm, &hint_data.ids_data, &hint_data.ap_tracking), + hint_code::UINT384_UNSIGNED_DIV_REM => { + uint384_unsigned_div_rem(vm, &hint_data.ids_data, &hint_data.ap_tracking) + } #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), code => Err(HintError::UnknownHint(code.to_string())), diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index d0231194d7..afa1fba820 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -609,33 +609,7 @@ ids.p.x = ids.x # This raises an exception if `x` is not on the curve. ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME)"; // The following hints support the lib https://github.com/NethermindEth/research-basic-Cairo-operations-big-integers/blob/main/lib/uint384.cairo -pub(crate) const UINT348_UNSIGNED_DIV_REM: &str = - "def split(num: int, num_bits_shift: int, length: int): -a = [] -for _ in range(length): - a.append( num & ((1 << num_bits_shift) - 1) ) - num = num >> num_bits_shift -return tuple(a) - -def pack(z, num_bits_shift: int) -> int: -limbs = (z.d0, z.d1, z.d2) -return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) - -a = pack(ids.a, num_bits_shift = 128) -div = pack(ids.div, num_bits_shift = 128) -quotient, remainder = divmod(a, div) - -quotient_split = split(quotient, num_bits_shift=128, length=3) -assert len(quotient_split) == 3 - -ids.quotient.d0 = quotient_split[0] -ids.quotient.d1 = quotient_split[1] -ids.quotient.d2 = quotient_split[2] - -remainder_split = split(remainder, num_bits_shift=128, length=3) -ids.remainder.d0 = remainder_split[0] -ids.remainder.d1 = remainder_split[1] -ids.remainder.d2 = remainder_split[2]"; +pub(crate) const UINT384_UNSIGNED_DIV_REM: &str ="def split(num: int, num_bits_shift: int, length: int):\n a = []\n for _ in range(length):\n a.append( num & ((1 << num_bits_shift) - 1) )\n num = num >> num_bits_shift\n return tuple(a)\n\ndef pack(z, num_bits_shift: int) -> int:\n limbs = (z.d0, z.d1, z.d2)\n return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))\n\na = pack(ids.a, num_bits_shift = 128)\ndiv = pack(ids.div, num_bits_shift = 128)\nquotient, remainder = divmod(a, div)\n\nquotient_split = split(quotient, num_bits_shift=128, length=3)\nassert len(quotient_split) == 3\n\nids.quotient.d0 = quotient_split[0]\nids.quotient.d1 = quotient_split[1]\nids.quotient.d2 = quotient_split[2]\n\nremainder_split = split(remainder, num_bits_shift=128, length=3)\nids.remainder.d0 = remainder_split[0]\nids.remainder.d1 = remainder_split[1]\nids.remainder.d2 = remainder_split[2]"; #[cfg(feature = "skip_next_instruction_hint")] pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; diff --git a/src/hint_processor/builtin_hint_processor/mod.rs b/src/hint_processor/builtin_hint_processor/mod.rs index 14abe764e5..19348b601c 100644 --- a/src/hint_processor/builtin_hint_processor/mod.rs +++ b/src/hint_processor/builtin_hint_processor/mod.rs @@ -24,5 +24,5 @@ pub mod signature; pub mod skip_next_instruction; pub mod squash_dict_utils; pub mod uint256_utils; -pub mod uint348; +pub mod uint384; pub mod usort; diff --git a/src/hint_processor/builtin_hint_processor/uint348.rs b/src/hint_processor/builtin_hint_processor/uint384.rs similarity index 94% rename from src/hint_processor/builtin_hint_processor/uint348.rs rename to src/hint_processor/builtin_hint_processor/uint384.rs index e16bd57bab..5fe41e1efd 100644 --- a/src/hint_processor/builtin_hint_processor/uint348.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -13,7 +13,7 @@ use crate::{ use super::hint_utils::get_relocatable_from_var_name; use super::secp::bigint_utils::BigInt3; -// Notes: Hints in this lib use the type Uint348, which is equal to common lib's BigInt3 +// Notes: Hints in this lib use the type Uint384, which is equal to common lib's BigInt3 fn split(num: &BigUint, num_bits_shift: u32) -> [BigUint; T] { let mut num = num.clone(); @@ -63,7 +63,7 @@ fn pack(num: BigInt3, num_bits_shift: usize) -> BigUint { ids.remainder.d2 = remainder_split[2] %} */ -pub fn uint348_unsigned_div_rem( +pub fn uint384_unsigned_div_rem( vm: &mut VirtualMachine, ids_data: &HashMap, ap_tracking: &ApTracking, @@ -74,7 +74,7 @@ pub fn uint348_unsigned_div_rem( 128, ); let quotient_addr = get_relocatable_from_var_name("quotient", vm, ids_data, ap_tracking)?; - let remainder_addr = get_relocatable_from_var_name("quotient", vm, ids_data, ap_tracking)?; + let remainder_addr = get_relocatable_from_var_name("remainder", vm, ids_data, ap_tracking)?; let (quotient, remainder) = a.div_mod_floor(&div); let quotient_split = split::<3>("ient, 128); for (i, quotient_split) in quotient_split.iter().enumerate() { From 7b2f5d9d3b915f68cbb7d88b0ca1757df9b0604d Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 12:15:33 -0300 Subject: [PATCH 07/30] Add integration test --- src/tests/cairo_run_test.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index 96a4c70b69..c5ffa826cb 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -1266,3 +1266,10 @@ fn cairo_run_recover_y() { let program_data = include_bytes!("../../cairo_programs/recover_y.json"); run_program_simple(program_data.as_slice()); } + +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] +fn cairo_run_uibt384() { + let program_data = include_bytes!("../../cairo_programs/uint384.json"); + run_program_simple(program_data.as_slice()); +} From b1a3fbb0e1a0520af77c725a7a2cb12fa56a3018 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 12:28:57 -0300 Subject: [PATCH 08/30] Add unit tests --- cairo_programs/uint384.cairo | 4 +- .../builtin_hint_processor/uint384.rs | 124 ++++++++++++++++++ 2 files changed, 126 insertions(+), 2 deletions(-) diff --git a/cairo_programs/uint384.cairo b/cairo_programs/uint384.cairo index e6b443893d..5b68339bb7 100644 --- a/cairo_programs/uint384.cairo +++ b/cairo_programs/uint384.cairo @@ -237,8 +237,8 @@ namespace uint384_lib { func main{range_check_ptr: felt}(){ let a = Uint384(83434123481193248,82349321849739284, 839243219401320423); - let b = Uint384(9283430921839492319493, 313248123482483248, 3790328402913840); - let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem(a, b); + let div = Uint384(9283430921839492319493, 313248123482483248, 3790328402913840); + let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem(a, div); assert quotient.d0 = 221; assert quotient.d1 = 0; assert quotient.d2 = 0; diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 5fe41e1efd..eef1f84e11 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -86,3 +86,127 @@ pub fn uint384_unsigned_div_rem( } Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use crate::hint_processor::builtin_hint_processor::hint_code; + use crate::vm::vm_memory::memory_segments::MemorySegmentManager; + use crate::{ + any_box, + hint_processor::{ + builtin_hint_processor::builtin_hint_processor_definition::{ + BuiltinHintProcessor, HintProcessorData, + }, + hint_processor_definition::HintProcessor, + }, + types::{ + exec_scope::ExecutionScopes, + relocatable::{MaybeRelocatable, Relocatable}, + }, + utils::test_utils::*, + vm::{ + errors::memory_errors::MemoryError, runners::builtin_runner::RangeCheckBuiltinRunner, + vm_core::VirtualMachine, vm_memory::memory::Memory, + }, + }; + use assert_matches::assert_matches; + use felt::felt_str; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::*; + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_unsigned_div_rem_ok() { + let mut vm = vm_with_range_check!(); + //Initialize fp + vm.run_context.fp = 10; + //Create hint_data + let ids_data = + non_continuous_ids_data![("a", -9), ("div", -6), ("quotient", -3), ("remainder", 0)]; + //Insert ids into memory + vm.segments = segments![ + //a + ((1, 1), 83434123481193248), + ((1, 2), 82349321849739284), + ((1, 3), 839243219401320423), + //div + ((1, 4), 9283430921839492319493), + ((1, 5), 313248123482483248), + ((1, 6), 3790328402913840), + //quotient + ((1, 7), 2) + ]; + //Execute the hint + assert_matches!( + run_hint!(vm, ids_data, hint_code::UINT384_UNSIGNED_DIV_REM), + Ok(()) + ); + //Check hint memory inserts + check_memory![ + vm.segments.memory, + // quotient + ((1, 7), 221), + ((1, 8), 0), + ((1, 9), 0), + // remainder + //((1, 10), 340282366920936411825224315027446796751), + //((1, 11), 340282366920938463394229121463989152931), + ((1, 12), 1580642357361782) + ]; + assert_eq!( + vm.segments + .memory + .get_integer((1, 10).into()) + .unwrap() + .as_ref(), + &felt_str!("340282366920936411825224315027446796751") + ); + assert_eq!( + vm.segments + .memory + .get_integer((1, 11).into()) + .unwrap() + .as_ref(), + &felt_str!("340282366920938463394229121463989152931") + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_unsigned_div_rem_invalid_memory_insert() { + let mut vm = vm_with_range_check!(); + //Initialize fp + vm.run_context.fp = 10; + //Create hint_data + let ids_data = + non_continuous_ids_data![("a", -9), ("div", -6), ("quotient", -3), ("remainder", 0)]; + //Insert ids into memory + vm.segments = segments![ + //a + ((1, 1), 83434123481193248), + ((1, 2), 82349321849739284), + ((1, 3), 839243219401320423), + //div + ((1, 4), 9283430921839492319493), + ((1, 5), 313248123482483248), + ((1, 6), 3790328402913840), + //quotient + ((1, 7), 2) + ]; + //Execute the hint + assert_matches!( + run_hint!(vm, ids_data, hint_code::UINT384_UNSIGNED_DIV_REM), + Err(HintError::Memory( + MemoryError::InconsistentMemory( + x, + y, + z, + ) + )) if x == Relocatable::from((1, 7)) && + y == MaybeRelocatable::from(Felt252::new(2)) && + z == MaybeRelocatable::from(Felt252::new(221)) + ); + } +} From 110605ad099d8712f83290ca9c4cd0c7398ba9af Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 12:47:11 -0300 Subject: [PATCH 09/30] Add hint on split_128 --- .../builtin_hint_processor_definition.rs | 5 +++- .../builtin_hint_processor/hint_code.rs | 2 ++ .../builtin_hint_processor/uint384.rs | 30 ++++++++++++++++--- src/tests/cairo_run_test.rs | 2 +- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index ad72aec8b2..82c1fd4a3e 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -71,7 +71,7 @@ use felt::Felt252; use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_next_instruction; use super::ec_utils::{chained_ec_op_random_ec_point_hint, random_ec_point_hint, recover_y_hint}; -use super::uint384::uint384_unsigned_div_rem; +use super::uint384::{uint384_split_128, uint384_unsigned_div_rem}; pub struct HintProcessorData { pub code: String, @@ -453,6 +453,9 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::UINT384_UNSIGNED_DIV_REM => { uint384_unsigned_div_rem(vm, &hint_data.ids_data, &hint_data.ap_tracking) } + hint_code::UINT384_SPLIT_128 => { + uint384_split_128(vm, &hint_data.ids_data, &hint_data.ap_tracking) + } #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), code => Err(HintError::UnknownHint(code.to_string())), diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index afa1fba820..cc616d1fdc 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -610,6 +610,8 @@ ids.p.x = ids.x ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME)"; // The following hints support the lib https://github.com/NethermindEth/research-basic-Cairo-operations-big-integers/blob/main/lib/uint384.cairo pub(crate) const UINT384_UNSIGNED_DIV_REM: &str ="def split(num: int, num_bits_shift: int, length: int):\n a = []\n for _ in range(length):\n a.append( num & ((1 << num_bits_shift) - 1) )\n num = num >> num_bits_shift\n return tuple(a)\n\ndef pack(z, num_bits_shift: int) -> int:\n limbs = (z.d0, z.d1, z.d2)\n return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))\n\na = pack(ids.a, num_bits_shift = 128)\ndiv = pack(ids.div, num_bits_shift = 128)\nquotient, remainder = divmod(a, div)\n\nquotient_split = split(quotient, num_bits_shift=128, length=3)\nassert len(quotient_split) == 3\n\nids.quotient.d0 = quotient_split[0]\nids.quotient.d1 = quotient_split[1]\nids.quotient.d2 = quotient_split[2]\n\nremainder_split = split(remainder, num_bits_shift=128, length=3)\nids.remainder.d0 = remainder_split[0]\nids.remainder.d1 = remainder_split[1]\nids.remainder.d2 = remainder_split[2]"; +pub(crate) const UINT384_SPLIT_128: &str = "ids.low = ids.a & ((1<<128) - 1) +ids.high = ids.a >> 128"; #[cfg(feature = "skip_next_instruction_hint")] pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index eef1f84e11..12de7af35e 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -11,7 +11,9 @@ use crate::{ vm::{errors::hint_errors::HintError, vm_core::VirtualMachine}, }; -use super::hint_utils::get_relocatable_from_var_name; +use super::hint_utils::{ + get_integer_from_var_name, get_relocatable_from_var_name, insert_value_from_var_name, +}; use super::secp::bigint_utils::BigInt3; // Notes: Hints in this lib use the type Uint384, which is equal to common lib's BigInt3 @@ -87,6 +89,28 @@ pub fn uint384_unsigned_div_rem( Ok(()) } +/* Implements Hint: + %{ + ids.low = ids.a & ((1<<128) - 1) + ids.high = ids.a >> 128 + %} +*/ +pub fn uint384_split_128( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let a = get_integer_from_var_name("a", vm, ids_data, ap_tracking)?.into_owned(); + insert_value_from_var_name( + "low", + &a & &Felt252::from(u128::MAX), + vm, + ids_data, + ap_tracking, + )?; + insert_value_from_var_name("high", a >> 128_u32, vm, ids_data, ap_tracking) +} + #[cfg(test)] mod tests { use super::*; @@ -134,9 +158,7 @@ mod tests { //div ((1, 4), 9283430921839492319493), ((1, 5), 313248123482483248), - ((1, 6), 3790328402913840), - //quotient - ((1, 7), 2) + ((1, 6), 3790328402913840) ]; //Execute the hint assert_matches!( diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index c5ffa826cb..53600326b9 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -1269,7 +1269,7 @@ fn cairo_run_recover_y() { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] -fn cairo_run_uibt384() { +fn cairo_run_uint384() { let program_data = include_bytes!("../../cairo_programs/uint384.json"); run_program_simple(program_data.as_slice()); } From 8cc3ce7ec856f1d1aee6c5eb8a8340d079e2060d Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 14:44:57 -0300 Subject: [PATCH 10/30] Test split_128 hint --- cairo_programs/uint384.cairo | 18 +++- .../builtin_hint_processor/uint384.rs | 91 +++++++++++++++++++ 2 files changed, 106 insertions(+), 3 deletions(-) diff --git a/cairo_programs/uint384.cairo b/cairo_programs/uint384.cairo index 5b68339bb7..6f37bfd89a 100644 --- a/cairo_programs/uint384.cairo +++ b/cairo_programs/uint384.cairo @@ -235,10 +235,11 @@ namespace uint384_lib { } } -func main{range_check_ptr: felt}(){ +func test_uint384_operations{range_check_ptr}() { + // Test unsigned_div_rem let a = Uint384(83434123481193248,82349321849739284, 839243219401320423); let div = Uint384(9283430921839492319493, 313248123482483248, 3790328402913840); - let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem(a, div); + let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem{range_check_ptr=range_check_ptr}(a, div); assert quotient.d0 = 221; assert quotient.d1 = 0; assert quotient.d2 = 0; @@ -246,5 +247,16 @@ func main{range_check_ptr: felt}(){ assert remainder.d0 = 340282366920936411825224315027446796751; assert remainder.d1 = 340282366920938463394229121463989152931; assert remainder.d2 = 1580642357361782; - return (); + + // Test split_128 + let b = 6805647338418769269267492148635364229100; + let (low, high) = uint384_lib.split_128{range_check_ptr=range_check_ptr}(b); + assert high = 19; + assert low = 340282366920938463463374607431768211436; + return(); +} + +func main{range_check_ptr: felt}(){ + test_uint384_operations(); + return(); } diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 12de7af35e..9de639721c 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -231,4 +231,95 @@ mod tests { z == MaybeRelocatable::from(Felt252::new(221)) ); } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_split_128_ok() { + let mut vm = vm_with_range_check!(); + //Initialize fp + vm.run_context.fp = 3; + //Create hint_data + let ids_data = ids_data!["a", "low", "high"]; + //Insert ids into memory + vm.segments = segments![((1, 0), 34895349583295832495320945304)]; + //Execute the hint + assert_matches!( + run_hint!(vm, ids_data, hint_code::UINT384_SPLIT_128), + Ok(()) + ); + //Check hint memory inserts + check_memory![ + vm.segments.memory, + // low + ((1, 1), 34895349583295832495320945304), + // high + ((1, 2), 0) + ]; + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_split_128_ok_big_number() { + let mut vm = vm_with_range_check!(); + //Initialize fp + vm.run_context.fp = 3; + //Create hint_data + let ids_data = ids_data!["a", "low", "high"]; + //Insert ids into memory + vm.segments.add(); + vm.segments.add(); + vm.segments + .memory + .insert( + (1, 0).into(), + Felt252::from(u128::MAX) * Felt252::from(20_u32), + ) + .unwrap(); + //Execute the hint + assert_matches!( + run_hint!(vm, ids_data, hint_code::UINT384_SPLIT_128), + Ok(()) + ); + //Check hint memory inserts + check_memory![ + vm.segments.memory, + // low + //((1, 1), 340282366920938463463374607431768211454) + // high + ((1, 2), 19) + ]; + assert_eq!( + vm.segments + .memory + .get_integer((1, 1).into()) + .unwrap() + .as_ref(), + &felt_str!("340282366920938463463374607431768211436") + ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_split_128_invalid_memory_insert() { + let mut vm = vm_with_range_check!(); + //Initialize fp + vm.run_context.fp = 3; + //Create hint_data + let ids_data = ids_data!["a", "low", "high"]; + //Insert ids into memory + vm.segments = segments![((1, 0), 34895349583295832495320945304), ((1, 1), 2)]; + //Execute the hint + assert_matches!( + run_hint!(vm, ids_data, hint_code::UINT384_SPLIT_128), + Err(HintError::Memory( + MemoryError::InconsistentMemory( + x, + y, + z, + ) + )) if x == Relocatable::from((1, 1)) && + y == MaybeRelocatable::from(Felt252::new(2)) && + z == MaybeRelocatable::from(Felt252::new(34895349583295832495320945304_i128)) + ); + } } From 378788df88223608a98e50046608ec2754fc01b8 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 15:08:21 -0300 Subject: [PATCH 11/30] Add add_no_uint384_hint --- .../builtin_hint_processor_definition.rs | 5 ++- .../builtin_hint_processor/hint_code.rs | 6 ++++ .../builtin_hint_processor/uint384.rs | 35 +++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index 82c1fd4a3e..c3feb5c265 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -71,7 +71,7 @@ use felt::Felt252; use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_next_instruction; use super::ec_utils::{chained_ec_op_random_ec_point_hint, random_ec_point_hint, recover_y_hint}; -use super::uint384::{uint384_split_128, uint384_unsigned_div_rem}; +use super::uint384::{add_no_uint384_check, uint384_split_128, uint384_unsigned_div_rem}; pub struct HintProcessorData { pub code: String, @@ -456,6 +456,9 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::UINT384_SPLIT_128 => { uint384_split_128(vm, &hint_data.ids_data, &hint_data.ap_tracking) } + hint_code::ADD_NO_UINT384_CHECK => { + add_no_uint384_check(vm, &hint_data.ids_data, &hint_data.ap_tracking, constants) + } #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), code => Err(HintError::UnknownHint(code.to_string())), diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index cc616d1fdc..5156054b55 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -612,6 +612,12 @@ ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME)"; pub(crate) const UINT384_UNSIGNED_DIV_REM: &str ="def split(num: int, num_bits_shift: int, length: int):\n a = []\n for _ in range(length):\n a.append( num & ((1 << num_bits_shift) - 1) )\n num = num >> num_bits_shift\n return tuple(a)\n\ndef pack(z, num_bits_shift: int) -> int:\n limbs = (z.d0, z.d1, z.d2)\n return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))\n\na = pack(ids.a, num_bits_shift = 128)\ndiv = pack(ids.div, num_bits_shift = 128)\nquotient, remainder = divmod(a, div)\n\nquotient_split = split(quotient, num_bits_shift=128, length=3)\nassert len(quotient_split) == 3\n\nids.quotient.d0 = quotient_split[0]\nids.quotient.d1 = quotient_split[1]\nids.quotient.d2 = quotient_split[2]\n\nremainder_split = split(remainder, num_bits_shift=128, length=3)\nids.remainder.d0 = remainder_split[0]\nids.remainder.d1 = remainder_split[1]\nids.remainder.d2 = remainder_split[2]"; pub(crate) const UINT384_SPLIT_128: &str = "ids.low = ids.a & ((1<<128) - 1) ids.high = ids.a >> 128"; +pub(crate) const ADD_NO_UINT384_CHECK: &str = "sum_d0 = ids.a.d0 + ids.b.d0 +ids.carry_d0 = 1 if sum_d0 >= ids.SHIFT else 0 +sum_d1 = ids.a.d1 + ids.b.d1 + ids.carry_d0 +ids.carry_d1 = 1 if sum_d1 >= ids.SHIFT else 0 +sum_d2 = ids.a.d2 + ids.b.d2 + ids.carry_d1 +ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0"; #[cfg(feature = "skip_next_instruction_hint")] pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 9de639721c..b0c2e49ff1 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -111,6 +111,41 @@ pub fn uint384_split_128( insert_value_from_var_name("high", a >> 128_u32, vm, ids_data, ap_tracking) } +/* Implements Hint: +%{ + sum_d0 = ids.a.d0 + ids.b.d0 + ids.carry_d0 = 1 if sum_d0 >= ids.SHIFT else 0 + sum_d1 = ids.a.d1 + ids.b.d1 + ids.carry_d0 + ids.carry_d1 = 1 if sum_d1 >= ids.SHIFT else 0 + sum_d2 = ids.a.d2 + ids.b.d2 + ids.carry_d1 + ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0 +%} + */ +pub fn add_no_uint384_check( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, + constants: &HashMap, +) -> Result<(), HintError> { + let a = BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?; + let b = BigInt3::from_var_name("b", vm, ids_data, ap_tracking)?; + // This hint is not from the cairo commonlib, and its lib can be found under different paths, so we cant rely on a full path name + let (_, shift) = constants + .iter() + .find(|(k, _)| k.rsplit('.').next() == Some("SHIFT")) + .ok_or(HintError::MissingConstant("SHIFT"))?; + + let sum_d0 = a.d0.as_ref() + b.d0.as_ref(); + let carry_d0 = Felt252::from((&sum_d0 >= shift) as usize); + let sum_d1 = a.d1.as_ref() + b.d1.as_ref(); + let carry_d1 = Felt252::from((&sum_d1 >= shift) as usize); + let sum_d2 = a.d2.as_ref() + b.d2.as_ref(); + let carry_d2 = Felt252::from((&sum_d2 >= shift) as usize); + + insert_value_from_var_name("carry_d0", carry_d0, vm, ids_data, ap_tracking)?; + insert_value_from_var_name("carry_d1", carry_d1, vm, ids_data, ap_tracking)?; + insert_value_from_var_name("carry_d2", carry_d2, vm, ids_data, ap_tracking) +} #[cfg(test)] mod tests { use super::*; From 271a38c233ed0cb90a3c8ba2cccbd9d8dbb6a7f9 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 15:48:23 -0300 Subject: [PATCH 12/30] Fix hint + add tests --- cairo_programs/uint384.cairo | 12 ++ .../builtin_hint_processor/uint384.rs | 108 ++++++++++++++++-- 2 files changed, 113 insertions(+), 7 deletions(-) diff --git a/cairo_programs/uint384.cairo b/cairo_programs/uint384.cairo index 6f37bfd89a..96aabbecb1 100644 --- a/cairo_programs/uint384.cairo +++ b/cairo_programs/uint384.cairo @@ -253,6 +253,18 @@ func test_uint384_operations{range_check_ptr}() { let (low, high) = uint384_lib.split_128{range_check_ptr=range_check_ptr}(b); assert high = 19; assert low = 340282366920938463463374607431768211436; + + // Test _add_no_uint384_test + + let c = Uint384(3789423292314891293, 21894, 340282366920938463463374607431768211455); + let d = Uint384(32838232, 17, 8); + let (sum_res, carry) = uint384_lib._add_no_uint384_check(c, d); + + assert sum_res.d0 = 3789423292347729525; + assert sum_res.d1 = 21911; + assert sum_res.d2 = 7; + assert carry = 1; + return(); } diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index b0c2e49ff1..f9c8f1ce0e 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -130,17 +130,18 @@ pub fn add_no_uint384_check( let a = BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?; let b = BigInt3::from_var_name("b", vm, ids_data, ap_tracking)?; // This hint is not from the cairo commonlib, and its lib can be found under different paths, so we cant rely on a full path name - let (_, shift) = constants + let shift = constants .iter() .find(|(k, _)| k.rsplit('.').next() == Some("SHIFT")) + .map(|(_, n)| n.to_biguint()) .ok_or(HintError::MissingConstant("SHIFT"))?; - let sum_d0 = a.d0.as_ref() + b.d0.as_ref(); - let carry_d0 = Felt252::from((&sum_d0 >= shift) as usize); - let sum_d1 = a.d1.as_ref() + b.d1.as_ref(); - let carry_d1 = Felt252::from((&sum_d1 >= shift) as usize); - let sum_d2 = a.d2.as_ref() + b.d2.as_ref(); - let carry_d2 = Felt252::from((&sum_d2 >= shift) as usize); + let sum_d0 = a.d0.to_biguint() + b.d0.to_biguint(); + let carry_d0 = Felt252::from((sum_d0 >= shift) as usize); + let sum_d1 = a.d1.to_biguint() + b.d1.to_biguint(); + let carry_d1 = Felt252::from((sum_d1 >= shift) as usize); + let sum_d2 = a.d2.to_biguint() + b.d2.to_biguint(); + let carry_d2 = Felt252::from((sum_d2 >= shift) as usize); insert_value_from_var_name("carry_d0", carry_d0, vm, ids_data, ap_tracking)?; insert_value_from_var_name("carry_d1", carry_d1, vm, ids_data, ap_tracking)?; @@ -357,4 +358,97 @@ mod tests { z == MaybeRelocatable::from(Felt252::new(34895349583295832495320945304_i128)) ); } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_add_no_check_ok() { + let mut vm = vm_with_range_check!(); + //Initialize fp + vm.run_context.fp = 10; + //Create hint_data + let ids_data = non_continuous_ids_data![ + ("a", -10), + ("b", -7), + ("carry_d0", -4), + ("carry_d1", -3), + ("carry_d2", -2) + ]; + //Insert ids into memory + vm.segments = segments![ + // a + ((1, 0), 3789423292314891293), + ((1, 1), 21894), + ((1, 2), 340282366920938463463374607431768211455_u128), + // b + ((1, 3), 32838232), + ((1, 4), 17), + ((1, 5), 8) + ]; + //Execute the hint + assert_matches!( + run_hint!( + vm, + ids_data, + hint_code::ADD_NO_UINT384_CHECK, + &mut exec_scopes_ref!(), + &[("path.path.path.SHIFT", Felt252::one().shl(128_u32))] + .into_iter() + .map(|(k, v)| (k.to_string(), v)) + .collect() + ), + Ok(()) + ); + //Check hint memory inserts + check_memory![ + vm.segments.memory, + // carry_d0 + ((1, 6), 0), + // carry_d1 + ((1, 7), 0), + // carry_d2 + ((1, 8), 1) + ]; + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_add_no_check_missing_constant() { + let mut vm = vm_with_range_check!(); + //Initialize fp + vm.run_context.fp = 10; + //Create hint_data + let ids_data = non_continuous_ids_data![ + ("a", -10), + ("b", -7), + ("carry_d0", -3), + ("carry_d1", -2), + ("carry_d2", -1) + ]; + //Insert ids into memory + vm.segments = segments![ + // a + ((1, 0), 3789423292314891293), + ((1, 2), 21894), + ((1, 3), 340282366920938463463374607431768211455_u128), + // b + ((1, 4), 32838232), + ((1, 5), 17), + ((1, 6), 8) + ]; + //Execute the hint + assert_matches!( + run_hint!(vm, ids_data, hint_code::ADD_NO_UINT384_CHECK), + Err(HintError::MissingConstant(s)) if s == "SHIFT" + ); + //Check hint memory inserts + check_memory![ + vm.segments.memory, + // carry_d0 + ((1, 7), 0), + // carry_d1 + ((1, 8), 0), + // carry_d2 + ((1, 9), 1) + ]; + } } From ed7de28887dfc247adad0573774a357fe37d8153 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 17:42:53 -0300 Subject: [PATCH 13/30] Add hint code for UINT348_UNSIGNED_DIV_REM_EXPAND --- .../builtin_hint_processor/hint_code.rs | 32 +++++ .../builtin_hint_processor/uint384.rs | 122 +++++++++++++++++- 2 files changed, 153 insertions(+), 1 deletion(-) diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 5156054b55..bdee29bc06 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -619,5 +619,37 @@ ids.carry_d1 = 1 if sum_d1 >= ids.SHIFT else 0 sum_d2 = ids.a.d2 + ids.b.d2 + ids.carry_d1 ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0"; +pub(crate) const UINT384_UNSIGNED_DIV_REM_EXPANDED: &str = + "def split(num: int, num_bits_shift: int, length: int): +a = [] +for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift +return tuple(a) + +def pack(z, num_bits_shift: int) -> int: +limbs = (z.d0, z.d1, z.d2) +return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + +def pack2(z, num_bits_shift: int) -> int: +limbs = (z.b01, z.b23, z.b45) +return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + +a = pack(ids.a, num_bits_shift = 128) +div = pack2(ids.div, num_bits_shift = 128) +quotient, remainder = divmod(a, div) + +quotient_split = split(quotient, num_bits_shift=128, length=3) +assert len(quotient_split) == 3 + +ids.quotient.d0 = quotient_split[0] +ids.quotient.d1 = quotient_split[1] +ids.quotient.d2 = quotient_split[2] + +remainder_split = split(remainder, num_bits_shift=128, length=3) +ids.remainder.d0 = remainder_split[0] +ids.remainder.d1 = remainder_split[1] +ids.remainder.d2 = remainder_split[2]"; + #[cfg(feature = "skip_next_instruction_hint")] pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index f9c8f1ce0e..537a0037e9 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -4,7 +4,8 @@ use num_bigint::BigUint; use num_integer::Integer; use num_traits::One; -use crate::stdlib::{collections::HashMap, prelude::*}; +use crate::stdlib::{borrow::Cow, collections::HashMap, prelude::*}; +use crate::types::relocatable::Relocatable; use crate::{ hint_processor::hint_processor_definition::HintReference, serde::deserialize_program::ApTracking, @@ -16,7 +17,58 @@ use super::hint_utils::{ }; use super::secp::bigint_utils::BigInt3; // Notes: Hints in this lib use the type Uint384, which is equal to common lib's BigInt3 +#[derive(Debug, PartialEq)] +pub(crate) struct Uint384Expand<'a> { + #[allow(non_snake_case)] + pub B0: Cow<'a, Felt252>, + pub b01: Cow<'a, Felt252>, + pub b12: Cow<'a, Felt252>, + pub b23: Cow<'a, Felt252>, + pub b34: Cow<'a, Felt252>, + pub b45: Cow<'a, Felt252>, + pub b5: Cow<'a, Felt252>, +} +impl Uint384Expand<'_> { + pub(crate) fn from_base_addr<'a>( + addr: Relocatable, + name: &str, + vm: &'a VirtualMachine, + ) -> Result, HintError> { + Ok(Uint384Expand { + B0: vm.get_integer(addr).map_err(|_| { + HintError::IdentifierHasNoMember(name.to_string(), "B0".to_string()) + })?, + b01: vm.get_integer((addr + 1)?).map_err(|_| { + HintError::IdentifierHasNoMember(name.to_string(), "b01".to_string()) + })?, + b12: vm.get_integer((addr + 2)?).map_err(|_| { + HintError::IdentifierHasNoMember(name.to_string(), "b12".to_string()) + })?, + b23: vm.get_integer((addr + 2)?).map_err(|_| { + HintError::IdentifierHasNoMember(name.to_string(), "b23".to_string()) + })?, + b34: vm.get_integer((addr + 2)?).map_err(|_| { + HintError::IdentifierHasNoMember(name.to_string(), "b34".to_string()) + })?, + b45: vm.get_integer((addr + 2)?).map_err(|_| { + HintError::IdentifierHasNoMember(name.to_string(), "b45".to_string()) + })?, + b5: vm.get_integer((addr + 2)?).map_err(|_| { + HintError::IdentifierHasNoMember(name.to_string(), "b5".to_string()) + })?, + }) + } + pub(crate) fn from_var_name<'a>( + name: &str, + vm: &'a VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, + ) -> Result, HintError> { + let base_addr = get_relocatable_from_var_name(name, vm, ids_data, ap_tracking)?; + Uint384Expand::from_base_addr(base_addr, name, vm) + } +} fn split(num: &BigUint, num_bits_shift: u32) -> [BigUint; T] { let mut num = num.clone(); [0; T].map(|_| { @@ -35,6 +87,16 @@ fn pack(num: BigInt3, num_bits_shift: usize) -> BigUint { .map(|(idx, value)| value.to_biguint().shl(idx * num_bits_shift)) .sum() } + +fn pack2(num: Uint384Expand, num_bits_shift: usize) -> BigUint { + let limbs = vec![num.b01, num.b23, num.b45]; + #[allow(deprecated)] + limbs + .into_iter() + .enumerate() + .map(|(idx, value)| value.to_biguint().shl(idx * num_bits_shift)) + .sum() +} /* Implements Hint: %{ def split(num: int, num_bits_shift: int, length: int): @@ -147,6 +209,64 @@ pub fn add_no_uint384_check( insert_value_from_var_name("carry_d1", carry_d1, vm, ids_data, ap_tracking)?; insert_value_from_var_name("carry_d2", carry_d2, vm, ids_data, ap_tracking) } + +/* Implements Hint: + %{ + def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + def pack2(z, num_bits_shift: int) -> int: + limbs = (z.b01, z.b23, z.b45) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a, num_bits_shift = 128) + div = pack2(ids.div, num_bits_shift = 128) + quotient, remainder = divmod(a, div) + + quotient_split = split(quotient, num_bits_shift=128, length=3) + assert len(quotient_split) == 3 + + ids.quotient.d0 = quotient_split[0] + ids.quotient.d1 = quotient_split[1] + ids.quotient.d2 = quotient_split[2] + + remainder_split = split(remainder, num_bits_shift=128, length=3) + ids.remainder.d0 = remainder_split[0] + ids.remainder.d1 = remainder_split[1] + ids.remainder.d2 = remainder_split[2] + %} +*/ +pub fn uint256_unsigned_div_rem_expanded( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let a = pack(BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?, 128); + let div = pack2( + Uint384Expand::from_var_name("div", vm, ids_data, ap_tracking)?, + 128, + ); + let quotient_addr = get_relocatable_from_var_name("quotient", vm, ids_data, ap_tracking)?; + let remainder_addr = get_relocatable_from_var_name("remainder", vm, ids_data, ap_tracking)?; + let (quotient, remainder) = a.div_mod_floor(&div); + let quotient_split = split::<3>("ient, 128); + for (i, quotient_split) in quotient_split.iter().enumerate() { + vm.insert_value((quotient_addr + i)?, Felt252::from(quotient_split))?; + } + let remainder_split = split::<3>(&remainder, 128); + for (i, remainder_split) in remainder_split.iter().enumerate() { + vm.insert_value((remainder_addr + i)?, Felt252::from(remainder_split))?; + } + Ok(()) +} #[cfg(test)] mod tests { use super::*; From be408c3061629c347e2a81db881831e99c8ed5e6 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 17:45:24 -0300 Subject: [PATCH 14/30] Msc fixes --- .../builtin_hint_processor_definition.rs | 8 +++++++- src/hint_processor/builtin_hint_processor/uint384.rs | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index c3feb5c265..f30770627e 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -71,7 +71,10 @@ use felt::Felt252; use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_next_instruction; use super::ec_utils::{chained_ec_op_random_ec_point_hint, random_ec_point_hint, recover_y_hint}; -use super::uint384::{add_no_uint384_check, uint384_split_128, uint384_unsigned_div_rem}; +use super::uint384::{ + add_no_uint384_check, uint384_split_128, uint384_unsigned_div_rem, + uint384_unsigned_div_rem_expanded, +}; pub struct HintProcessorData { pub code: String, @@ -459,6 +462,9 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::ADD_NO_UINT384_CHECK => { add_no_uint384_check(vm, &hint_data.ids_data, &hint_data.ap_tracking, constants) } + hint_code::UINT384_UNSIGNED_DIV_REM_EXPANDED => { + uint384_unsigned_div_rem_expanded(vm, &hint_data.ids_data, &hint_data.ap_tracking) + } #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), code => Err(HintError::UnknownHint(code.to_string())), diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 537a0037e9..19338f1c36 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -18,8 +18,8 @@ use super::hint_utils::{ use super::secp::bigint_utils::BigInt3; // Notes: Hints in this lib use the type Uint384, which is equal to common lib's BigInt3 #[derive(Debug, PartialEq)] +#[allow(non_snake_case)] pub(crate) struct Uint384Expand<'a> { - #[allow(non_snake_case)] pub B0: Cow<'a, Felt252>, pub b01: Cow<'a, Felt252>, pub b12: Cow<'a, Felt252>, @@ -244,7 +244,7 @@ pub fn add_no_uint384_check( ids.remainder.d2 = remainder_split[2] %} */ -pub fn uint256_unsigned_div_rem_expanded( +pub fn uint384_unsigned_div_rem_expanded( vm: &mut VirtualMachine, ids_data: &HashMap, ap_tracking: &ApTracking, From 8c6fb8c80f8d18fabc4bbe905f0c39de445186c4 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 17:53:47 -0300 Subject: [PATCH 15/30] Add integration test --- cairo_programs/uint384.cairo | 103 ++++++++++++++++++ .../builtin_hint_processor/hint_code.rs | 34 +----- .../builtin_hint_processor/uint384.rs | 8 +- 3 files changed, 109 insertions(+), 36 deletions(-) diff --git a/cairo_programs/uint384.cairo b/cairo_programs/uint384.cairo index 96aabbecb1..2cc9d992fa 100644 --- a/cairo_programs/uint384.cairo +++ b/cairo_programs/uint384.cairo @@ -23,6 +23,16 @@ struct Uint384 { d2: felt, } +struct Uint384_expand { + B0: felt, + b01: felt, + b12: felt, + b23: felt, + b34: felt, + b45: felt, + b5: felt, +} + const SHIFT = 2 ** 128; const ALL_ONES = 2 ** 128 - 1; const HALF_SHIFT = 2 ** 64; @@ -133,6 +143,31 @@ namespace uint384_lib { high=Uint384(d0=res6 + HALF_SHIFT * res7, d1=res8 + HALF_SHIFT * res9, d2=res10 + HALF_SHIFT * carry), ); } + func mul_expanded{range_check_ptr}(a: Uint384, b: Uint384_expand) -> (low: Uint384, high: Uint384) { + let (a0, a1) = split_64(a.d0); + let (a2, a3) = split_64(a.d1); + let (a4, a5) = split_64(a.d2); + + let (res0, carry) = split_128(a1 * b.B0 + a0 * b.b01); + let (res2, carry) = split_128( + a3 * b.B0 + a2 * b.b01 + a1 * b.b12 + a0 * b.b23 + carry, + ); + let (res4, carry) = split_128( + a5 * b.B0 + a4 * b.b01 + a3 * b.b12 + a2 * b.b23 + a1 * b.b34 + a0 * b.b45 + carry, + ); + let (res6, carry) = split_128( + a5 * b.b12 + a4 * b.b23 + a3 * b.b34 + a2 * b.b45 + a1 * b.b5 + carry, + ); + let (res8, carry) = split_128( + a5 * b.b34 + a4 * b.b45 + a3 * b.b5 + carry + ); + // let (res10, carry) = split_64(a5 * b.b5 + carry) + + return ( + low=Uint384(d0=res0, d1=res2, d2=res4), + high=Uint384(d0=res6, d1=res8, d2=a5 * b.b5 + carry), + ); + } func mul_d{range_check_ptr}(a: Uint384, b: Uint384) -> (low: Uint384, high: Uint384) { alloc_locals; @@ -233,6 +268,62 @@ namespace uint384_lib { assert is_valid = 1; return (quotient=quotient, remainder=remainder); } + +// Unsigned integer division between two integers. Returns the quotient and the remainder. + func unsigned_div_rem_expanded{range_check_ptr}(a: Uint384, div: Uint384_expand) -> ( + quotient: Uint384, remainder: Uint384 + ) { + alloc_locals; + local quotient: Uint384; + local remainder: Uint384; + + let div2 = Uint384(div.b01,div.b23,div.b45); + + %{ + def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + def pack2(z, num_bits_shift: int) -> int: + limbs = (z.b01, z.b23, z.b45) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a, num_bits_shift = 128) + div = pack2(ids.div, num_bits_shift = 128) + quotient, remainder = divmod(a, div) + + quotient_split = split(quotient, num_bits_shift=128, length=3) + assert len(quotient_split) == 3 + + ids.quotient.d0 = quotient_split[0] + ids.quotient.d1 = quotient_split[1] + ids.quotient.d2 = quotient_split[2] + + remainder_split = split(remainder, num_bits_shift=128, length=3) + ids.remainder.d0 = remainder_split[0] + ids.remainder.d1 = remainder_split[1] + ids.remainder.d2 = remainder_split[2] + %} + check(quotient); + check(remainder); + let (res_mul: Uint384, carry: Uint384) = mul_expanded(quotient, div); + assert carry = Uint384(0, 0, 0); + + let (check_val: Uint384, add_carry: felt) = _add_no_uint384_check(res_mul, remainder); + assert check_val = a; + assert add_carry = 0; + + let (is_valid) = lt(remainder, div2); + assert is_valid = 1; + return (quotient=quotient, remainder=remainder); + } } func test_uint384_operations{range_check_ptr}() { @@ -265,6 +356,18 @@ func test_uint384_operations{range_check_ptr}() { assert sum_res.d2 = 7; assert carry = 1; + // Test unsigned_div_rem_expanded + let e = Uint384(83434123481193248,82349321849739284, 839243219401320423); + let div_expand = Uint384_expand(9283430921839492319493, 313248123482483248, 3790328402913840, 13, 78990, 109, 7); + let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem_expanded{range_check_ptr=range_check_ptr}(a, div_expand); + assert quotient.d0 = 7699479077076334; + assert quotient.d1 = 0; + assert quotient.d2 = 0; + + assert remainder.d0 = 340279955073565776659831804641277151872; + assert remainder.d1 = 340282366920938463463356863525615958397; + assert remainder.d2 = 16; + return(); } diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index bdee29bc06..c1b38f654c 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -608,6 +608,7 @@ from starkware.python.math_utils import recover_y ids.p.x = ids.x # This raises an exception if `x` is not on the curve. ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME)"; + // The following hints support the lib https://github.com/NethermindEth/research-basic-Cairo-operations-big-integers/blob/main/lib/uint384.cairo pub(crate) const UINT384_UNSIGNED_DIV_REM: &str ="def split(num: int, num_bits_shift: int, length: int):\n a = []\n for _ in range(length):\n a.append( num & ((1 << num_bits_shift) - 1) )\n num = num >> num_bits_shift\n return tuple(a)\n\ndef pack(z, num_bits_shift: int) -> int:\n limbs = (z.d0, z.d1, z.d2)\n return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))\n\na = pack(ids.a, num_bits_shift = 128)\ndiv = pack(ids.div, num_bits_shift = 128)\nquotient, remainder = divmod(a, div)\n\nquotient_split = split(quotient, num_bits_shift=128, length=3)\nassert len(quotient_split) == 3\n\nids.quotient.d0 = quotient_split[0]\nids.quotient.d1 = quotient_split[1]\nids.quotient.d2 = quotient_split[2]\n\nremainder_split = split(remainder, num_bits_shift=128, length=3)\nids.remainder.d0 = remainder_split[0]\nids.remainder.d1 = remainder_split[1]\nids.remainder.d2 = remainder_split[2]"; pub(crate) const UINT384_SPLIT_128: &str = "ids.low = ids.a & ((1<<128) - 1) @@ -618,38 +619,7 @@ sum_d1 = ids.a.d1 + ids.b.d1 + ids.carry_d0 ids.carry_d1 = 1 if sum_d1 >= ids.SHIFT else 0 sum_d2 = ids.a.d2 + ids.b.d2 + ids.carry_d1 ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0"; - -pub(crate) const UINT384_UNSIGNED_DIV_REM_EXPANDED: &str = - "def split(num: int, num_bits_shift: int, length: int): -a = [] -for _ in range(length): - a.append( num & ((1 << num_bits_shift) - 1) ) - num = num >> num_bits_shift -return tuple(a) - -def pack(z, num_bits_shift: int) -> int: -limbs = (z.d0, z.d1, z.d2) -return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) - -def pack2(z, num_bits_shift: int) -> int: -limbs = (z.b01, z.b23, z.b45) -return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) - -a = pack(ids.a, num_bits_shift = 128) -div = pack2(ids.div, num_bits_shift = 128) -quotient, remainder = divmod(a, div) - -quotient_split = split(quotient, num_bits_shift=128, length=3) -assert len(quotient_split) == 3 - -ids.quotient.d0 = quotient_split[0] -ids.quotient.d1 = quotient_split[1] -ids.quotient.d2 = quotient_split[2] - -remainder_split = split(remainder, num_bits_shift=128, length=3) -ids.remainder.d0 = remainder_split[0] -ids.remainder.d1 = remainder_split[1] -ids.remainder.d2 = remainder_split[2]"; +pub(crate) const UINT384_UNSIGNED_DIV_REM_EXPANDED: &str = "def split(num: int, num_bits_shift: int, length: int):\n a = []\n for _ in range(length):\n a.append( num & ((1 << num_bits_shift) - 1) )\n num = num >> num_bits_shift\n return tuple(a)\n\ndef pack(z, num_bits_shift: int) -> int:\n limbs = (z.d0, z.d1, z.d2)\n return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))\n\ndef pack2(z, num_bits_shift: int) -> int:\n limbs = (z.b01, z.b23, z.b45)\n return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))\n\na = pack(ids.a, num_bits_shift = 128)\ndiv = pack2(ids.div, num_bits_shift = 128)\nquotient, remainder = divmod(a, div)\n\nquotient_split = split(quotient, num_bits_shift=128, length=3)\nassert len(quotient_split) == 3\n\nids.quotient.d0 = quotient_split[0]\nids.quotient.d1 = quotient_split[1]\nids.quotient.d2 = quotient_split[2]\n\nremainder_split = split(remainder, num_bits_shift=128, length=3)\nids.remainder.d0 = remainder_split[0]\nids.remainder.d1 = remainder_split[1]\nids.remainder.d2 = remainder_split[2]"; #[cfg(feature = "skip_next_instruction_hint")] pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 19338f1c36..872f78555f 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -45,16 +45,16 @@ impl Uint384Expand<'_> { b12: vm.get_integer((addr + 2)?).map_err(|_| { HintError::IdentifierHasNoMember(name.to_string(), "b12".to_string()) })?, - b23: vm.get_integer((addr + 2)?).map_err(|_| { + b23: vm.get_integer((addr + 3)?).map_err(|_| { HintError::IdentifierHasNoMember(name.to_string(), "b23".to_string()) })?, - b34: vm.get_integer((addr + 2)?).map_err(|_| { + b34: vm.get_integer((addr + 4)?).map_err(|_| { HintError::IdentifierHasNoMember(name.to_string(), "b34".to_string()) })?, - b45: vm.get_integer((addr + 2)?).map_err(|_| { + b45: vm.get_integer((addr + 5)?).map_err(|_| { HintError::IdentifierHasNoMember(name.to_string(), "b45".to_string()) })?, - b5: vm.get_integer((addr + 2)?).map_err(|_| { + b5: vm.get_integer((addr + 6)?).map_err(|_| { HintError::IdentifierHasNoMember(name.to_string(), "b5".to_string()) })?, }) From 512a80efee5c29754dfd525e6dfe7858274065e5 Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 18:00:33 -0300 Subject: [PATCH 16/30] Reduce Uint384_expand representation to the 3 used limbs --- .../builtin_hint_processor/uint384.rs | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 872f78555f..36256ee294 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -17,46 +17,35 @@ use super::hint_utils::{ }; use super::secp::bigint_utils::BigInt3; // Notes: Hints in this lib use the type Uint384, which is equal to common lib's BigInt3 + +/* Reduced version of Uint384_expand +The full version has 7 limbs (B0, b01, b12, b23, b34, b45, b5), but only 3 are used by the pack2 fn (b01, b23, b45) +As there are no other uses of Uint384_expand outside of these in the lib, we can use a reduced version with just 3 limbs +*/ #[derive(Debug, PartialEq)] #[allow(non_snake_case)] -pub(crate) struct Uint384Expand<'a> { - pub B0: Cow<'a, Felt252>, +pub(crate) struct Uint384ExpandReduced<'a> { pub b01: Cow<'a, Felt252>, - pub b12: Cow<'a, Felt252>, pub b23: Cow<'a, Felt252>, - pub b34: Cow<'a, Felt252>, pub b45: Cow<'a, Felt252>, - pub b5: Cow<'a, Felt252>, } -impl Uint384Expand<'_> { +impl Uint384ExpandReduced<'_> { pub(crate) fn from_base_addr<'a>( addr: Relocatable, name: &str, vm: &'a VirtualMachine, - ) -> Result, HintError> { - Ok(Uint384Expand { - B0: vm.get_integer(addr).map_err(|_| { - HintError::IdentifierHasNoMember(name.to_string(), "B0".to_string()) - })?, + ) -> Result, HintError> { + Ok(Uint384ExpandReduced { b01: vm.get_integer((addr + 1)?).map_err(|_| { HintError::IdentifierHasNoMember(name.to_string(), "b01".to_string()) })?, - b12: vm.get_integer((addr + 2)?).map_err(|_| { - HintError::IdentifierHasNoMember(name.to_string(), "b12".to_string()) - })?, b23: vm.get_integer((addr + 3)?).map_err(|_| { HintError::IdentifierHasNoMember(name.to_string(), "b23".to_string()) })?, - b34: vm.get_integer((addr + 4)?).map_err(|_| { - HintError::IdentifierHasNoMember(name.to_string(), "b34".to_string()) - })?, b45: vm.get_integer((addr + 5)?).map_err(|_| { HintError::IdentifierHasNoMember(name.to_string(), "b45".to_string()) })?, - b5: vm.get_integer((addr + 6)?).map_err(|_| { - HintError::IdentifierHasNoMember(name.to_string(), "b5".to_string()) - })?, }) } pub(crate) fn from_var_name<'a>( @@ -64,11 +53,12 @@ impl Uint384Expand<'_> { vm: &'a VirtualMachine, ids_data: &HashMap, ap_tracking: &ApTracking, - ) -> Result, HintError> { + ) -> Result, HintError> { let base_addr = get_relocatable_from_var_name(name, vm, ids_data, ap_tracking)?; - Uint384Expand::from_base_addr(base_addr, name, vm) + Uint384ExpandReduced::from_base_addr(base_addr, name, vm) } } + fn split(num: &BigUint, num_bits_shift: u32) -> [BigUint; T] { let mut num = num.clone(); [0; T].map(|_| { @@ -88,7 +78,7 @@ fn pack(num: BigInt3, num_bits_shift: usize) -> BigUint { .sum() } -fn pack2(num: Uint384Expand, num_bits_shift: usize) -> BigUint { +fn pack2(num: Uint384ExpandReduced, num_bits_shift: usize) -> BigUint { let limbs = vec![num.b01, num.b23, num.b45]; #[allow(deprecated)] limbs @@ -251,7 +241,7 @@ pub fn uint384_unsigned_div_rem_expanded( ) -> Result<(), HintError> { let a = pack(BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?, 128); let div = pack2( - Uint384Expand::from_var_name("div", vm, ids_data, ap_tracking)?, + Uint384ExpandReduced::from_var_name("div", vm, ids_data, ap_tracking)?, 128, ); let quotient_addr = get_relocatable_from_var_name("quotient", vm, ids_data, ap_tracking)?; From 089398e57aceafeba353d615bb345f4ca157c94e Mon Sep 17 00:00:00 2001 From: Federica Date: Wed, 12 Apr 2023 18:11:37 -0300 Subject: [PATCH 17/30] Add unit test --- .../builtin_hint_processor/uint384.rs | 71 ++++++++++++++++--- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 36256ee294..90d468b671 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -538,27 +538,76 @@ mod tests { vm.segments = segments![ // a ((1, 0), 3789423292314891293), - ((1, 2), 21894), - ((1, 3), 340282366920938463463374607431768211455_u128), + ((1, 1), 21894), + ((1, 2), 340282366920938463463374607431768211455_u128), // b - ((1, 4), 32838232), - ((1, 5), 17), - ((1, 6), 8) + ((1, 3), 32838232), + ((1, 4), 17), + ((1, 5), 8) ]; //Execute the hint assert_matches!( run_hint!(vm, ids_data, hint_code::ADD_NO_UINT384_CHECK), Err(HintError::MissingConstant(s)) if s == "SHIFT" ); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_unsigned_div_rem_expand_ok() { + let mut vm = vm_with_range_check!(); + //Initialize fp + vm.run_context.fp = 13; + //Create hint_data + let ids_data = + non_continuous_ids_data![("a", -13), ("div", -10), ("quotient", -3), ("remainder", 0)]; + //Insert ids into memory + vm.segments = segments![ + //a + ((1, 0), 83434123481193248), + ((1, 1), 82349321849739284), + ((1, 2), 839243219401320423), + //div + ((1, 3), 9283430921839492319493), + ((1, 4), 313248123482483248), + ((1, 5), 3790328402913840), + ((1, 6), 13), + ((1, 7), 78990), + ((1, 8), 109), + ((1, 9), 7) + ]; + //Execute the hint + assert_matches!( + run_hint!(vm, ids_data, hint_code::UINT384_UNSIGNED_DIV_REM_EXPANDED), + Ok(()) + ); //Check hint memory inserts check_memory![ vm.segments.memory, - // carry_d0 - ((1, 7), 0), - // carry_d1 - ((1, 8), 0), - // carry_d2 - ((1, 9), 1) + // quotient + ((1, 10), 7699479077076334), + ((1, 11), 0), + ((1, 12), 0), + // remainder + //((1, 13), 340279955073565776659831804641277151872), + //((1, 14), 340282366920938463463356863525615958397), + ((1, 15), 16) ]; + assert_eq!( + vm.segments + .memory + .get_integer((1, 13).into()) + .unwrap() + .as_ref(), + &felt_str!("340279955073565776659831804641277151872") + ); + assert_eq!( + vm.segments + .memory + .get_integer((1, 14).into()) + .unwrap() + .as_ref(), + &felt_str!("340282366920938463463356863525615958397") + ); } } From 6852a871d5d245938c36a89d8aa364c5ae7af407 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 13 Apr 2023 10:27:32 -0300 Subject: [PATCH 18/30] Add hint code for UINT384_SQRT --- .../builtin_hint_processor/hint_code.rs | 20 +++ .../builtin_hint_processor/uint384.rs | 145 ++++++++++-------- 2 files changed, 105 insertions(+), 60 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index c1b38f654c..b4dca769c0 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -620,6 +620,26 @@ ids.carry_d1 = 1 if sum_d1 >= ids.SHIFT else 0 sum_d2 = ids.a.d2 + ids.b.d2 + ids.carry_d1 ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0"; pub(crate) const UINT384_UNSIGNED_DIV_REM_EXPANDED: &str = "def split(num: int, num_bits_shift: int, length: int):\n a = []\n for _ in range(length):\n a.append( num & ((1 << num_bits_shift) - 1) )\n num = num >> num_bits_shift\n return tuple(a)\n\ndef pack(z, num_bits_shift: int) -> int:\n limbs = (z.d0, z.d1, z.d2)\n return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))\n\ndef pack2(z, num_bits_shift: int) -> int:\n limbs = (z.b01, z.b23, z.b45)\n return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))\n\na = pack(ids.a, num_bits_shift = 128)\ndiv = pack2(ids.div, num_bits_shift = 128)\nquotient, remainder = divmod(a, div)\n\nquotient_split = split(quotient, num_bits_shift=128, length=3)\nassert len(quotient_split) == 3\n\nids.quotient.d0 = quotient_split[0]\nids.quotient.d1 = quotient_split[1]\nids.quotient.d2 = quotient_split[2]\n\nremainder_split = split(remainder, num_bits_shift=128, length=3)\nids.remainder.d0 = remainder_split[0]\nids.remainder.d1 = remainder_split[1]\nids.remainder.d2 = remainder_split[2]"; +pub(crate) const UINT384_SQRT: &str = "from starkware.python.math_utils import isqrt + +def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + +def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + +a = pack(ids.a, num_bits_shift=128) +root = isqrt(a) +assert 0 <= root < 2 ** 192 +root_split = split(root, num_bits_shift=128, length=3) +ids.root.d0 = root_split[0] +ids.root.d1 = root_split[1] +ids.root.d2 = root_split[2]"; #[cfg(feature = "skip_next_instruction_hint")] pub(crate) const SKIP_NEXT_INSTRUCTION: &str = "skip_next_instruction()"; diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 90d468b671..4796234fb9 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -88,34 +88,34 @@ fn pack2(num: Uint384ExpandReduced, num_bits_shift: usize) -> BigUint { .sum() } /* Implements Hint: - %{ - def split(num: int, num_bits_shift: int, length: int): - a = [] - for _ in range(length): - a.append( num & ((1 << num_bits_shift) - 1) ) - num = num >> num_bits_shift - return tuple(a) - - def pack(z, num_bits_shift: int) -> int: - limbs = (z.d0, z.d1, z.d2) - return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) - - a = pack(ids.a, num_bits_shift = 128) - div = pack(ids.div, num_bits_shift = 128) - quotient, remainder = divmod(a, div) - - quotient_split = split(quotient, num_bits_shift=128, length=3) - assert len(quotient_split) == 3 - - ids.quotient.d0 = quotient_split[0] - ids.quotient.d1 = quotient_split[1] - ids.quotient.d2 = quotient_split[2] - - remainder_split = split(remainder, num_bits_shift=128, length=3) - ids.remainder.d0 = remainder_split[0] - ids.remainder.d1 = remainder_split[1] - ids.remainder.d2 = remainder_split[2] - %} +%{ + def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a, num_bits_shift = 128) + div = pack(ids.div, num_bits_shift = 128) + quotient, remainder = divmod(a, div) + + quotient_split = split(quotient, num_bits_shift=128, length=3) + assert len(quotient_split) == 3 + + ids.quotient.d0 = quotient_split[0] + ids.quotient.d1 = quotient_split[1] + ids.quotient.d2 = quotient_split[2] + + remainder_split = split(remainder, num_bits_shift=128, length=3) + ids.remainder.d0 = remainder_split[0] + ids.remainder.d1 = remainder_split[1] + ids.remainder.d2 = remainder_split[2] +%} */ pub fn uint384_unsigned_div_rem( vm: &mut VirtualMachine, @@ -201,38 +201,38 @@ pub fn add_no_uint384_check( } /* Implements Hint: - %{ - def split(num: int, num_bits_shift: int, length: int): - a = [] - for _ in range(length): - a.append( num & ((1 << num_bits_shift) - 1) ) - num = num >> num_bits_shift - return tuple(a) - - def pack(z, num_bits_shift: int) -> int: - limbs = (z.d0, z.d1, z.d2) - return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) - - def pack2(z, num_bits_shift: int) -> int: - limbs = (z.b01, z.b23, z.b45) - return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) - - a = pack(ids.a, num_bits_shift = 128) - div = pack2(ids.div, num_bits_shift = 128) - quotient, remainder = divmod(a, div) - - quotient_split = split(quotient, num_bits_shift=128, length=3) - assert len(quotient_split) == 3 - - ids.quotient.d0 = quotient_split[0] - ids.quotient.d1 = quotient_split[1] - ids.quotient.d2 = quotient_split[2] - - remainder_split = split(remainder, num_bits_shift=128, length=3) - ids.remainder.d0 = remainder_split[0] - ids.remainder.d1 = remainder_split[1] - ids.remainder.d2 = remainder_split[2] - %} +%{ + def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + def pack2(z, num_bits_shift: int) -> int: + limbs = (z.b01, z.b23, z.b45) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a, num_bits_shift = 128) + div = pack2(ids.div, num_bits_shift = 128) + quotient, remainder = divmod(a, div) + + quotient_split = split(quotient, num_bits_shift=128, length=3) + assert len(quotient_split) == 3 + + ids.quotient.d0 = quotient_split[0] + ids.quotient.d1 = quotient_split[1] + ids.quotient.d2 = quotient_split[2] + + remainder_split = split(remainder, num_bits_shift=128, length=3) + ids.remainder.d0 = remainder_split[0] + ids.remainder.d1 = remainder_split[1] + ids.remainder.d2 = remainder_split[2] +%} */ pub fn uint384_unsigned_div_rem_expanded( vm: &mut VirtualMachine, @@ -257,6 +257,31 @@ pub fn uint384_unsigned_div_rem_expanded( } Ok(()) } + +/* Implements Hint +%{ + from starkware.python.math_utils import isqrt + + def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a, num_bits_shift=128) + root = isqrt(a) + assert 0 <= root < 2 ** 192 + root_split = split(root, num_bits_shift=128, length=3) + ids.root.d0 = root_split[0] + ids.root.d1 = root_split[1] + ids.root.d2 = root_split[2] +%} + */ #[cfg(test)] mod tests { use super::*; From e3e05be4950f8c0ed88c6622f85b53451b00969c Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 13 Apr 2023 10:38:24 -0300 Subject: [PATCH 19/30] Add implementation for hint on sqrt --- .../builtin_hint_processor_definition.rs | 3 +++ .../builtin_hint_processor/uint384.rs | 22 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index f30770627e..abddad0804 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -465,6 +465,9 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::UINT384_UNSIGNED_DIV_REM_EXPANDED => { uint384_unsigned_div_rem_expanded(vm, &hint_data.ids_data, &hint_data.ap_tracking) } + hint_code::UINT384_SQRT => { + uint256_sqrt(vm, &hint_data.ids_data, &hint_data.ap_tracking) + } #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), code => Err(HintError::UnknownHint(code.to_string())), diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 4796234fb9..6543368ff7 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -2,8 +2,9 @@ use core::ops::Shl; use felt::Felt252; use num_bigint::BigUint; use num_integer::Integer; -use num_traits::One; +use num_traits::{One, Zero}; +use crate::math_utils::isqrt; use crate::stdlib::{borrow::Cow, collections::HashMap, prelude::*}; use crate::types::relocatable::Relocatable; use crate::{ @@ -282,6 +283,25 @@ pub fn uint384_unsigned_div_rem_expanded( ids.root.d2 = root_split[2] %} */ +pub fn uint384_sqrt( + vm: &mut VirtualMachine, + ids_data: &HashMap, + ap_tracking: &ApTracking, +) -> Result<(), HintError> { + let a = pack(BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?, 128); + let root_addr = get_relocatable_from_var_name("root", vm, ids_data, ap_tracking)?; + let root = isqrt(&a)?; + if root.is_zero() || root >= BigUint::one().shl(192_u32) { + return Err(HintError::AssertionFailed(String::from( + "assert 0 <= root < 2 ** 192", + ))); + } + let root_split = split::<3>(&root, 128); + for (i, root_split) in root_split.iter().enumerate() { + vm.insert_value((root_addr + i)?, Felt252::from(root_split))?; + } + Ok(()) +} #[cfg(test)] mod tests { use super::*; From faf47654b3253a75fd418de6f95ad26b3e40ff41 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 13 Apr 2023 10:54:57 -0300 Subject: [PATCH 20/30] Integration test --- cairo_programs/uint384.cairo | 153 +++++++++++++++++- .../builtin_hint_processor_definition.rs | 4 +- 2 files changed, 154 insertions(+), 3 deletions(-) diff --git a/cairo_programs/uint384.cairo b/cairo_programs/uint384.cairo index 2cc9d992fa..9e52c5d399 100644 --- a/cairo_programs/uint384.cairo +++ b/cairo_programs/uint384.cairo @@ -47,6 +47,56 @@ namespace uint384_lib { return (); } + // Adds two integers. Returns the result as a 384-bit integer and the (1-bit) carry. + func add{range_check_ptr}(a: Uint384, b: Uint384) -> (res: Uint384, carry: felt) { + alloc_locals; + local res: Uint384; + local carry_d0: felt; + local carry_d1: felt; + local carry_d2: felt; + %{ + sum_d0 = ids.a.d0 + ids.b.d0 + ids.carry_d0 = 1 if sum_d0 >= ids.SHIFT else 0 + sum_d1 = ids.a.d1 + ids.b.d1 + ids.carry_d0 + ids.carry_d1 = 1 if sum_d1 >= ids.SHIFT else 0 + sum_d2 = ids.a.d2 + ids.b.d2 + ids.carry_d1 + ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0 + %} + + // Either 0 or 1 + assert carry_d0 * carry_d0 = carry_d0; + assert carry_d1 * carry_d1 = carry_d1; + assert carry_d2 * carry_d2 = carry_d2; + + assert res.d0 = a.d0 + b.d0 - carry_d0 * SHIFT; + assert res.d1 = a.d1 + b.d1 + carry_d0 - carry_d1 * SHIFT; + assert res.d2 = a.d2 + b.d2 + carry_d1 - carry_d2 * SHIFT; + + check(res); + + return (res, carry_d2); + } + + // Subtracts two integers. Returns the result as a 384-bit integer. + func sub{range_check_ptr}(a: Uint384, b: Uint384) -> (res: Uint384) { + let (b_neg) = neg(b); + let (res, _) = add(a, b_neg); + return (res,); + } + + // Returns the bitwise NOT of an integer. + func not(a: Uint384) -> (res: Uint384) { + return (Uint384(d0=ALL_ONES - a.d0, d1=ALL_ONES - a.d1, d2=ALL_ONES - a.d2),); + } + + // Returns the negation of an integer. + // Note that the negation of -2**383 is -2**383. + func neg{range_check_ptr}(a: Uint384) -> (res: Uint384) { + let (not_num) = not(a); + let (res, _) = add(not_num, Uint384(d0=1, d1=0, d2=0)); + return (res,); + } + // Adds two integers. Returns the result as a 384-bit integer and the (1-bit) carry. // Doesn't verify that the result is a proper Uint384, that's now the responsibility of the calling function func _add_no_uint384_check{range_check_ptr}(a: Uint384, b: Uint384) -> (res: Uint384, carry: felt) { @@ -213,6 +263,12 @@ namespace uint384_lib { return (is_le(a.d2 + 1, b.d2),); } + // Returns 1 if the first unsigned integer is less than or equal to the second unsigned integer. + func le{range_check_ptr}(a: Uint384, b: Uint384) -> (res: felt) { + let (not_le) = lt(a=b, b=a); + return (1 - not_le,); + } + // Unsigned integer division between two integers. Returns the quotient and the remainder. // Conforms to EVM specifications: division by 0 yields 0. func unsigned_div_rem{range_check_ptr}(a: Uint384, div: Uint384) -> ( @@ -324,11 +380,99 @@ namespace uint384_lib { assert is_valid = 1; return (quotient=quotient, remainder=remainder); } + + func square_e{range_check_ptr}(a: Uint384) -> (low: Uint384, high: Uint384) { + alloc_locals; + let (a0, a1) = split_64(a.d0); + let (a2, a3) = split_64(a.d1); + let (a4, a5) = split_64(a.d2); + + const HALF_SHIFT2 = 2*HALF_SHIFT; + local a0_2 = a0*2; + local a34 = a3 + a4*HALF_SHIFT2; + + let (res0, carry) = split_128(a0*(a0 + a1*HALF_SHIFT2)); + let (res2, carry) = split_128( + a.d1*a0_2 + a1*(a1 + a2*HALF_SHIFT2) + carry, + ); + let (res4, carry) = split_128( + a.d2*a0_2 + (a3 + a34)*a1 + a2*(a2 + a3*HALF_SHIFT2) + carry, + ); + let (res6, carry) = split_128( + (a5*a1 + a.d2*a2)*2 + a3*a34 + carry, + ); + let (res8, carry) = split_128( + a5*(a3 + a34) + a4*a4 + carry + ); + // let (res10, carry) = split_64(a5*a5 + carry) + + return ( + low=Uint384(d0=res0, d1=res2, d2=res4), + high=Uint384(d0=res6, d1=res8, d2=a5*a5 + carry), + ); + } + + // Returns the floor value of the square root of a Uint384 integer. + func sqrt{range_check_ptr}(a: Uint384) -> (res: Uint384) { + alloc_locals; + let (__fp__, _) = get_fp_and_pc(); + local root: Uint384; + + %{ + from starkware.python.math_utils import isqrt + + def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a, num_bits_shift=128) + root = isqrt(a) + assert 0 <= root < 2 ** 192 + root_split = split(root, num_bits_shift=128, length=3) + ids.root.d0 = root_split[0] + ids.root.d1 = root_split[1] + ids.root.d2 = root_split[2] + %} + + // Verify that 0 <= root < 2**192. + assert root.d2 = 0; + [range_check_ptr] = root.d0; + + // We don't need to check that 0 <= d1 < 2**64, since this gets checked + // when we check that carry==0 later + assert [range_check_ptr + 1] = root.d1; + let range_check_ptr = range_check_ptr + 2; + + // Verify that n >= root**2. + let (root_squared, carry) = square_e(root); + assert carry = Uint384(0, 0, 0); + let (check_lower_bound) = le(root_squared, a); + assert check_lower_bound = 1; + + // Verify that n <= (root+1)**2 - 1. + // In the case where root = 2**192 - 1, we will have next_root_squared=0, since + // (root+1)**2 = 2**384. Therefore next_root_squared - 1 = 2**384 - 1, as desired. + let (next_root, add_carry) = add(root, Uint384(1, 0, 0)); + assert add_carry = 0; + let (next_root_squared, _) = square_e(next_root); + let (next_root_squared_minus_one) = sub(next_root_squared, Uint384(1, 0, 0)); + let (check_upper_bound) = le(a, next_root_squared_minus_one); + assert check_upper_bound = 1; + + return (res=root); + } } func test_uint384_operations{range_check_ptr}() { // Test unsigned_div_rem - let a = Uint384(83434123481193248,82349321849739284, 839243219401320423); + let a = Uint384(83434123481193248, 82349321849739284, 839243219401320423); let div = Uint384(9283430921839492319493, 313248123482483248, 3790328402913840); let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem{range_check_ptr=range_check_ptr}(a, div); assert quotient.d0 = 221; @@ -368,6 +512,13 @@ func test_uint384_operations{range_check_ptr}() { assert remainder.d1 = 340282366920938463463356863525615958397; assert remainder.d2 = 16; + // Test sqrt + let f = Uint384(83434123481193248, 82349321849739284, 839243219401320423); + let (root) = uint384_lib.sqrt(f); + assert root.d0 = 100835122758113432298839930225328621183; + assert root.d1 = 916102188; + assert root.d2 = 0; + return(); } diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index abddad0804..35c45482e3 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -72,7 +72,7 @@ use crate::hint_processor::builtin_hint_processor::skip_next_instruction::skip_n use super::ec_utils::{chained_ec_op_random_ec_point_hint, random_ec_point_hint, recover_y_hint}; use super::uint384::{ - add_no_uint384_check, uint384_split_128, uint384_unsigned_div_rem, + add_no_uint384_check, uint384_split_128, uint384_sqrt, uint384_unsigned_div_rem, uint384_unsigned_div_rem_expanded, }; @@ -466,7 +466,7 @@ impl HintProcessor for BuiltinHintProcessor { uint384_unsigned_div_rem_expanded(vm, &hint_data.ids_data, &hint_data.ap_tracking) } hint_code::UINT384_SQRT => { - uint256_sqrt(vm, &hint_data.ids_data, &hint_data.ap_tracking) + uint384_sqrt(vm, &hint_data.ids_data, &hint_data.ap_tracking) } #[cfg(feature = "skip_next_instruction_hint")] hint_code::SKIP_NEXT_INSTRUCTION => skip_next_instruction(vm), From d30d40489061efa8277501bf04bb79a0a57986c7 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 13 Apr 2023 11:49:40 -0300 Subject: [PATCH 21/30] Add unit tests --- .../builtin_hint_processor/uint384.rs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 6543368ff7..3299583e6c 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -655,4 +655,54 @@ mod tests { &felt_str!("340282366920938463463356863525615958397") ); } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_sqrt_ok() { + let mut vm = vm_with_range_check!(); + //Initialize fp + vm.run_context.fp = 5; + //Create hint_data + let ids_data = non_continuous_ids_data![("a", -5), ("root", -2)]; + //Insert ids into memory + vm.segments = segments![ + //a + ((1, 0), 83434123481193248), + ((1, 1), 82349321849739284), + ((1, 2), 839243219401320423) + ]; + //Execute the hint + assert_matches!(run_hint!(vm, ids_data, hint_code::UINT384_SQRT), Ok(())); + //Check hint memory inserts + check_memory![ + vm.segments.memory, + // root + ((1, 3), 100835122758113432298839930225328621183), + ((1, 4), 916102188), + ((1, 5), 0) + ]; + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn run_sqrt_assertion_fail() { + let mut vm = vm_with_range_check!(); + //Initialize fp + vm.run_context.fp = 5; + //Create hint_data + let ids_data = non_continuous_ids_data![("a", -5), ("root", -2)]; + //Insert ids into memory + //Insert ids into memory + vm.segments = segments![ + //a + ((1, 0), (-1)), + ((1, 1), (-1)), + ((1, 2), (-1)) + ]; + //Execute the hint + assert_matches!( + run_hint!(vm, ids_data, hint_code::UINT384_SQRT), + Err(HintError::AssertionFailed(s)) if s == "assert 0 <= root < 2 ** 192" + ); + } } From f257b564523178a2c5199bf5a4deba4ea2a20fe1 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 13 Apr 2023 11:56:03 -0300 Subject: [PATCH 22/30] Fix missing directive --- src/tests/cairo_run_test.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tests/cairo_run_test.rs b/src/tests/cairo_run_test.rs index 9ce329c177..b0b7b131b4 100644 --- a/src/tests/cairo_run_test.rs +++ b/src/tests/cairo_run_test.rs @@ -1267,6 +1267,8 @@ fn cairo_run_recover_y() { run_program_simple(program_data.as_slice()); } +#[test] +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn cairo_run_math_integration() { let program_data = include_bytes!("../../cairo_programs/math_integration_tests.json"); run_program_simple(program_data.as_slice()); From 4218e8059620ee10a000e1684d1956355ceef55a Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 13 Apr 2023 12:10:52 -0300 Subject: [PATCH 23/30] Run cairo-format --- cairo_programs/is_quad_residue_test.cairo | 2 +- cairo_programs/uint384.cairo | 116 +++++++++++----------- 2 files changed, 61 insertions(+), 57 deletions(-) diff --git a/cairo_programs/is_quad_residue_test.cairo b/cairo_programs/is_quad_residue_test.cairo index 4b20e3a2e9..7b78ec6e65 100644 --- a/cairo_programs/is_quad_residue_test.cairo +++ b/cairo_programs/is_quad_residue_test.cairo @@ -39,5 +39,5 @@ func main{output_ptr: felt*}() { check_quad_res(inputs, expected, 0); - return(); + return (); } diff --git a/cairo_programs/uint384.cairo b/cairo_programs/uint384.cairo index 9e52c5d399..ecb135dae8 100644 --- a/cairo_programs/uint384.cairo +++ b/cairo_programs/uint384.cairo @@ -99,7 +99,9 @@ namespace uint384_lib { // Adds two integers. Returns the result as a 384-bit integer and the (1-bit) carry. // Doesn't verify that the result is a proper Uint384, that's now the responsibility of the calling function - func _add_no_uint384_check{range_check_ptr}(a: Uint384, b: Uint384) -> (res: Uint384, carry: felt) { + func _add_no_uint384_check{range_check_ptr}(a: Uint384, b: Uint384) -> ( + res: Uint384, carry: felt + ) { alloc_locals; local res: Uint384; local carry_d0: felt; @@ -189,28 +191,34 @@ namespace uint384_lib { let (res10, carry) = split_64(a5 * b5 + carry); return ( - low=Uint384(d0=res0 + HALF_SHIFT * res1, d1=res2 + HALF_SHIFT * res3, d2=res4 + HALF_SHIFT * res5), - high=Uint384(d0=res6 + HALF_SHIFT * res7, d1=res8 + HALF_SHIFT * res9, d2=res10 + HALF_SHIFT * carry), + low=Uint384( + d0=res0 + HALF_SHIFT * res1, + d1=res2 + HALF_SHIFT * res3, + d2=res4 + HALF_SHIFT * res5, + ), + high=Uint384( + d0=res6 + HALF_SHIFT * res7, + d1=res8 + HALF_SHIFT * res9, + d2=res10 + HALF_SHIFT * carry, + ), ); } - func mul_expanded{range_check_ptr}(a: Uint384, b: Uint384_expand) -> (low: Uint384, high: Uint384) { + func mul_expanded{range_check_ptr}(a: Uint384, b: Uint384_expand) -> ( + low: Uint384, high: Uint384 + ) { let (a0, a1) = split_64(a.d0); let (a2, a3) = split_64(a.d1); let (a4, a5) = split_64(a.d2); let (res0, carry) = split_128(a1 * b.B0 + a0 * b.b01); - let (res2, carry) = split_128( - a3 * b.B0 + a2 * b.b01 + a1 * b.b12 + a0 * b.b23 + carry, - ); + let (res2, carry) = split_128(a3 * b.B0 + a2 * b.b01 + a1 * b.b12 + a0 * b.b23 + carry); let (res4, carry) = split_128( - a5 * b.B0 + a4 * b.b01 + a3 * b.b12 + a2 * b.b23 + a1 * b.b34 + a0 * b.b45 + carry, + a5 * b.B0 + a4 * b.b01 + a3 * b.b12 + a2 * b.b23 + a1 * b.b34 + a0 * b.b45 + carry ); let (res6, carry) = split_128( - a5 * b.b12 + a4 * b.b23 + a3 * b.b34 + a2 * b.b45 + a1 * b.b5 + carry, - ); - let (res8, carry) = split_128( - a5 * b.b34 + a4 * b.b45 + a3 * b.b5 + carry + a5 * b.b12 + a4 * b.b23 + a3 * b.b34 + a2 * b.b45 + a1 * b.b5 + carry ); + let (res8, carry) = split_128(a5 * b.b34 + a4 * b.b45 + a3 * b.b5 + carry); // let (res10, carry) = split_64(a5 * b.b5 + carry) return ( @@ -219,7 +227,7 @@ namespace uint384_lib { ); } - func mul_d{range_check_ptr}(a: Uint384, b: Uint384) -> (low: Uint384, high: Uint384) { + func mul_d{range_check_ptr}(a: Uint384, b: Uint384) -> (low: Uint384, high: Uint384) { alloc_locals; let (a0, a1) = split_64(a.d0); let (a2, a3) = split_64(a.d1); @@ -228,23 +236,19 @@ namespace uint384_lib { let (b2, b3) = split_64(b.d1); let (b4, b5) = split_64(b.d2); - local B0 = b0*HALF_SHIFT; - local b12 = b1 + b2*HALF_SHIFT; - local b34 = b3 + b4*HALF_SHIFT; + local B0 = b0 * HALF_SHIFT; + local b12 = b1 + b2 * HALF_SHIFT; + local b34 = b3 + b4 * HALF_SHIFT; let (res0, carry) = split_128(a1 * B0 + a0 * b.d0); - let (res2, carry) = split_128( - a3 * B0 + a2 * b.d0 + a1 * b12 + a0 * b.d1 + carry, - ); + let (res2, carry) = split_128(a3 * B0 + a2 * b.d0 + a1 * b12 + a0 * b.d1 + carry); let (res4, carry) = split_128( - a5 * B0 + a4 * b.d0 + a3 * b12 + a2 * b.d1 + a1 * b34 + a0 * b.d2 + carry, + a5 * B0 + a4 * b.d0 + a3 * b12 + a2 * b.d1 + a1 * b34 + a0 * b.d2 + carry ); let (res6, carry) = split_128( - a5 * b12 + a4 * b.d1 + a3 * b34 + a2 * b.d2 + a1 * b5 + carry, - ); - let (res8, carry) = split_128( - a5 * b34 + a4 * b.d2 + a3 * b5 + carry + a5 * b12 + a4 * b.d1 + a3 * b34 + a2 * b.d2 + a1 * b5 + carry ); + let (res8, carry) = split_128(a5 * b34 + a4 * b.d2 + a3 * b5 + carry); // let (res10, carry) = split_64(a5 * b5 + carry) return ( @@ -311,8 +315,8 @@ namespace uint384_lib { ids.remainder.d1 = remainder_split[1] ids.remainder.d2 = remainder_split[2] %} - check(quotient); - check(remainder); + check(quotient); + check(remainder); let (res_mul: Uint384, carry: Uint384) = mul_d(quotient, div); assert carry = Uint384(0, 0, 0); @@ -325,7 +329,7 @@ namespace uint384_lib { return (quotient=quotient, remainder=remainder); } -// Unsigned integer division between two integers. Returns the quotient and the remainder. + // Unsigned integer division between two integers. Returns the quotient and the remainder. func unsigned_div_rem_expanded{range_check_ptr}(a: Uint384, div: Uint384_expand) -> ( quotient: Uint384, remainder: Uint384 ) { @@ -333,7 +337,7 @@ namespace uint384_lib { local quotient: Uint384; local remainder: Uint384; - let div2 = Uint384(div.b01,div.b23,div.b45); + let div2 = Uint384(div.b01, div.b23, div.b45); %{ def split(num: int, num_bits_shift: int, length: int): @@ -367,8 +371,8 @@ namespace uint384_lib { ids.remainder.d1 = remainder_split[1] ids.remainder.d2 = remainder_split[2] %} - check(quotient); - check(remainder); + check(quotient); + check(remainder); let (res_mul: Uint384, carry: Uint384) = mul_expanded(quotient, div); assert carry = Uint384(0, 0, 0); @@ -381,34 +385,28 @@ namespace uint384_lib { return (quotient=quotient, remainder=remainder); } - func square_e{range_check_ptr}(a: Uint384) -> (low: Uint384, high: Uint384) { + func square_e{range_check_ptr}(a: Uint384) -> (low: Uint384, high: Uint384) { alloc_locals; let (a0, a1) = split_64(a.d0); let (a2, a3) = split_64(a.d1); let (a4, a5) = split_64(a.d2); - const HALF_SHIFT2 = 2*HALF_SHIFT; - local a0_2 = a0*2; - local a34 = a3 + a4*HALF_SHIFT2; + const HALF_SHIFT2 = 2 * HALF_SHIFT; + local a0_2 = a0 * 2; + local a34 = a3 + a4 * HALF_SHIFT2; - let (res0, carry) = split_128(a0*(a0 + a1*HALF_SHIFT2)); - let (res2, carry) = split_128( - a.d1*a0_2 + a1*(a1 + a2*HALF_SHIFT2) + carry, - ); + let (res0, carry) = split_128(a0 * (a0 + a1 * HALF_SHIFT2)); + let (res2, carry) = split_128(a.d1 * a0_2 + a1 * (a1 + a2 * HALF_SHIFT2) + carry); let (res4, carry) = split_128( - a.d2*a0_2 + (a3 + a34)*a1 + a2*(a2 + a3*HALF_SHIFT2) + carry, - ); - let (res6, carry) = split_128( - (a5*a1 + a.d2*a2)*2 + a3*a34 + carry, - ); - let (res8, carry) = split_128( - a5*(a3 + a34) + a4*a4 + carry + a.d2 * a0_2 + (a3 + a34) * a1 + a2 * (a2 + a3 * HALF_SHIFT2) + carry ); + let (res6, carry) = split_128((a5 * a1 + a.d2 * a2) * 2 + a3 * a34 + carry); + let (res8, carry) = split_128(a5 * (a3 + a34) + a4 * a4 + carry); // let (res10, carry) = split_64(a5*a5 + carry) return ( low=Uint384(d0=res0, d1=res2, d2=res4), - high=Uint384(d0=res6, d1=res8, d2=a5*a5 + carry), + high=Uint384(d0=res6, d1=res8, d2=a5 * a5 + carry), ); } @@ -446,7 +444,7 @@ namespace uint384_lib { [range_check_ptr] = root.d0; // We don't need to check that 0 <= d1 < 2**64, since this gets checked - // when we check that carry==0 later + // when we check that carry==0 later assert [range_check_ptr + 1] = root.d1; let range_check_ptr = range_check_ptr + 2; @@ -474,11 +472,13 @@ func test_uint384_operations{range_check_ptr}() { // Test unsigned_div_rem let a = Uint384(83434123481193248, 82349321849739284, 839243219401320423); let div = Uint384(9283430921839492319493, 313248123482483248, 3790328402913840); - let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem{range_check_ptr=range_check_ptr}(a, div); + let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem{ + range_check_ptr=range_check_ptr + }(a, div); assert quotient.d0 = 221; assert quotient.d1 = 0; assert quotient.d2 = 0; - + assert remainder.d0 = 340282366920936411825224315027446796751; assert remainder.d1 = 340282366920938463394229121463989152931; assert remainder.d2 = 1580642357361782; @@ -501,13 +501,17 @@ func test_uint384_operations{range_check_ptr}() { assert carry = 1; // Test unsigned_div_rem_expanded - let e = Uint384(83434123481193248,82349321849739284, 839243219401320423); - let div_expand = Uint384_expand(9283430921839492319493, 313248123482483248, 3790328402913840, 13, 78990, 109, 7); - let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem_expanded{range_check_ptr=range_check_ptr}(a, div_expand); + let e = Uint384(83434123481193248, 82349321849739284, 839243219401320423); + let div_expand = Uint384_expand( + 9283430921839492319493, 313248123482483248, 3790328402913840, 13, 78990, 109, 7 + ); + let (quotient: Uint384, remainder: Uint384) = uint384_lib.unsigned_div_rem_expanded{ + range_check_ptr=range_check_ptr + }(a, div_expand); assert quotient.d0 = 7699479077076334; assert quotient.d1 = 0; assert quotient.d2 = 0; - + assert remainder.d0 = 340279955073565776659831804641277151872; assert remainder.d1 = 340282366920938463463356863525615958397; assert remainder.d2 = 16; @@ -519,10 +523,10 @@ func test_uint384_operations{range_check_ptr}() { assert root.d1 = 916102188; assert root.d2 = 0; - return(); + return (); } -func main{range_check_ptr: felt}(){ +func main{range_check_ptr: felt}() { test_uint384_operations(); - return(); + return (); } From e1d01007ff999b7200a67ba91041a06e63153c4a Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 13 Apr 2023 15:24:10 -0300 Subject: [PATCH 24/30] Add changelog entry --- CHANGELOG.md | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1c8956cac..5c2f229a61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,109 @@ #### Upcoming Changes +* Implement hints on uint384 lib (Part 1) [#960](https://github.com/lambdaclass/cairo-rs/pull/960) + + `BuiltinHintProcessor` now supports the following hint: + + ```python + def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a, num_bits_shift = 128) + div = pack(ids.div, num_bits_shift = 128) + quotient, remainder = divmod(a, div) + + quotient_split = split(quotient, num_bits_shift=128, length=3) + assert len(quotient_split) == 3 + + ids.quotient.d0 = quotient_split[0] + ids.quotient.d1 = quotient_split[1] + ids.quotient.d2 = quotient_split[2] + + remainder_split = split(remainder, num_bits_shift=128, length=3) + ids.remainder.d0 = remainder_split[0] + ids.remainder.d1 = remainder_split[1] + ids.remainder.d2 = remainder_split[2] + ``` + + ```python + ids.low = ids.a & ((1<<128) - 1) + ids.high = ids.a >> 128 + ``` + + ```python + sum_d0 = ids.a.d0 + ids.b.d0 + ids.carry_d0 = 1 if sum_d0 >= ids.SHIFT else 0 + sum_d1 = ids.a.d1 + ids.b.d1 + ids.carry_d0 + ids.carry_d1 = 1 if sum_d1 >= ids.SHIFT else 0 + sum_d2 = ids.a.d2 + ids.b.d2 + ids.carry_d1 + ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0 + ``` + + ```python + def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + def pack2(z, num_bits_shift: int) -> int: + limbs = (z.b01, z.b23, z.b45) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a, num_bits_shift = 128) + div = pack2(ids.div, num_bits_shift = 128) + quotient, remainder = divmod(a, div) + + quotient_split = split(quotient, num_bits_shift=128, length=3) + assert len(quotient_split) == 3 + + ids.quotient.d0 = quotient_split[0] + ids.quotient.d1 = quotient_split[1] + ids.quotient.d2 = quotient_split[2] + + remainder_split = split(remainder, num_bits_shift=128, length=3) + ids.remainder.d0 = remainder_split[0] + ids.remainder.d1 = remainder_split[1] + ids.remainder.d2 = remainder_split[2] + ``` + + ```python + from starkware.python.math_utils import isqrt + + def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + + def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + + a = pack(ids.a, num_bits_shift=128) + root = isqrt(a) + assert 0 <= root < 2 ** 192 + root_split = split(root, num_bits_shift=128, length=3) + ids.root.d0 = root_split[0] + ids.root.d1 = root_split[1] + ids.root.d2 = root_split[2] + ``` + * Move `Memory` into `MemorySegmentManager` [#830](https://github.com/lambdaclass/cairo-rs/pull/830) * Structural changes: * Remove `memory: Memory` field from `VirtualMachine` From 51c7dc3bec007903b4b57e5e331469244842c6b0 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 13 Apr 2023 15:26:35 -0300 Subject: [PATCH 25/30] Spelling --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c2f229a61..23009d6737 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ * Implement hints on uint384 lib (Part 1) [#960](https://github.com/lambdaclass/cairo-rs/pull/960) - `BuiltinHintProcessor` now supports the following hint: + `BuiltinHintProcessor` now supports the following hints: ```python def split(num: int, num_bits_shift: int, length: int): From 3f8bdfb985b1213c91146048e139815f2ec05bba Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Thu, 13 Apr 2023 16:09:55 -0300 Subject: [PATCH 26/30] Update src/hint_processor/builtin_hint_processor/uint384.rs Co-authored-by: Mario Rugiero --- src/hint_processor/builtin_hint_processor/uint384.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 3299583e6c..b2a4a831e5 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -291,7 +291,7 @@ pub fn uint384_sqrt( let a = pack(BigInt3::from_var_name("a", vm, ids_data, ap_tracking)?, 128); let root_addr = get_relocatable_from_var_name("root", vm, ids_data, ap_tracking)?; let root = isqrt(&a)?; - if root.is_zero() || root >= BigUint::one().shl(192_u32) { + if root.is_zero() || root.bits() > 192 { return Err(HintError::AssertionFailed(String::from( "assert 0 <= root < 2 ** 192", ))); From 43f39776597b847e05354ff1d886971660f57b31 Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Thu, 13 Apr 2023 16:14:40 -0300 Subject: [PATCH 27/30] Update src/hint_processor/builtin_hint_processor/uint384.rs Co-authored-by: Mario Rugiero --- src/hint_processor/builtin_hint_processor/uint384.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index b2a4a831e5..58ab113f5a 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -70,7 +70,7 @@ fn split(num: &BigUint, num_bits_shift: u32) -> [BigUint; T] { } fn pack(num: BigInt3, num_bits_shift: usize) -> BigUint { - let limbs = vec![num.d0, num.d1, num.d2]; + let limbs = [num.d0, num.d1, num.d2]; #[allow(deprecated)] limbs .into_iter() From 004591460b5e0982254049fdada6160b850f6e35 Mon Sep 17 00:00:00 2001 From: fmoletta <99273364+fmoletta@users.noreply.github.com> Date: Thu, 13 Apr 2023 16:14:49 -0300 Subject: [PATCH 28/30] Update src/hint_processor/builtin_hint_processor/uint384.rs Co-authored-by: Mario Rugiero --- src/hint_processor/builtin_hint_processor/uint384.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hint_processor/builtin_hint_processor/uint384.rs b/src/hint_processor/builtin_hint_processor/uint384.rs index 58ab113f5a..055d907dd7 100644 --- a/src/hint_processor/builtin_hint_processor/uint384.rs +++ b/src/hint_processor/builtin_hint_processor/uint384.rs @@ -80,7 +80,7 @@ fn pack(num: BigInt3, num_bits_shift: usize) -> BigUint { } fn pack2(num: Uint384ExpandReduced, num_bits_shift: usize) -> BigUint { - let limbs = vec![num.b01, num.b23, num.b45]; + let limbs = [num.b01, num.b23, num.b45]; #[allow(deprecated)] limbs .into_iter() From 4f8920236c20f119d92696594b9c3cab7258b3e2 Mon Sep 17 00:00:00 2001 From: Federica Date: Thu, 13 Apr 2023 16:28:14 -0300 Subject: [PATCH 29/30] Make hint code more readable --- .../builtin_hint_processor/hint_code.rs | 60 ++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/src/hint_processor/builtin_hint_processor/hint_code.rs b/src/hint_processor/builtin_hint_processor/hint_code.rs index 5948958efa..02f421db06 100644 --- a/src/hint_processor/builtin_hint_processor/hint_code.rs +++ b/src/hint_processor/builtin_hint_processor/hint_code.rs @@ -619,7 +619,33 @@ ids.p.x = ids.x ids.p.y = recover_y(ids.x, ALPHA, BETA, FIELD_PRIME)"; // The following hints support the lib https://github.com/NethermindEth/research-basic-Cairo-operations-big-integers/blob/main/lib/uint384.cairo -pub(crate) const UINT384_UNSIGNED_DIV_REM: &str ="def split(num: int, num_bits_shift: int, length: int):\n a = []\n for _ in range(length):\n a.append( num & ((1 << num_bits_shift) - 1) )\n num = num >> num_bits_shift\n return tuple(a)\n\ndef pack(z, num_bits_shift: int) -> int:\n limbs = (z.d0, z.d1, z.d2)\n return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))\n\na = pack(ids.a, num_bits_shift = 128)\ndiv = pack(ids.div, num_bits_shift = 128)\nquotient, remainder = divmod(a, div)\n\nquotient_split = split(quotient, num_bits_shift=128, length=3)\nassert len(quotient_split) == 3\n\nids.quotient.d0 = quotient_split[0]\nids.quotient.d1 = quotient_split[1]\nids.quotient.d2 = quotient_split[2]\n\nremainder_split = split(remainder, num_bits_shift=128, length=3)\nids.remainder.d0 = remainder_split[0]\nids.remainder.d1 = remainder_split[1]\nids.remainder.d2 = remainder_split[2]"; +pub(crate) const UINT384_UNSIGNED_DIV_REM: &str = + "def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + +def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + +a = pack(ids.a, num_bits_shift = 128) +div = pack(ids.div, num_bits_shift = 128) +quotient, remainder = divmod(a, div) + +quotient_split = split(quotient, num_bits_shift=128, length=3) +assert len(quotient_split) == 3 + +ids.quotient.d0 = quotient_split[0] +ids.quotient.d1 = quotient_split[1] +ids.quotient.d2 = quotient_split[2] + +remainder_split = split(remainder, num_bits_shift=128, length=3) +ids.remainder.d0 = remainder_split[0] +ids.remainder.d1 = remainder_split[1] +ids.remainder.d2 = remainder_split[2]"; pub(crate) const UINT384_SPLIT_128: &str = "ids.low = ids.a & ((1<<128) - 1) ids.high = ids.a >> 128"; pub(crate) const ADD_NO_UINT384_CHECK: &str = "sum_d0 = ids.a.d0 + ids.b.d0 @@ -628,7 +654,37 @@ sum_d1 = ids.a.d1 + ids.b.d1 + ids.carry_d0 ids.carry_d1 = 1 if sum_d1 >= ids.SHIFT else 0 sum_d2 = ids.a.d2 + ids.b.d2 + ids.carry_d1 ids.carry_d2 = 1 if sum_d2 >= ids.SHIFT else 0"; -pub(crate) const UINT384_UNSIGNED_DIV_REM_EXPANDED: &str = "def split(num: int, num_bits_shift: int, length: int):\n a = []\n for _ in range(length):\n a.append( num & ((1 << num_bits_shift) - 1) )\n num = num >> num_bits_shift\n return tuple(a)\n\ndef pack(z, num_bits_shift: int) -> int:\n limbs = (z.d0, z.d1, z.d2)\n return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))\n\ndef pack2(z, num_bits_shift: int) -> int:\n limbs = (z.b01, z.b23, z.b45)\n return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs))\n\na = pack(ids.a, num_bits_shift = 128)\ndiv = pack2(ids.div, num_bits_shift = 128)\nquotient, remainder = divmod(a, div)\n\nquotient_split = split(quotient, num_bits_shift=128, length=3)\nassert len(quotient_split) == 3\n\nids.quotient.d0 = quotient_split[0]\nids.quotient.d1 = quotient_split[1]\nids.quotient.d2 = quotient_split[2]\n\nremainder_split = split(remainder, num_bits_shift=128, length=3)\nids.remainder.d0 = remainder_split[0]\nids.remainder.d1 = remainder_split[1]\nids.remainder.d2 = remainder_split[2]"; +pub(crate) const UINT384_UNSIGNED_DIV_REM_EXPANDED: &str = + "def split(num: int, num_bits_shift: int, length: int): + a = [] + for _ in range(length): + a.append( num & ((1 << num_bits_shift) - 1) ) + num = num >> num_bits_shift + return tuple(a) + +def pack(z, num_bits_shift: int) -> int: + limbs = (z.d0, z.d1, z.d2) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + +def pack2(z, num_bits_shift: int) -> int: + limbs = (z.b01, z.b23, z.b45) + return sum(limb << (num_bits_shift * i) for i, limb in enumerate(limbs)) + +a = pack(ids.a, num_bits_shift = 128) +div = pack2(ids.div, num_bits_shift = 128) +quotient, remainder = divmod(a, div) + +quotient_split = split(quotient, num_bits_shift=128, length=3) +assert len(quotient_split) == 3 + +ids.quotient.d0 = quotient_split[0] +ids.quotient.d1 = quotient_split[1] +ids.quotient.d2 = quotient_split[2] + +remainder_split = split(remainder, num_bits_shift=128, length=3) +ids.remainder.d0 = remainder_split[0] +ids.remainder.d1 = remainder_split[1] +ids.remainder.d2 = remainder_split[2]"; pub(crate) const UINT384_SQRT: &str = "from starkware.python.math_utils import isqrt def split(num: int, num_bits_shift: int, length: int): From 6861479e40e1ec6802badf14721c03f766256d32 Mon Sep 17 00:00:00 2001 From: Federica Date: Fri, 14 Apr 2023 12:44:37 -0300 Subject: [PATCH 30/30] fix fmt --- .../builtin_hint_processor/builtin_hint_processor_definition.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs index 3508096f1a..9c0e96eada 100644 --- a/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs +++ b/src/hint_processor/builtin_hint_processor/builtin_hint_processor_definition.rs @@ -456,7 +456,7 @@ impl HintProcessor for BuiltinHintProcessor { hint_code::CHAINED_EC_OP_RANDOM_EC_POINT => { chained_ec_op_random_ec_point_hint(vm, &hint_data.ids_data, &hint_data.ap_tracking) } - hint_code::RECOVER_Y => recover_y_hint(vm, &hint_data.ids_data, &hint_data.ap_tracking) + hint_code::RECOVER_Y => recover_y_hint(vm, &hint_data.ids_data, &hint_data.ap_tracking), hint_code::UINT384_UNSIGNED_DIV_REM => { uint384_unsigned_div_rem(vm, &hint_data.ids_data, &hint_data.ap_tracking) }