diff --git a/Cargo.toml b/Cargo.toml index 9430a7171..b2bf71930 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,6 +111,7 @@ repository.workspace = true resolver = "2" [features] +default = ["with-debug-utils"] with-cheatcode = [] with-debug-utils = [] with-mem-tracing = [] diff --git a/src/libfuncs/bounded_int.rs b/src/libfuncs/bounded_int.rs index 35cf19c7b..fe8b735a7 100644 --- a/src/libfuncs/bounded_int.rs +++ b/src/libfuncs/bounded_int.rs @@ -851,11 +851,17 @@ fn build_wrap_non_zero<'ctx, 'this>( #[cfg(test)] mod test { + use cairo_lang_sierra::extensions::utils::Range; use cairo_vm::Felt252; + use num_bigint::BigInt; use crate::{ - context::NativeContext, execution_result::ExecutionResult, executor::JitNativeExecutor, - load_cairo, OptLevel, Value, + context::NativeContext, + execution_result::ExecutionResult, + executor::JitNativeExecutor, + jit_enum, jit_struct, load_cairo, + utils::testing::{run_program, run_program_assert_output}, + OptLevel, Value, }; #[test] @@ -989,4 +995,621 @@ mod test { }; assert_eq!(value, Felt252::from(0)); } + + #[test] + fn test_add() { + let cairo = load_cairo! { + #[feature("bounded-int-utils")] + use core::internal::bounded_int::{BoundedInt, add, AddHelper}; + + impl AddHelper1 of AddHelper, BoundedInt<1, 1>> { + type Result = BoundedInt<2, 32>; + } + + fn run_test_1( + a: felt252, + b: felt252, + ) -> BoundedInt<2, 32> { + let a: BoundedInt<1, 31> = a.try_into().unwrap(); + let b: BoundedInt<1, 1> = b.try_into().unwrap(); + return add(a, b); + } + + impl AddHelper2 of AddHelper, BoundedInt<-1, -1>> { + type Result = BoundedInt<0, 30>; + } + + fn run_test_2( + a: felt252, + b: felt252, + ) -> BoundedInt<0, 30> { + let a: BoundedInt<1, 31> = a.try_into().unwrap(); + let b: BoundedInt<-1, -1> = b.try_into().unwrap(); + return add(a, b); + } + + impl AddHelper3 of AddHelper, BoundedInt<0, 10>> { + type Result = BoundedInt<0, 40>; + } + + fn run_test_3( + a: felt252, + b: felt252, + ) -> BoundedInt<0, 40> { + let a: BoundedInt<0, 30> = a.try_into().unwrap(); + let b: BoundedInt<0, 10> = b.try_into().unwrap(); + return add(a, b); + } + + impl AddHelper4 of AddHelper, BoundedInt<0, 10>> { + type Result = BoundedInt<-20, -5>; + } + + fn run_test_4( + a: felt252, + b: felt252, + ) -> BoundedInt<-20, -5> { + let a: BoundedInt<-20, -15> = a.try_into().unwrap(); + let b: BoundedInt<0, 10> = b.try_into().unwrap(); + return add(a, b); + } + + impl AddHelper4 of AddHelper, BoundedInt<-5, -5>> { + type Result = BoundedInt<-10, -10>; + } + + fn run_test_4( + a: felt252, + b: felt252, + ) -> BoundedInt<-10, -10> { + let a: BoundedInt<-5, -5> = a.try_into().unwrap(); + let b: BoundedInt<-5, -5> = b.try_into().unwrap(); + return add(a, b); + } + }; + + run_program_assert_output( + &cairo, + "run_test_1", + &[ + Value::Felt252(Felt252::from(31)), + Value::Felt252(Felt252::from(1)), + ], + jit_enum!( + 0, + jit_struct!(Value::BoundedInt { + value: Felt252::from(32), + range: Range { + lower: BigInt::from(2), + upper: BigInt::from(33), + } + }) + ), + ); + + run_program_assert_output( + &cairo, + "run_test_2", + &[ + Value::Felt252(Felt252::from(31)), + Value::Felt252(Felt252::from(-1)), + ], + jit_enum!( + 0, + jit_struct!(Value::BoundedInt { + value: Felt252::from(30), + range: Range { + lower: BigInt::from(0), + upper: BigInt::from(31), + } + }) + ), + ); + + run_program_assert_output( + &cairo, + "run_test_3", + &[ + Value::Felt252(Felt252::from(30)), + Value::Felt252(Felt252::from(10)), + ], + jit_enum!( + 0, + jit_struct!(Value::BoundedInt { + value: Felt252::from(40), + range: Range { + lower: BigInt::from(0), + upper: BigInt::from(41), + } + }) + ), + ); + + run_program_assert_output( + &cairo, + "run_test_4", + &[ + Value::Felt252(Felt252::from(-15)), + Value::Felt252(Felt252::from(10)), + ], + jit_enum!( + 0, + jit_struct!(Value::BoundedInt { + value: Felt252::from(-5), + range: Range { + lower: BigInt::from(-20), + upper: BigInt::from(-4), + } + }) + ), + ); + + run_program_assert_output( + &cairo, + "run_test_4", + &[ + Value::Felt252(Felt252::from(-20)), + Value::Felt252(Felt252::from(10)), + ], + jit_enum!( + 0, + jit_struct!(Value::BoundedInt { + value: Felt252::from(-10), + range: Range { + lower: BigInt::from(-20), + upper: BigInt::from(-4), + } + }) + ), + ); + + run_program_assert_output( + &cairo, + "run_test_5", + &[ + Value::Felt252(Felt252::from(-5)), + Value::Felt252(Felt252::from(-5)), + ], + jit_enum!( + 0, + jit_struct!(Value::BoundedInt { + value: Felt252::from(-10), + range: Range { + lower: BigInt::from(-10), + upper: BigInt::from(-10), + } + }) + ), + ); + } + + #[test] + fn test_sub() { + let cairo = load_cairo! { + #[feature("bounded-int-utils")] + use core::internal::bounded_int::{BoundedInt, sub, SubHelper}; + + impl SubHelper1 of SubHelper, BoundedInt<1, 5>> { + type Result = BoundedInt<-4, 0>; + } + + fn run_test_1( + a: felt252, + b: felt252, + ) -> BoundedInt<-4, 0> { + let a: BoundedInt<1, 1> = a.try_into().unwrap(); + let b: BoundedInt<1, 5> = b.try_into().unwrap(); + return sub(a, b); + } + + impl SubHelper2 of SubHelper, BoundedInt<1, 1>> { + type Result = BoundedInt<0, 0>; + } + + fn run_test_2( + a: felt252, + b: felt252, + ) -> BoundedInt<-4, 0> { + let a: BoundedInt<1, 1> = a.try_into().unwrap(); + let b: BoundedInt<1, 1> = b.try_into().unwrap(); + return sub(a, b); + } + + impl SubHelper3 of SubHelper, BoundedInt<-3, -3>> { + type Result = BoundedInt<0, 0>; + } + + fn run_test_3( + a: felt252, + b: felt252, + ) -> BoundedInt<-4, 0> { + let a: BoundedInt<-3, -3> = a.try_into().unwrap(); + let b: BoundedInt<-3, -3> = b.try_into().unwrap(); + return sub(a, b); + } + + impl SubHelper4 of SubHelper, BoundedInt<1, 3>> { + type Result = BoundedInt<-9, -4>; + } + + fn run_test_4( + a: felt252, + b: felt252, + ) -> BoundedInt<-9, -4> { + let a: BoundedInt<-6, -3> = a.try_into().unwrap(); + let b: BoundedInt<1, 3> = b.try_into().unwrap(); + return sub(a, b); + } + + impl SubHelper5 of SubHelper, BoundedInt<-20, -10>> { + type Result = BoundedInt<4, 18>; + } + + fn run_test_5( + a: felt252, + b: felt252, + ) -> BoundedInt<4, 18> { + let a: BoundedInt<-6, -2> = a.try_into().unwrap(); + let b: BoundedInt<-20, -10> = b.try_into().unwrap(); + return sub(a, b); + } + }; + + run_program_assert_output( + &cairo, + "run_test_1", + &[ + Value::Felt252(Felt252::from(1)), + Value::Felt252(Felt252::from(5)), + ], + jit_enum!( + 0, + jit_struct!(Value::BoundedInt { + value: Felt252::from(-4), + range: Range { + lower: BigInt::from(-4), + upper: BigInt::from(1), + } + }) + ), + ); + + run_program_assert_output( + &cairo, + "run_test_2", + &[ + Value::Felt252(Felt252::from(1)), + Value::Felt252(Felt252::from(1)), + ], + jit_enum!( + 0, + jit_struct!(Value::BoundedInt { + value: Felt252::from(0), + range: Range { + lower: BigInt::from(0), + upper: BigInt::from(1), + } + }) + ), + ); + + run_program_assert_output( + &cairo, + "run_test_3", + &[ + Value::Felt252(Felt252::from(-3)), + Value::Felt252(Felt252::from(-3)), + ], + jit_enum!( + 0, + jit_struct!(Value::BoundedInt { + value: Felt252::from(0), + range: Range { + lower: BigInt::from(0), + upper: BigInt::from(1), + } + }) + ), + ); + + run_program_assert_output( + &cairo, + "run_test_4", + &[ + Value::Felt252(Felt252::from(-6)), + Value::Felt252(Felt252::from(3)), + ], + jit_enum!( + 0, + jit_struct!(Value::BoundedInt { + value: Felt252::from(-9), + range: Range { + lower: BigInt::from(-0), + upper: BigInt::from(-3), + } + }) + ), + ); + + run_program_assert_output( + &cairo, + "run_test_5", + &[ + Value::Felt252(Felt252::from(-2)), + Value::Felt252(Felt252::from(-20)), + ], + jit_enum!( + 0, + jit_struct!(Value::BoundedInt { + value: Felt252::from(18), + range: Range { + lower: BigInt::from(4), + upper: BigInt::from(19), + } + }) + ), + ); + } + + fn assert_bool_output(result: Value, expected_tag: usize) { + if let Value::Enum { tag, value, .. } = result { + assert_eq!(tag, 0); + if let Value::Struct { fields, .. } = *value { + if let Value::Enum { tag, .. } = fields[0] { + assert_eq!(tag, expected_tag) + } + } + } + } + + #[test] + fn test_is_zero() { + let program = load_cairo! { + #[feature("bounded-int-utils")] + use core::internal::bounded_int::{self, BoundedInt, is_zero}; + use core::zeroable::IsZeroResult; + + fn run_test_1(a: felt252) -> bool { + let bi: BoundedInt<0, 5> = a.try_into().unwrap(); + match is_zero(bi) { + IsZeroResult::Zero => true, + IsZeroResult::NonZero(_) => false, + } + } + + fn run_test_2(a: felt252) -> bool { + let bi: BoundedInt<-5, 5> = a.try_into().unwrap(); + match is_zero(bi) { + IsZeroResult::Zero => true, + IsZeroResult::NonZero(_) => false, + } + } + }; + + let result = + run_program(&program, "run_test_1", &[Value::Felt252(Felt252::from(0))]).return_value; + assert_bool_output(result, 1); + + let result = + run_program(&program, "run_test_1", &[Value::Felt252(Felt252::from(5))]).return_value; + assert_bool_output(result, 0); + + let result = + run_program(&program, "run_test_2", &[Value::Felt252(Felt252::from(0))]).return_value; + assert_bool_output(result, 1); + + let result = + run_program(&program, "run_test_2", &[Value::Felt252(Felt252::from(-5))]).return_value; + assert_bool_output(result, 0); + } + + fn assert_constrain_output(result: Value, expected_bi: Value) { + if let Value::Enum { tag, value, .. } = result { + assert_eq!(tag, 0); + if let Value::Struct { fields, .. } = *value { + assert_eq!(expected_bi, fields[0]); + } + } + } + + #[test] + fn test_constrain() { + let program = load_cairo! { + #[feature("bounded-int-utils")] + use core::internal::bounded_int::{self, BoundedInt, ConstrainHelper, constrain}; + + fn run_test_1(a: i8) -> BoundedInt<-128, -1> { + match constrain::(a) { + Ok(lt0) => lt0, + Err(_gt0) => panic!(), + } + } + + fn run_test_2(a: i8) -> BoundedInt<0, 127> { + match constrain::(a) { + Ok(_lt0) => panic!(), + Err(gt0) => gt0, + } + } + + impl ConstrainTest1 of ConstrainHelper, 5> { + type LowT = BoundedInt<0, 4>; + type HighT = BoundedInt<5, 15>; + } + + fn run_test_3(a: felt252) -> BoundedInt<0, 4> { + let a_bi: BoundedInt<0, 15> = a.try_into().unwrap(); + match constrain::<_, 5>(a_bi) { + Ok(lt0) => lt0, + Err(_gt0) => panic!(), + } + } + + fn run_test_4(a: felt252) -> BoundedInt<5, 15> { + let a_bi: BoundedInt<0, 15> = a.try_into().unwrap(); + match constrain::<_, 5>(a_bi) { + Ok(_lt0) => panic!(), + Err(gt0) => gt0, + } + } + + impl ConstrainTest2 of ConstrainHelper, 0> { + type LowT = BoundedInt<-10, -1>; + type HighT = BoundedInt<0, 10>; + } + + fn run_test_5(a: felt252) -> BoundedInt<-10, -1> { + let a_bi: BoundedInt<-10, 10> = a.try_into().unwrap(); + match constrain::<_, 0>(a_bi) { + Ok(lt0) => lt0, + Err(_gt0) => panic!(), + } + } + + fn run_test_6(a: felt252) -> BoundedInt<0, 10> { + let a_bi: BoundedInt<-10, 10> = a.try_into().unwrap(); + match constrain::<_, 0>(a_bi) { + Ok(_lt0) => panic!(), + Err(gt0) => gt0, + } + } + + impl ConstrainTest3 of ConstrainHelper, 31> { + type LowT = BoundedInt<1, 30>; + type HighT = BoundedInt<31, 61>; + } + + fn run_test_7(a: felt252) -> BoundedInt<1, 30> { + let a_bi: BoundedInt<1, 61> = a.try_into().unwrap(); + match constrain::<_, 31>(a_bi) { + Ok(lt0) => lt0, + Err(_gt0) => panic!(), + } + } + + fn run_test_8(a: felt252) -> BoundedInt<31, 61> { + let a_bi: BoundedInt<1, 61> = a.try_into().unwrap(); + match constrain::<_, 31>(a_bi) { + Ok(_lt0) => panic!(), + Err(gt0) => gt0, + } + } + }; + + let result = run_program(&program, "run_test_1", &[Value::Sint8(-1)]).return_value; + assert_constrain_output( + result, + Value::BoundedInt { + value: Felt252::from(-1), + range: Range { + lower: BigInt::from(-128), + upper: BigInt::from(0), + }, + }, + ); + + let result = run_program(&program, "run_test_2", &[Value::Sint8(1)]).return_value; + assert_constrain_output( + result, + Value::BoundedInt { + value: Felt252::from(1), + range: Range { + lower: BigInt::from(0), + upper: BigInt::from(128), + }, + }, + ); + + let result = run_program(&program, "run_test_2", &[Value::Sint8(0)]).return_value; + assert_constrain_output( + result, + Value::BoundedInt { + value: Felt252::from(0), + range: Range { + lower: BigInt::from(0), + upper: BigInt::from(128), + }, + }, + ); + + let result = + run_program(&program, "run_test_3", &[Value::Felt252(Felt252::from(0))]).return_value; + assert_constrain_output( + result, + Value::BoundedInt { + value: Felt252::from(0), + range: Range { + lower: BigInt::from(0), + upper: BigInt::from(5), + }, + }, + ); + + let result = + run_program(&program, "run_test_4", &[Value::Felt252(Felt252::from(15))]).return_value; + assert_constrain_output( + result, + Value::BoundedInt { + value: Felt252::from(15), + range: Range { + lower: BigInt::from(5), + upper: BigInt::from(16), + }, + }, + ); + + // TODO: Check why this fails + // let result = + // run_program(&program, "run_test_5", &[Value::Felt252(Felt252::from(-5))]).return_value; + // assert_constrain_output( + // result, + // Value::BoundedInt { + // value: Felt252::from(-5), + // range: Range { + // lower: BigInt::from(-10), + // upper: BigInt::from(0), + // }, + // }, + // ); + + let result = + run_program(&program, "run_test_6", &[Value::Felt252(Felt252::from(5))]).return_value; + assert_constrain_output( + result, + Value::BoundedInt { + value: Felt252::from(5), + range: Range { + lower: BigInt::from(0), + upper: BigInt::from(11), + }, + }, + ); + + let result = + run_program(&program, "run_test_7", &[Value::Felt252(Felt252::from(30))]).return_value; + assert_constrain_output( + result, + Value::BoundedInt { + value: Felt252::from(30), + range: Range { + lower: BigInt::from(1), + upper: BigInt::from(31), + }, + }, + ); + + // TODO: Check why this fails + // let result = + // run_program(&program, "run_test_8", &[Value::Felt252(Felt252::from(31))]).return_value; + // assert_constrain_output( + // result, + // Value::BoundedInt { + // value: Felt252::from(31), + // range: Range { + // lower: BigInt::from(31), + // upper: BigInt::from(62), + // }, + // }, + // ); + } } diff --git a/test.cairo b/test.cairo new file mode 100644 index 000000000..2eb3541a8 --- /dev/null +++ b/test.cairo @@ -0,0 +1,50 @@ +// #[feature("bounded-int-utils")] +// use core::internal::bounded_int::{BoundedInt, add, AddHelper, is_zero}; + +// use core::zeroable::IsZeroResult; +// use core::option::Option; +// // impl AddWeirdBI of AddHelper, BoundedInt<-2, -1>> { +// // type Result = BoundedInt<-1, 34>; +// // } + +// // #[inline(never)] +// // fn run_test_3( +// // a: felt252, +// // b: felt252, +// // ) -> BoundedInt<-1, 34> { +// // let a: BoundedInt<1, 35> = a.try_into().unwrap(); +// // let b: BoundedInt<-2, -1> = b.try_into().unwrap(); +// // return add(a, b); +// // } + +// #[inline(never)] +// fn test_is_zero() -> Option::>> { +// let a: BoundedInt<-105, 307> = 0; +// match is_zero(a) { +// IsZeroResult::Zero => None, +// IsZeroResult::NonZero(n) => Some(n), +// } +// } + +// fn main() -> Option::>> { +// test_is_zero() +// } +#[feature("bounded-int-utils")] +use core::internal::bounded_int::{self, BoundedInt, constrain, ConstrainHelper, upcast, downcast}; + +impl ConstrainTest2 of ConstrainHelper, 0> { + type LowT = BoundedInt<-10, -1>; + type HighT = BoundedInt<0, 10>; +} + +fn run_test_5(a: felt252) -> BoundedInt<-10, -1> { + let a_bi: BoundedInt<-10, 10> = a.try_into().unwrap(); + match constrain::<_, 0>(a_bi) { + Ok(lt0) => lt0, + Err(_gt0) => panic!(), + } +} + +fn main() -> BoundedInt<-10, -1> { + run_test_5(5) +}