From 8a5c65feefa94696a7ce6f27155688a1a3bee175 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Sat, 1 Aug 2026 14:04:59 +0300 Subject: [PATCH 01/15] added full_add_128; refactored normalize_to_threshold to calculate_normalizer_base_64; made algorithm_d function private --- simf/lib/u128.simf | 127 +++++++++------- simf/u128_test_arithmetic.simf | 89 +++++++----- tests/u128_test_arithmetic.rs | 257 ++++++++++++++++++++++----------- 3 files changed, 297 insertions(+), 176 deletions(-) diff --git a/simf/lib/u128.simf b/simf/lib/u128.simf index 5695081..60bd00e 100644 --- a/simf/lib/u128.simf +++ b/simf/lib/u128.simf @@ -141,7 +141,7 @@ pub fn add_128(a: u128, b: u128) -> (bool, u128) { (carry_high, res) } -/// Adds the 128-bit integer with the 64-bit integer. Returns a tuple of the sum and the carry +/// Adds the 128-bit integer with the 64-bit integer and returns the carry pub fn add_128_64(a: u128, b: u64) -> (bool, u128) { let (a_high, a_low): (u64, u64) = ::into(a); @@ -151,6 +151,18 @@ pub fn add_128_64(a: u128, b: u64) -> (bool, u128) { (carry_high, <(u64, u64)>::into((res_high, res_low))) } +/// Add two integers. Take a carry-in and return a carry-out +pub fn full_add_128(carry_low: bool, a: u128, b: u128) -> (bool, u128) { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + let (carry_low, sum_low): (bool, u64) = jet::full_add_64(carry_low, a_low, b_low); + let (carry_high, sum_high): (bool, u64) = jet::full_add_64(carry_low, a_high, b_high); + + let res: u128 = <(u64, u64)>::into((sum_high, sum_low)); + (carry_high, res) +} + /// Returns the sum of two u128 values wrapped in Some, or None if the result overflows u128 pub fn checked_add_128(a: u128, b: u128) -> Option { let (carry, sum): (bool, u128) = add_128(a, b); @@ -178,6 +190,8 @@ pub fn sub_128(a: u128, b: u128) -> (bool, u128) { (borrow_high, res) } +// todo full_sub_128 + /// Returns the difference of two u128 values wrapped in Some, or None if the result overflows u128 pub fn checked_sub_128(a: u128, b: u128) -> Option { let (borrow, diff): (bool, u128) = sub_128(a, b); @@ -248,26 +262,12 @@ pub fn safe_mul_128(a: u128, b: u128) -> u128 { unwrap(checked_mul_128(a, b)) } -/// Splits the u256 integer into four u64 integers -// TODO: Move to u256 once that module is added. -pub fn split_256_into_64(a: u256) -> ((u64, u64), (u64, u64)) { - let (high, low): (u128, u128) = ::into(a); - - (::into(high), ::into(low)) -} - -/// Helper function, can be used with jet::div_mod_128_64. -/// Normalizes two u128 values by multiplying both by the same factor, -/// ensuring that the most significant non-zero word of `b` is at least 2^63. -/// -/// If `is_b_u128` is true, expects the upper half of `b` to be non-zero. -/// If `is_b_u128` is false, expects `b` to fit into u64. -/// -/// Division algorithms operate in base 2^64, so the normalization threshold is 2^63. -pub fn normalize_to_threshold(a: u128, b: u128, is_b_u128: bool) -> (u256, u128) { - // Compile-time constant: 2^63. Avoids a runtime jet::left_shift_64 call. +/// Helper function that can be used with jet::div_mod_128_64 or Algorithm D. +/// Returns the normalization factor by which `b` should be multiplied so that +/// its most significant non-zero word is greater than or equal to 2^63 +pub fn calculate_normalizer_base_64(b: u128, is_b_u128: bool) -> u64 { + // Compile-time constant: 2^63. Avoids a runtime jet::left_shift_64 call let threshold: u64 = 0x8000000000000000; - let (b_high, b_low): (u64, u64) = ::into(b); let b_highest_word: u64 = match is_b_u128 { @@ -281,45 +281,56 @@ pub fn normalize_to_threshold(a: u128, b: u128, is_b_u128: bool) -> (u256, u128) let (norm, remainder): (u64, u64) = jet::div_mod_64(threshold, b_highest_word); - let norm: u64 = match jet::is_zero_64(remainder) { + match jet::is_zero_64(remainder) { true => norm, false => { let (_, norm): (bool, u64) = jet::add_64(norm, 1); // norm <= 2^63, so norm + 1 can not overflow norm } - }; - let norm: u128 = <(u64, u64)>::into((0, norm)); - - match jet::lt_64(b_highest_word, threshold) { - true => (mul_128(a, norm), safe_mul_128(b, norm)), - false => (<(u128, u128)>::into((0, a)), b), } } -/// Divides the first u128 integer by the second u128 integer, -/// returns the u64 quotient and the u128 remainder. -/// Implements Algorithm D by Donald Knuth. -/// Requires the upper half of the divisor to be non-zero. -pub fn algorithm_d(dividend: u128, divisor: u128) -> (u64, u128) { - let (norm_dividend, norm_divisor): (u256, u128) = normalize_to_threshold(dividend, divisor, true); - - // normalized dividend fits into 192 bits - let ((_, u2), (u1, u0)): ((u64, u64), (u64, u64)) = split_256_into_64(norm_dividend); - let (v1, v0): (u64, u64) = ::into(norm_divisor); +/// Helper function, can be used with jet::div_mod_128_64 or Algorithm D. +/// Normalizes two u128 values by multiplying both by the same factor, +/// ensuring that the most significant non-zero word of `b` is at least 2^63. +/// +/// If `is_b_u128` is true, expects the upper half of `b` to be non-zero. +/// If `is_b_u128` is false, expects `b` to fit into u64. +/// +/// Division algorithms operate in base 2^64, so the normalization threshold is 2^63 +fn normalize_to_threshold_128_63( + a: u128, + b: u128, + is_b_u128: bool +) -> (u256, u128, u64) { + let norm: u64 = calculate_normalizer_base_64(b, is_b_u128); + let norm_128: u128 = <(u64, u64)>::into((0, norm)); + + match jet::eq_64(norm, 1) { + true => (<(u128, u128)>::into((0, a)), b, norm), + false => (mul_128(a, norm_128), safe_mul_128(b, norm_128), norm), + } +} +/// Estimates and corrects the next quotient digit (q_hat) for Algorithm D. +/// Returns the quotient digit to use in the subsequent multiply-and-subtract step. +/// Expects result to fit into u64 +pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u64) -> u64 { let (q_hat, r_hat): (u64, u64) = jet::div_mod_128_64(<(u64, u64)>::into((u2, u1)), v1); let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); // correcting estimation: q_hat is off by at most 2. - let q: u64 = match lt_128(r_hat_u0, jet::multiply_64(q_hat, v0)) { + match lt_128(r_hat_u0, jet::multiply_64(q_hat, v0)) { true => { // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); let (carry, r_hat): (bool, u64) = jet::add_64(r_hat, v1); - + match carry { - true => q_hat, + true => { + q_hat + }, false => { let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); @@ -327,17 +338,34 @@ pub fn algorithm_d(dividend: u128, divisor: u128) -> (u64, u128) { true => { // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); - + q_hat } - false => q_hat, + false => { + q_hat + }, } } - } }, - false => q_hat, - }; + false => { + q_hat + }, + } +} + +/// Divides the first u128 integer by the second u128 integer, +/// returns the u64 quotient and the u128 remainder. +/// Implements Algorithm D by Donald Knuth. +/// Requires the upper half of the divisor to be non-zero. +fn algorithm_d_128_128(dividend: u128, divisor: u128) -> (u64, u128) { + let (norm_dividend, norm_divisor, _): (u256, u128, u64) = normalize_to_threshold_128_63(dividend, divisor, true); + + // normalized dividend fits into 192 bits + let (_, u2, u1, u0): (u64, u64, u64, u64) = ::into(norm_dividend); + let (v1, v0): (u64, u64) = ::into(norm_divisor); + + let q: u64 = estimate_quotient_digit_base_64(u2, u1, u0, v1, v0); let remainder: u128 = safe_sub_128(dividend, safe_mul_128(divisor, <(u64, u64)>::into((0, q)))); (q, remainder) @@ -355,16 +383,15 @@ pub fn div_mod_128_64(a: u128, b: u64) -> (u128, u64) { let a_prime: u128 = <(u64, u64)>::into((remainder, a_low)); // we need to normalize here, because jet::div_mod_128_64 only accepts b >= 2^63 - let (a_normalized, b_normalized): (u256, u128) = normalize_to_threshold(a_prime, <(u64, u64)>::into((0, b)), false); + let (a_normalized, b_normalized, norm): (u256, u128, u64) = normalize_to_threshold_128_63(a_prime, <(u64, u64)>::into((0, b)), false); // a_normalized fits into u128, because remainder < b and b_normalized fits into u64 let (_, a_normalized): (u128, u128) = ::into(a_normalized); let (_, b_normalized): (u64, u64) = ::into(b_normalized); // remainder < b, so (remainder * 2^64 + a_low) / b fits into u64 - let (q_low, _): (u64, u64) = jet::div_mod_128_64(a_normalized, b_normalized); // remainder is not valid here due to normalizing - - let (_, remainder): (u64, u64) = ::into(safe_sub_128(a_prime, jet::multiply_64(q_low, b))); + let (q_low, r_normalized): (u64, u64) = jet::div_mod_128_64(a_normalized, b_normalized); + let remainder: u64 = jet::divide_64(r_normalized, norm); (<(u64, u64)>::into((q_high, q_low)), remainder) } @@ -399,7 +426,7 @@ pub fn div_mod_128(a: u128, b: u128) -> (u128, u128) { (q, <(u64, u64)>::into((0, r))) }, false => { - let (q, r): (u64, u128) = algorithm_d(a, b); + let (q, r): (u64, u128) = algorithm_d_128_128(a, b); (<(u64, u64)>::into((0, q)), r) } } diff --git a/simf/u128_test_arithmetic.simf b/simf/u128_test_arithmetic.simf index a95ee18..193c00e 100644 --- a/simf/u128_test_arithmetic.simf +++ b/simf/u128_test_arithmetic.simf @@ -1,13 +1,35 @@ -use crate::lib::u128::{ eq_128, is_zero_128, lt_128, le_128, add_128, add_128_64, sub_128, mul_128, split_256_into_64, normalize_to_threshold, algorithm_d, div_mod_128_64, div_mod_128, div_128 }; -use crate::helper::{if_test_this_function, assert_bool}; +use crate::lib::u128::{ + eq_128, + is_zero_128, + lt_128, + le_128, + add_128, + add_128_64, + full_add_128, + sub_128, + mul_128, + calculate_normalizer_base_64, + estimate_quotient_digit_base_64, + div_mod_128_64, + div_mod_128, + div_128 +}; +use crate::helper::{ + if_test_this_function, + assert_bool +}; /// Asserts a result equals expected u128 and bool values. /// Used for functions that return carry or borrow bool value. -fn assert_eq_uint_bool(result: (bool, u128), expected: u128, expected_bool: bool) { +fn assert_eq_uint_bool( + result: (bool, u128), + expected: u128, + expected_bool: bool +) { let (bool_res, uint_res): (bool, u128) = result; assert_bool(bool_res, expected_bool); - + assert!(eq_128(uint_res, expected)); } @@ -24,63 +46,50 @@ fn main() { /// Arithmetic - match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_128(a), expected_bool); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_128(a, b), expected_bool); }, false => (), }; - match if_test_this_function(2, fn_idx) { true => { assert_bool(le_128(a, b), expected_bool); }, false => (), }; + match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_128(a), expected_bool); }, false => {}, }; + match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_128(a, b), expected_bool); }, false => {}, }; + match if_test_this_function(2, fn_idx) { true => { assert_bool(le_128(a, b), expected_bool); }, false => {}, }; + + match if_test_this_function(3, fn_idx) { true => { assert_eq_uint_bool(add_128(a, b), unwrap(expected), expected_bool); }, false => {}, }; + match if_test_this_function(4, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); assert_eq_uint_bool(add_128_64(a, b), unwrap(expected), expected_bool); }, false => {}, }; + match if_test_this_function(5, fn_idx) { true => { assert_eq_uint_bool(full_add_128(eq_128(second_expected, 1), a, b), unwrap(expected), expected_bool); }, false => {}, }; - match if_test_this_function(3, fn_idx) { true => { assert_eq_uint_bool(add_128(a, b), unwrap(expected), expected_bool); }, false => (), }; - match if_test_this_function(4, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); assert_eq_uint_bool(add_128_64(a, b), unwrap(expected), expected_bool); }, false => (), }; - match if_test_this_function(5, fn_idx) { true => { assert_eq_uint_bool(sub_128(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(6, fn_idx) { true => { assert_eq_uint_bool(sub_128(a, b), unwrap(expected), expected_bool); }, false => {}, }; - match if_test_this_function(6, fn_idx) { + match if_test_this_function(7, fn_idx) { true => { let result: u256 = mul_128(a, b); let (result_high, result_low): (u128, u128) = ::into(result); - + assert!(eq_128(result_high, unwrap(expected))); assert!(eq_128(result_low, second_expected)); }, - false => (), - }; - - match if_test_this_function(7, fn_idx) { - true => { - let input: u256 = <(u128, u128)>::into((a, b)); - let ((res1, res2), (res3, res4)): ((u64, u64), (u64, u64)) = split_256_into_64(input); - - let (expected1, expected2): (u64, u64) = ::into(unwrap(expected)); - let (expected3, expected4): (u64, u64) = ::into(second_expected); - - assert!(jet::eq_64(res1, expected1)); - assert!(jet::eq_64(res2, expected2)); - assert!(jet::eq_64(res3, expected3)); - assert!(jet::eq_64(res4, expected4)); - }, - false => (), + false => {}, }; match if_test_this_function(8, fn_idx) { true => { - let (result_a, result_b): (u256, u128) = normalize_to_threshold(a, b, expected_bool); + let norm: u64 = calculate_normalizer_base_64(b, expected_bool); - let (result_a_high, result_a_low): (u128, u128) = ::into(result_a); + let (_, expected_norm): (u64, u64) = ::into(unwrap(expected)); - assert!(eq_128(result_a_high, unwrap(expected))); - assert!(eq_128(result_a_low, second_expected)); - assert!(eq_128(result_b, third_expected)); + assert!(jet::eq_64(norm, expected_norm)); }, false => (), }; match if_test_this_function(9, fn_idx) { true => { - let (_, expected_q): (u64, u64) = ::into(unwrap(expected)); + let (_, u2): (u64, u64) = ::into(a); + let (u1, u0): (u64, u64) = ::into(b); + let (v1, v0): (u64, u64) = ::into(second_expected); + + let q: u64 = estimate_quotient_digit_base_64(u2, u1, u0, v1, v0); - let (q, r): (u64, u128) = algorithm_d(a, b); + let (_, expected_q): (u64, u64) = ::into(unwrap(expected)); assert!(jet::eq_64(q, expected_q)); - assert!(eq_128(r, second_expected)); }, false => (), }; @@ -95,7 +104,7 @@ fn main() { assert!(eq_128(q, unwrap(expected))); assert!(jet::eq_64(r, expected_r)); }, - false => (), + false => {}, }; match if_test_this_function(11, fn_idx) { @@ -105,8 +114,8 @@ fn main() { assert!(eq_128(q, unwrap(expected))); assert!(eq_128(r, second_expected)); }, - false => (), + false => {}, }; - match if_test_this_function(12, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(12, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => {}, }; } diff --git a/tests/u128_test_arithmetic.rs b/tests/u128_test_arithmetic.rs index 7b72ea9..2180960 100644 --- a/tests/u128_test_arithmetic.rs +++ b/tests/u128_test_arithmetic.rs @@ -16,11 +16,11 @@ enum FunctionToTest { Le128, Add128, Add128_64, + FullAdd128, Sub128, Mul128, - Split256Into64, - NormalizeToThreshold, - AlgorithmD, + CalculateNormalizerBase64, + EstimateQuotientDigitBase64, DivMod128_64, DivMod128, Div128, @@ -322,6 +322,110 @@ mod u128_tests_arithmetic { ) } + #[simplex::test] + fn u128_test_full_add_128_not_overflow_carry_low_false( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX / 2); + let b = rand::thread_rng().gen_range(0..=u128::MAX / 2); + let result = a + b; + let result_carry = false; + let carry_low = 0_u128; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd128), + a, + b, + Some(result), + result_carry, + carry_low, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_full_add_128_overflow_carry_low_false( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = u128::MAX; + let b = rand::thread_rng().gen_range(1..=u128::MAX); + let result = b - 1; + let result_carry = true; + let carry_low = 0_u128; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd128), + a, + b, + Some(result), + result_carry, + carry_low, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_full_add_128_not_overflow_carry_low_true( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX / 2); + let b = rand::thread_rng().gen_range(0..=u128::MAX / 2); + let result = a + b + 1; + let result_carry = false; + let carry_low = 1_u128; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd128), + a, + b, + Some(result), + result_carry, + carry_low, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_full_add_128_overflow_carry_low_true( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = u128::MAX; + let b = rand::thread_rng().gen_range(1..=u128::MAX); + let result = b; + let result_carry = true; + let carry_low = 1_u128; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd128), + a, + b, + Some(result), + result_carry, + carry_low, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + #[simplex::test] fn u128_test_sub_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u128::MAX); @@ -488,128 +592,94 @@ mod u128_tests_arithmetic { } #[simplex::test] - fn u128_test_split_256_into_64(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); - let b = rand::thread_rng().gen_range(0..=u128::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Split256Into64), - a, - b, - Some(a), - DEFAULT_BOOL, - b, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_u64( + fn u128_test_calculate_normalizer_base_64_b_is_u64( context: simplex::TestContext, ) -> anyhow::Result<()> { let threshold = 1u128 << 63; - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = rand::thread_rng().gen_range(1..threshold); let norm: u128 = threshold.div_ceil(b); - let result_a = U256::from(a) * U256::from(norm); - let result_b = b * norm; - let (result_a_high, result_a_low) = split_helper(result_a); - run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, - Some(result_a_high), + Some(norm), false, - result_a_low, - result_b, + DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_big_enough_not_normalize( + fn u128_test_calculate_normalizer_base_64_b_is_big_enough_not_normalize( context: simplex::TestContext, ) -> anyhow::Result<()> { let threshold = 1u128 << 63; - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = rand::thread_rng().gen_range(threshold..=u64::MAX as u128); run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, - Some(0), + Some(1), false, - a, - b, + DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_u128( + fn u128_test_calculate_normalizer_base_64_b_is_u128( context: simplex::TestContext, ) -> anyhow::Result<()> { let threshold = 1u128 << 63; - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = rand::thread_rng().gen_range((u64::MAX as u128) + 1..=u128::MAX); - let b_high = b >> 64; let norm: u128 = threshold.div_ceil(b_high); - let result_a = U256::from(a) * U256::from(norm); - let result_b = b * norm; - let (result_a_high, result_a_low) = split_helper(result_a); - run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, - Some(result_a_high), + Some(norm), true, - result_a_low, - result_b, + DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_u64_fail( + fn u128_test_calculate_normalizer_base_64_b_is_u64_fail( context: simplex::TestContext, ) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = rand::thread_rng().gen_range((u64::MAX as u128) + 1..=u128::MAX); run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, Some(DEFAULT_EXPECTED), false, @@ -621,18 +691,17 @@ mod u128_tests_arithmetic { } #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_u128_fail( + fn u128_test_calculate_normalizer_base_64_b_is_u128_fail( context: simplex::TestContext, ) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = rand::thread_rng().gen_range(1..=u64::MAX as u128); run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, Some(DEFAULT_EXPECTED), true, @@ -644,18 +713,17 @@ mod u128_tests_arithmetic { } #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_zero_fail( + fn u128_test_calculate_normalizer_base_64_b_is_zero_fail( context: simplex::TestContext, ) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = 0; run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, Some(DEFAULT_EXPECTED), false, @@ -667,24 +735,32 @@ mod u128_tests_arithmetic { } #[simplex::test] - fn u128_test_algorithm_d(context: simplex::TestContext) -> anyhow::Result<()> { - // divisor is expected to be greater than or equal to 2^64 - let b = rand::thread_rng().gen_range((u64::MAX as u128) + 1..u128::MAX); - let a = rand::thread_rng().gen_range(b..=u128::MAX); + fn u128_test_estimate_quotient_digit_base_64( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let threshold = 1u64 << 63; - let q = a / b; - let r = a % b; + let b_high = rand::thread_rng().gen_range(threshold..=u64::MAX); + let b_low = rand::thread_rng().gen_range(0..=u64::MAX); + + let a_high = rand::thread_rng().gen_range(0..b_high); + let a_low = rand::thread_rng().gen_range(0..=u128::MAX); + + let a = ((U256::from(a_high)) << 128) | (U256::from(a_low)); + let b = ((b_high as u128) << 64) | (b_low as u128); + + let q = (a / b).as_u128(); run( &context, program(), build_witness( - op(FunctionToTest::AlgorithmD), - a, - b, + op(FunctionToTest::EstimateQuotientDigitBase64), + a_high as u128, + a_low, Some(q), DEFAULT_BOOL, - r, + b, DEFAULT_EXPECTED, ), Expect::Ok, @@ -692,24 +768,33 @@ mod u128_tests_arithmetic { } #[simplex::test] - fn u128_test_algorithm_d_fail(context: simplex::TestContext) -> anyhow::Result<()> { - // expected to fail because divisor is less than 2^64 - let b = rand::thread_rng().gen_range(1..=u64::MAX as u128); - let a = rand::thread_rng().gen_range(b..=u128::MAX); + fn u128_test_estimate_quotient_digit_base_64_fail( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + // expected to fail because a is to big for q to fit unto u64 + let threshold = 1u64 << 63; - let q = a / b; - let r = a % b; + let b_high = rand::thread_rng().gen_range(threshold..u64::MAX); + let b_low = rand::thread_rng().gen_range(0..=u64::MAX); + + let a_high = rand::thread_rng().gen_range(b_high..=u64::MAX); + let a_low = rand::thread_rng().gen_range(0..=u128::MAX); + + let a = ((U256::from(a_high)) << 128) | (U256::from(a_low)); + let b = ((b_high as u128) << 64) | (b_low as u128); + + let q = (a / b).as_u128(); run( &context, program(), build_witness( - op(FunctionToTest::AlgorithmD), - a, - b, + op(FunctionToTest::EstimateQuotientDigitBase64), + a_high as u128, + a_low, Some(q), DEFAULT_BOOL, - r, + b, DEFAULT_EXPECTED, ), Expect::AssertFailed, From 3f9910c9368087f53e2efb3b2c1bfff897604dec Mon Sep 17 00:00:00 2001 From: aritkulova Date: Sat, 1 Aug 2026 16:35:13 +0300 Subject: [PATCH 02/15] added functions for uint256 --- simf/lib/u256.simf | 633 +++++++++++++++++++++++++++++++++ simf/u256_test.simf | 65 ++++ simf/u256_test_arithmetic.simf | 121 +++++++ simf/u256_test_bits.simf | 36 ++ 4 files changed, 855 insertions(+) create mode 100644 simf/lib/u256.simf create mode 100644 simf/u256_test.simf create mode 100644 simf/u256_test_arithmetic.simf create mode 100644 simf/u256_test_bits.simf diff --git a/simf/lib/u256.simf b/simf/lib/u256.simf new file mode 100644 index 0000000..4f22fc6 --- /dev/null +++ b/simf/lib/u256.simf @@ -0,0 +1,633 @@ +use crate::lib::binary::{not, or, and}; +use crate::lib::u128::{ + and_128, + or_128, + eq_128, + left_shift_128, + right_shift_128, + is_zero_128, + lt_128, + le_128, + add_128, + full_add_128, + sub_128, + mul_128, + safe_add_128, + calculate_normalizer_base_64, + estimate_quotient_digit_base_64, + div_mod_128, + div_mod_128_64 +}; +use crate::lib::u64::{ + u64_into_u256 +}; + +/// Bit logic + +/// Bitwise AND of two 256-bit values +pub fn and_256(a: u256, b: u256) -> u256 { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + <(u128, u128)>::into((and_128(a_high, b_high), and_128(a_low, b_low))) +} + +/// Bitwise OR of two 256-bit values +pub fn or_256(a: u256, b: u256) -> u256 { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + <(u128, u128)>::into((or_128(a_high, b_high), or_128(a_low, b_low))) +} + +/// Checks if two 256-bit values are equal +pub fn eq_256(a: u256, b: u256) -> bool { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + and(eq_128(a_high, b_high), eq_128(a_low, b_low)) +} + +/// Left-shift a 256-bit value by the given amount. Bits are filled with zeroes +pub fn left_shift_256(shift: u8, a: u256) -> u256 { + match jet::is_zero_8(shift) { + true => a, + false => { + let (a_high, a_low): (u128, u128) = ::into(a); + + match jet::lt_8(shift, 128) { + true => { + let (_, low_to_high_amount): (bool, u8) = jet::subtract_8(128, shift); // shift < 128 + let shifted_bits: u128 = right_shift_128(low_to_high_amount, a_low); + + let res_high: u128 = or_128(left_shift_128(shift, a_high), shifted_bits); + + <(u128, u128)>::into((res_high, left_shift_128(shift, a_low))) + }, + false => { + let (_, shift): (bool, u8) = jet::subtract_8(shift, 128); // shift >= 128 + + <(u128, u128)>::into((left_shift_128(shift, a_low), 0)) + } + } + } + } +} + +/// Right-shift a 256-bit value by the given amount. Bits are filled with zeroes +pub fn right_shift_256(shift: u8, a: u256) -> u256 { + match jet::is_zero_8(shift) { + true => { + a + }, + false => { + let (a_high, a_low): (u128, u128) = ::into(a); + + match jet::lt_8(shift, 128) { + true => { + let (_, high_to_low_amount): (bool, u8) = jet::subtract_8(128, shift); // shift < 128 + let shifted_bits: u128 = left_shift_128(high_to_low_amount, a_high); + + let res_low: u128 = or_128(right_shift_128(shift, a_low), shifted_bits); + + <(u128, u128)>::into((right_shift_128(shift, a_high), res_low)) + }, + false => { + let (_, shift): (bool, u8) = jet::subtract_8(shift, 128); // shift >= 128 + + <(u128, u128)>::into((0, right_shift_128(shift, a_high))) + } + } + } + } +} + +/// Arithmetic + +/// Checks if an integer is zero +pub fn is_zero_256( + a: u256 +) -> bool { + let (a_high, a_low): (u128, u128) = ::into(a); + + and(is_zero_128(a_high), is_zero_128(a_low)) +} + +/// Checks if an integer is less than another integer +pub fn lt_256(a: u256, b: u256) -> bool { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + match lt_128(a_high, b_high) { + false => { + match eq_128(a_high, b_high) { + false => { + false + }, + true => { + lt_128(a_low, b_low) + } + } + }, + true => { + true + } + } +} + +/// Checks if an integer is less than or equal to another integer +pub fn le_256( + a: u256, + b: u256 +) -> bool { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + match lt_128(a_high, b_high) { + false => { + match eq_128(a_high, b_high) { + false => { + false + }, + true => { + le_128(a_low, b_low) + } + } + }, + true => { + true + } + } +} + +/// Check if an integer is greater than another integer +pub fn gt_256(a: u256, b: u256) -> bool { + lt_256(b, a) +} + +/// Check if an integer is greater than or equal to another integer +pub fn ge_256( + a: u256, + b: u256 +) -> bool { + le_256(b, a) +} + +/// Splits the u256 integer into four u64 integers +pub fn split_256_into_64( + a: u256 +) -> ((u64, u64), (u64, u64)) { + let (high, low): (u128, u128) = ::into(a); + + (::into(high), ::into(low)) +} + +/// Adds two integers and returns the carry +pub fn add_256(a: u256, b: u256) -> (bool, u256) { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + let (carry_low, sum_low): (bool, u128) = add_128(a_low, b_low); + let (carry_high, sum_high): (bool, u128) = full_add_128(carry_low, a_high, b_high); + + let res: u256 = <(u128, u128)>::into((sum_high, sum_low)); + (carry_high, res) +} + +/// Adds the 128-bit integer with the 64-bit integer and returns the carry +pub fn add_256_128( + a: u256, + b: u128 +) -> (bool, u256) { + let b: u256 = <(u128, u128)>::into((0, b)); + + add_256(a, b) +} + +/// Returns the sum of two u256 values wrapped in Some, or None if the result overflows u256 +pub fn checked_add_256( + a: u256, + b: u256 +) -> Option { + let (carry, sum): (bool, u256) = add_256(a, b); + + match carry { + false => { + Some(sum) + }, + true => { + None + } + } +} + +/// Returns the sum of two u256 values, panics if the result overflows u256 +pub fn safe_add_256( + a: u256, + b: u256 +) -> u256 { + unwrap(checked_add_256(a, b)) +} + +/// Subtracts the second integer from the first integer, and returns the borrow bit +pub fn sub_256( + a: u256, + b: u256 +) -> (bool, u256) { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + let (borrow_high, diff_high): (bool, u128) = sub_128(a_high, b_high); + let (borrow_low, diff_low): (bool, u128) = sub_128(a_low, b_low); + + match borrow_low { + false => { + let res: u256 = <(u128, u128)>::into((diff_high, diff_low)); + + (borrow_high, res) + }, + true => { + let (borrow, diff_high): (bool, u128) = sub_128(diff_high, 1); + + let final_borrow: bool = or(borrow, borrow_high); + let res: u256 = <(u128, u128)>::into((diff_high, diff_low)); + + (final_borrow, res) + } + } +} + +/// Returns the difference of two u256 values wrapped in Some, or None if the result overflows u256 +pub fn checked_sub_256( + a: u256, + b: u256 +) -> Option { + let (borrow, diff): (bool, u256) = sub_256(a, b); + + match borrow { + false => { + Some(diff) + }, + true => { + None + } + } +} + +/// Returns the difference of two u256 values, panics if the result overflows u256 +pub fn safe_sub_256( + a: u256, + b: u256 +) -> u256 { + unwrap(checked_sub_256(a, b)) +} + +/// Multiply two integers. The output is two 256-bit integers +/// The idea is that u256-bit `a` divides into 128-bit `a_high` and `a_low`, +/// so a = a_high * 2^128 + a_low. +/// In the same way, b = b_high * 2^128 + b_low. +/// Therefore, a * b = 2^256 * a_high * b_high + 2^128(a_high * b_low + a_low * b_high) + a_low * b_low. +pub fn mul_256(a: u256, b: u256) -> (u256, u256) { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + let highest: u256 = mul_128(a_high, b_high); + let lowest: u256 = mul_128(a_low, b_low); + let (word_1, word_0): (u128, u128) = ::into(lowest); + let (word_3, word_2): (u128, u128) = ::into(highest); + + let product_1: u256 = mul_128(a_high, b_low); + let product_2: u256 = mul_128(b_high, a_low); + let (carry_3a, middle): (bool, u256) = add_256(product_1, product_2); + + let (middle_2, middle_1): (u128, u128) = ::into(middle); + + // fold the low-side carry directly into the word_2 addition via full_add_128, + // then propagate any resulting carry into word_3 + let (carry_2, res_1): (bool, u128) = add_128(word_1, middle_1); + let (carry_3b, res_2): (bool, u128) = full_add_128(carry_2, word_2, middle_2); + + // `word_3` is the upper half of a_high * b_high. It is at most `u128::MAX - 1` when + // either factor is `u128::MAX`, and even in the extreme case where + // a == b == u256::MAX, the total product still fits into two u256. + // Therefore, word_3 + carry_3a + carry_3b can not overflow, and `add_128` + // is used instead of `safe_add_128` to avoid the unnecessary overflow check + let (_, res_3a): (bool, u128) = full_add_128(carry_3a, word_3, 0); + let (_, res_3): (bool, u128) = full_add_128(carry_3b, res_3a, 0); + + let res_1_0: u256 = <(u128, u128)>::into((res_1, word_0)); + let res_3_2: u256 = <(u128, u128)>::into((res_3, res_2)); + + (res_3_2, res_1_0) +} + +/// Returns the product of two u256 values wrapped in Some, or None if the result overflows u256 +pub fn checked_mul_256( + a: u256, + b: u256 +) -> Option { + let (result_high, result_low): (u256, u256) = mul_256(a, b); + + match is_zero_256(result_high) { + false => { + None + }, + true => { + Some(result_low) + } + } +} + +/// Returns the product of two u256 values, panics if the result overflows u256 +pub fn safe_mul_256( + a: u256, + b: u256 +) -> u256 { + unwrap(checked_mul_256(a, b)) +} + +/// Normalizes the dividend and divisor for Algorithm D by multiplying +/// both u256 and u128 by the same factor, ensuring that +/// the most significant non-zero word of `b` is at least 2^63. +fn normalize_to_threshold_256_63(a: u256, b: u128, is_b_u128: bool) -> (u256, u256, u128, u64) { + let norm: u64 = calculate_normalizer_base_64(b, is_b_u128); + let norm_128: u128 = <(u64, u64)>::into((0, norm)); + + match jet::eq_64(norm, 1) { + true => (0, a, b, norm), + false => { + let (high, low): (u256, u256) = mul_256(a, <(u128, u128)>::into((0, norm_128))); + let b_norm: u256 = mul_128(b, norm_128); + let( _, b_norm): (u128, u128) = ::into(b_norm); + + (high, low, b_norm, norm) + }, + } +} + +/// Normalizes the dividend and divisor for Algorithm D by multiplying +/// both u256 values by the same factor, ensuring that +/// the most significant non-zero word of `b` is at least 2^127. +fn normalize_to_threshold_256_127(a: u256, b: u256, is_b_u256: bool) -> (u256, u256, u256) { + // Compile-time constant: 2^127 + let threshold: u128 = 0x80000000000000000000000000000000; + + let (b_high, b_low): (u128, u128) = ::into(b); + + let b_highest_word: u128 = match is_b_u256 { + true => b_high, + false => { + assert!(is_zero_128(b_high)); + b_low + } + }; + assert!(not(is_zero_128(b_highest_word))); + + let (norm, remainder): (u128, u128) = div_mod_128(threshold, b_highest_word); + + let norm: u128 = match is_zero_128(remainder) { + true => { + norm + }, + false => { + let (_, norm): (bool, u128) = add_128(norm, 1); // norm <= 2^127, so norm + 1 can not overflow + norm + } + }; + let norm: u256 = <(u128, u128)>::into((0, norm)); + + match lt_128(b_highest_word, threshold) { + true => { + let (high, low): (u256, u256) = mul_256(a, norm); + + (high, low, safe_mul_256(b, norm)) + }, + false => { + (0, a, b) + }, + } +} + +/// Divides the 256-bit integer by the 64-bit integer, +/// returns a tuple of the u256 quotient and the u64 remainder +pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { + let ((u3, u2), (u1, u0)): ((u64, u64), (u64, u64)) = split_256_into_64(dividend); + + // calculate the upper part of the quotient + let (q3, remainder): (u64, u64) = jet::div_mod_64(u3, divisor); + + let divisor: u128 = <(u64, u64)>::into((0, divisor)); + let dividend: u256 = <(u64, u64, u64, u64)>::into((remainder, u2, u1, u0)); + + let (norm_dividend_high, norm_dividend_low, norm_divisor, norm): (u256, u256, u128, u64) = + normalize_to_threshold_256_63(dividend, divisor, false); + + // normalized dividend fits into 256 bits + let ((u3, u2), (u1, u0)): ((u64, u64), (u64, u64)) = split_256_into_64(norm_dividend_low); + let (_, v0): (u64, u64) = ::into(norm_divisor); + + let (q2, remainder): (u64, u64) = jet::div_mod_128_64( <(u64, u64)>::into((u3, u2)), v0); + let (q1, remainder): (u64, u64) = jet::div_mod_128_64( <(u64, u64)>::into((remainder, u1)), v0); + let (q0, remainder): (u64, u64) = jet::div_mod_128_64( <(u64, u64)>::into((remainder, u0)), v0); + + let remainder: u64 = jet::divide_64(remainder, norm); + let q: u256 = <(u64, u64, u64, u64)>::into(( q3, q2, q1, q0)); + + (q, remainder) +} + +/// Multiplies the divisor by the quotient digit and subtracts the result from +/// the corresponding dividend limbs. Returns the updated dividend segment, +/// which is used by Algorithm D +fn mul_and_sub(q: u64, u2: u64, u1: u64, u0: u64, v: u128) -> (u64, u64) { + let u: u256 = <(u64, u64, u64, u64)>::into((0, u2, u1, u0)); + + let q_v: u256 = mul_128(<(u64, u64)>::into((0, q)), v); + + let u_updated: u256 = safe_sub_256(u, q_v); + let ((_, _), (u1, u0)): ((u64, u64), (u64, u64)) = split_256_into_64(u_updated); + + (u1, u0) +} + +/// Divides the 256-bit integer by the 128-bit integer, +/// returns a tuple of the u256 quotient and the u128 remainder. +/// Implements Algorithm D by Donald Knuth. +/// Requires the upper half of the divisor to be non-zero (divisor >= 2^64) +fn algorithm_d_256_128(dividend: u256, divisor: u128) -> (u256, u128) { + let (norm_dividend_high, norm_dividend_low, norm_divisor, norm): (u256, u256, u128, u64) = + normalize_to_threshold_256_63(dividend, divisor, true); + + // normalized dividend fits into 320 bits + let ((_, _), (_, u4)): ((u64, u64), (u64, u64)) = split_256_into_64(norm_dividend_high); + let ((u3, u2), (u1, u0)): ((u64, u64), (u64, u64)) = split_256_into_64(norm_dividend_low); + let (v1, v0): (u64, u64) = ::into(norm_divisor); + + let q2: u64 = estimate_quotient_digit_base_64(u4, u3, u2, v1, v0); + let (u3, u2): (u64, u64) = mul_and_sub(q2, u4, u3, u2, norm_divisor); + + let q1: u64 = estimate_quotient_digit_base_64(u3, u2, u1, v1, v0); + let (u2, u1): (u64, u64) = mul_and_sub(q1, u3, u2, u1, norm_divisor); + + let q0: u64 = estimate_quotient_digit_base_64(u2, u1, u0, v1, v0); + let (u1, u0): (u64, u64) = mul_and_sub(q0, u2, u1, u0, norm_divisor); + + let q: u256 = <(u64, u64, u64, u64)>::into((0, q2, q1, q0)); + let (remainder, _): (u128, u64) = div_mod_128_64(<(u64, u64)>::into((u1, u0)), norm); + + (q, remainder) +} + +/// Divides the 256-bit integer by the 128-bit integer, +/// returns a tuple of the u256 quotient and the u128 remainder +pub fn div_mod_256_128( + dividend: u256, + divisor: u128 +) -> (u256, u128) { + let (divisor_high, divisor_low): (u64, u64) = ::into(divisor); + + match jet::is_zero_64(divisor_high) { + false => { + algorithm_d_256_128(dividend, divisor) + }, + true => { + let (q, r): (u256, u64) = div_mod_256_64(dividend, divisor_low); + + (q, <(u64, u64)>::into((0, r))) + } + } +} + +/// Divides the first u256 integer by the second u256 integer, +/// returns the u128 quotient and the u256 remainder. +/// Implements Algorithm D by Donald Knuth. +/// Requires the upper half of the divisor to be non-zero (divisor >= 2^128). +fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { + let (norm_dividend_high, norm_dividend_low, norm_divisor): (u256, u256, u256) = normalize_to_threshold_256_127(dividend, divisor, true); + + // normalized dividend fits into 384 bits + let (_, u2): (u128, u128) = ::into(norm_dividend_high); + let (u1, u0): (u128, u128) = ::into(norm_dividend_low); + let (v1, v0): (u128, u128) = ::into(norm_divisor); + + let (q_hat, r_hat): (u256, u128) = algorithm_d_256_128(<(u128, u128)>::into((u2, u1)), v1); + let( _, q_hat): (u128, u128) = ::into(q_hat); + + let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); + let u_hat: u256 = mul_128(q_hat, v0); + + // correcting estimation: q_hat is off by at most 2. + let q: u128 = match lt_256(r_hat_u0, u_hat) { + true => { + // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 + let (_, q_hat): (bool, u128) = sub_128(q_hat, 1); + let (carry, r_hat): (bool, u128) = add_128(r_hat, v1); + + match carry { + true => { + q_hat + }, + false => { + let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); + let (_, u_hat): (bool, u256) = sub_256(u_hat, <(u128, u128)>::into((0, v0))); + + match lt_256(r_hat_u0, u_hat) { + true => { + // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 + let (_, q_hat): (bool, u128) = sub_128(q_hat, 1); + + q_hat + } + false => { + q_hat + }, + } + } + + } + }, + false => { + q_hat + }, + }; + + let remainder: u256 = safe_sub_256(dividend, safe_mul_256(divisor, <(u128, u128)>::into((0, q)))); + (q, remainder) +} + +/// Divides the first integer by the second integer, +/// returns the quotient and the remainder +pub fn div_mod_256(a: u256, b: u256) -> (u256, u256) { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + match lt_256(a, b) { + true => { + (0, a) + }, + false => { + match and(is_zero_128(a_high), is_zero_128(b_high)) { + true => { + // if both a_high and b_high are zero, this narrows down to 128-bit division + let (q, r): (u128, u128) = div_mod_128(a_low, b_low); + (<(u128, u128)>::into((0, q)), <(u128, u128)>::into((0, r))) + }, + false => { + match eq_128(a_high, b_high) { + true => { + // safe: !lt_256(a, b) and a_high == b_high, so a_low >= b_low, + // and the subtraction can not underflow + let (_, diff): (bool, u128) = sub_128(a_low, b_low); + (1, <(u128, u128)>::into((0, diff))) + }, + false => { + match is_zero_128(b_high) { + true => { + let (q, r): (u256, u128) = div_mod_256_128(a, b_low); + (q, <(u128, u128)>::into((0, r))) + }, + false => { + let (q, r): (u128, u256) = algorithm_d_256_256(a, b); + (<(u128, u128)>::into((0, q)), r) + } + } + } + } + } + } + } + } +} + +/// Divide the first integer by the second integer, returns the quotient +pub fn div_256( + a: u256, + b: u256 +) -> u256 { + let (q, _): (u256, u256) = div_mod_256(a, b); + + q +} + +/// Returns the quotient of two u256 values wrapped in Some, or None if the result overflows u256 +pub fn checked_div_256( + a: u256, + b: u256 +) -> Option { + match is_zero_256(b) { + false => { + Some(div_256(a, b)) + }, + true => { + None + } + } +} + +/// Returns the quotient of two u256 values, panics if the result overflows u256 +pub fn safe_div_256( + a: u256, + b: u256 +) -> u256 { + unwrap(checked_div_256(a, b)) +} diff --git a/simf/u256_test.simf b/simf/u256_test.simf new file mode 100644 index 0000000..9725a48 --- /dev/null +++ b/simf/u256_test.simf @@ -0,0 +1,65 @@ +use crate::lib::u256::{ + checked_add_256, + safe_add_256, + checked_sub_256, + safe_sub_256, + checked_mul_256, + safe_mul_256, + checked_div_256, + safe_div_256, + eq_256, + gt_256, + ge_256 +}; +use crate::lib::asserts::{assert_none_256, assert_eq_256}; +use crate::lib::binary::not; +use crate::helper::if_test_this_function; + +/// Asserts a `checked_*` result equals the expected Option. +/// `None` encodes the overflow case, `Some(e)` the fitting case, so a single +/// witness value carries both, removing the need for a separate overflow flag. +fn assert_eq_opt(result: Option, expected: Option) { + match expected { + None => assert_none_256(result), + Some(e: u256) => assert_eq_256(unwrap(result), e), + } +} + +/// Asserts a result equals the expected bool value. +/// `None` encodes `false`, and `Some(_)` encodes `true`. +fn assert_bool_by_opt(result: bool, expected: Option) { + match expected { + Some(_: u256) => assert!(result), + None => assert!(not(result)), + } +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + /// Safe functions + + // add + match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_256(a, b), expected); }, false => {}, }; + match if_test_this_function(1, fn_idx) { true => { assert!(eq_256(safe_add_256(a, b), unwrap(expected))); }, false => {}, }; + + // sub + match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_256(a, b), expected); }, false => {}, }; + match if_test_this_function(3, fn_idx) { true => { assert!(eq_256(safe_sub_256(a, b), unwrap(expected))); }, false => {}, }; + + // mul + match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_256(a, b), expected); }, false => {}, }; + match if_test_this_function(5, fn_idx) { true => { assert!(eq_256(safe_mul_256(a, b), unwrap(expected))); }, false => {}, }; + + // div + match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_256(a, b), expected); }, false => {}, }; + match if_test_this_function(7, fn_idx) { true => { assert!(eq_256(safe_div_256(a, b), unwrap(expected))); }, false => {}, }; + + // gt, ge + match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_256(a, b), expected); }, false => {}, }; + match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_256(a, b), expected); }, false => {}, }; +} diff --git a/simf/u256_test_arithmetic.simf b/simf/u256_test_arithmetic.simf new file mode 100644 index 0000000..aa25b60 --- /dev/null +++ b/simf/u256_test_arithmetic.simf @@ -0,0 +1,121 @@ +use crate::lib::u256::{ + eq_256, + is_zero_256, + lt_256, + le_256, + split_256_into_64, + add_256, + add_256_128, + sub_256, + mul_256, + div_mod_256_64, + div_mod_256_128, + div_mod_256, + div_256 +}; +use crate::lib::u128::{eq_128}; +use crate::helper::{ + if_test_this_function, + assert_bool +}; + +/// Asserts a result equals expected u256 and bool values. +/// Used for functions that return carry or borrow bool value. +fn assert_eq_uint_bool( + result: (bool, u256), + expected: u256, + expected_bool: bool +) { + let (bool_res, uint_res): (bool, u256) = result; + + assert_bool(bool_res, expected_bool); + + assert!(eq_256(uint_res, expected)); +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + let expected_bool: bool = witness::EXPECTED_BOOL; + let second_expected: u256 = witness::SECOND_EXPECTED; + + /// Arithmetic + + match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_256(a), expected_bool); }, false => {}, }; + match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_256(a, b), expected_bool); }, false => {}, }; + match if_test_this_function(2, fn_idx) { true => { assert_bool(le_256(a, b), expected_bool); }, false => {}, }; + + match if_test_this_function(3, fn_idx) { + true => { + let ((res1, res2), (res3, res4)): ((u64, u64), (u64, u64)) = split_256_into_64(a); + + let (high, low): (u128, u128) = ::into(unwrap(expected)); + + let (expected1, expected2): (u64, u64) = ::into(high); + let (expected3, expected4): (u64, u64) = ::into(low); + + assert!(jet::eq_64(res1, expected1)); + assert!(jet::eq_64(res2, expected2)); + assert!(jet::eq_64(res3, expected3)); + assert!(jet::eq_64(res4, expected4)); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { true => { assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; + match if_test_this_function(5, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); assert_eq_uint_bool(add_256_128(a, b), unwrap(expected), expected_bool); }, false => {}, }; + + match if_test_this_function(6, fn_idx) { true => { assert_eq_uint_bool(sub_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; + + match if_test_this_function(7, fn_idx) { + true => { + let (result_high, result_low): (u256,u256) = mul_256(a, b); + + assert!(eq_256(result_high, unwrap(expected))); + assert!(eq_256(result_low, second_expected)); + }, + false => {}, + }; + + match if_test_this_function(8, fn_idx) { + true => { + let ((_, _), (_, b)): ((u64, u64), (u64, u64)) = split_256_into_64(b); + let ((_, _), (_, expected_r)): ((u64, u64), (u64, u64)) = split_256_into_64(second_expected); + + let (q, r): (u256, u64) = div_mod_256_64(a, b); + + assert!(eq_256(q, unwrap(expected))); + assert!(jet::eq_64(r, expected_r)); + }, + false => {}, + }; + + match if_test_this_function(9, fn_idx) { + true => { + let (_, b): (u128, u128) = ::into(b); + let (_, expected_r): (u128, u128) = ::into(second_expected); + + let (q, r): (u256, u128) = div_mod_256_128(a, b); + + assert!(eq_256(q, unwrap(expected))); + assert!(eq_128(r, expected_r)); + }, + false => {}, + }; + + match if_test_this_function(10, fn_idx) { + true => { + let (q, r): (u256, u256) = div_mod_256(a, b); + + assert!(eq_256(q, unwrap(expected))); + assert!(eq_256(r, second_expected)); + }, + false => {}, + }; + + match if_test_this_function(11, fn_idx) { true => { assert!(eq_256(div_256(a, b), unwrap(expected))); }, false => {}, }; +} diff --git a/simf/u256_test_bits.simf b/simf/u256_test_bits.simf new file mode 100644 index 0000000..9ff1490 --- /dev/null +++ b/simf/u256_test_bits.simf @@ -0,0 +1,36 @@ +use crate::lib::u256::{and_256, or_256, eq_256, left_shift_256, right_shift_256, split_256_into_64}; +use crate::helper::{if_test_this_function, assert_bool}; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + let expected_bool: bool = witness::EXPECTED_BOOL; + + /// Bit logic + + match if_test_this_function(0, fn_idx) { true => { assert!(eq_256(and_256(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(1, fn_idx) { true => { assert!(eq_256(or_256(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(2, fn_idx) { true => { assert_bool(eq_256(a, b), expected_bool); }, false => {}, }; + + match if_test_this_function(3, fn_idx) { + true => { + let ((_, _,), (_, a)): ((u64, u64), (u64, u64)) = split_256_into_64(a); + let shift: u8 = jet::rightmost_64_8(a); + + assert!(eq_256(left_shift_256(shift, b), unwrap(expected))); + }, false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let ((_, _,), (_, a)): ((u64, u64), (u64, u64)) = split_256_into_64(a); + let shift: u8 = jet::rightmost_64_8(a); + + assert!(eq_256(right_shift_256(shift, b), unwrap(expected))); + }, false => {}, + }; +} From cb05ec96e3afa1334c2bc9e098040ebe6bc7a8f8 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Sat, 1 Aug 2026 16:48:58 +0300 Subject: [PATCH 03/15] added U256Wrapper for u256 tests; added tests for u256 --- tests/common/helper.rs | 33 ++ tests/common/mod.rs | 2 + tests/common/u256_wrapper.rs | 149 ++++++ tests/u128_test_arithmetic.rs | 2 +- tests/u128_test_bits.rs | 2 +- tests/u256_test.rs | 48 ++ tests/u256_test_arithmetic.rs | 842 ++++++++++++++++++++++++++++++++++ tests/u256_test_bits.rs | 248 ++++++++++ 8 files changed, 1324 insertions(+), 2 deletions(-) create mode 100644 tests/common/helper.rs create mode 100644 tests/common/u256_wrapper.rs create mode 100644 tests/u256_test.rs create mode 100644 tests/u256_test_arithmetic.rs create mode 100644 tests/u256_test_bits.rs diff --git a/tests/common/helper.rs b/tests/common/helper.rs new file mode 100644 index 0000000..d1c6106 --- /dev/null +++ b/tests/common/helper.rs @@ -0,0 +1,33 @@ +use primitive_types::U256; +use rand::Rng; + +#[allow(dead_code)] +pub const DEFAULT_BOOL: bool = false; + +#[allow(dead_code)] +pub fn generate_u256(lower_bound: U256, upper_bound: U256) -> U256 { + assert!( + lower_bound <= upper_bound, + "Error: lower bound is greater than upper bound" + ); + + let (a_high, a_low): (u128, u128) = if lower_bound > U256::from(u128::MAX) { + ( + rand::thread_rng() + .gen_range((lower_bound >> 128).as_u128()..=(upper_bound >> 128).as_u128()), + rand::thread_rng().gen_range(0..=u128::MAX), + ) + } else if upper_bound > U256::from(u128::MAX) { + ( + rand::thread_rng().gen_range(0_u128..=(upper_bound >> 128).as_u128()), + rand::thread_rng().gen_range(lower_bound.as_u128()..=u128::MAX), + ) + } else { + ( + 0, + rand::thread_rng().gen_range(lower_bound.as_u128()..=upper_bound.as_u128()), + ) + }; + + (U256::from(a_high) << 128) | U256::from(a_low) +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 21e7049..e8669e2 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,2 +1,4 @@ pub mod core; +pub mod helper; +pub mod u256_wrapper; pub mod uint; diff --git a/tests/common/u256_wrapper.rs b/tests/common/u256_wrapper.rs new file mode 100644 index 0000000..97b2679 --- /dev/null +++ b/tests/common/u256_wrapper.rs @@ -0,0 +1,149 @@ +use primitive_types::U256; +use rand::Rng; +use rand::distributions::uniform::{SampleBorrow, SampleUniform, UniformSampler}; +use std::ops::{Add, Deref, DerefMut, Div, Mul, Sub}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct U256Wrapper(pub U256); + +impl U256Wrapper { + pub fn to_be_bytes(&self) -> [u8; 32] { + self.0.to_big_endian() + } +} + +impl Deref for U256Wrapper { + type Target = U256; + fn deref(&self) -> &U256 { + &self.0 + } +} + +impl DerefMut for U256Wrapper { + fn deref_mut(&mut self) -> &mut U256 { + &mut self.0 + } +} + +impl From for U256Wrapper { + fn from(v: U256) -> Self { + U256Wrapper(v) + } +} + +impl From for U256 { + fn from(v: U256Wrapper) -> Self { + v.0 + } +} + +impl Add for U256Wrapper { + type Output = U256Wrapper; + fn add(self, rhs: Self) -> Self::Output { + U256Wrapper(self.0 + rhs.0) + } +} + +impl Sub for U256Wrapper { + type Output = U256Wrapper; + fn sub(self, rhs: Self) -> Self::Output { + U256Wrapper(self.0 - rhs.0) + } +} + +impl Mul for U256Wrapper { + type Output = U256Wrapper; + fn mul(self, rhs: Self) -> Self::Output { + U256Wrapper(self.0 * rhs.0) + } +} + +impl Div for U256Wrapper { + type Output = U256Wrapper; + fn div(self, rhs: Self) -> Self::Output { + U256Wrapper(self.0 / rhs.0) + } +} + +#[derive(Clone, Copy, Debug)] +pub struct UniformU256 { + lower: U256, + range: U256, + inclusive_max: bool, +} + +impl UniformSampler for UniformU256 { + type X = U256Wrapper; + + fn new(lower: B1, upper: B2) -> Self + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let lower = lower.borrow().0; + let upper = upper.borrow().0; + + assert!(lower < upper, "Lower bound must be less than upper bound"); + + UniformU256 { + lower, + range: upper - lower, + inclusive_max: false, + } + } + + fn new_inclusive(lower: B1, upper: B2) -> Self + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let lower = lower.borrow().0; + let upper = upper.borrow().0; + + assert!( + lower <= upper, + "Lower bound must be less than or equal to upper bound" + ); + + if upper == U256::MAX && lower == U256::zero() { + UniformU256 { + lower, + range: U256::zero(), + inclusive_max: true, + } + } else { + UniformU256 { + lower, + range: upper - lower + U256::one(), + inclusive_max: false, + } + } + } + + fn sample(&self, rng: &mut R) -> Self::X { + if self.inclusive_max { + return U256Wrapper(random_u256(rng)); + } + loop { + let candidate = random_u256(rng); + let result = candidate % self.range; + let limit = U256::MAX - (U256::MAX % self.range); + + if candidate < limit { + return U256Wrapper(self.lower + result); + } + } + } +} + +impl SampleUniform for U256Wrapper { + type Sampler = UniformU256; +} + +fn random_u256(rng: &mut R) -> U256 { + let mut bytes = [0u8; 32]; + + rng.fill(&mut bytes); + + U256::from_big_endian(&bytes) +} diff --git a/tests/u128_test_arithmetic.rs b/tests/u128_test_arithmetic.rs index 2180960..e160cfb 100644 --- a/tests/u128_test_arithmetic.rs +++ b/tests/u128_test_arithmetic.rs @@ -3,6 +3,7 @@ mod common; use primitive_types::U256; use rand::Rng; +use crate::common::helper::DEFAULT_BOOL; use common::core::{Expect, run}; use simplicityhl_std::artifacts::u128_test_arithmetic::U128TestArithmeticProgram; @@ -31,7 +32,6 @@ fn op(o: FunctionToTest) -> u8 { o as u8 } -const DEFAULT_BOOL: bool = false; const DEFAULT_EXPECTED: u128 = 0; fn program() -> U128TestArithmeticProgram { diff --git a/tests/u128_test_bits.rs b/tests/u128_test_bits.rs index cfc4531..5d04898 100644 --- a/tests/u128_test_bits.rs +++ b/tests/u128_test_bits.rs @@ -2,6 +2,7 @@ mod common; use rand::Rng; +use crate::common::helper::DEFAULT_BOOL; use common::core::{Expect, run}; use simplicityhl_std::artifacts::u128_test_bits::U128TestBitsProgram; @@ -22,7 +23,6 @@ fn op(o: FunctionToTest) -> u8 { o as u8 } -const DEFAULT_BOOL: bool = false; const DEFAULT_EXPECTED: u128 = 0; fn program() -> U128TestBitsProgram { diff --git a/tests/u256_test.rs b/tests/u256_test.rs new file mode 100644 index 0000000..3cf5d18 --- /dev/null +++ b/tests/u256_test.rs @@ -0,0 +1,48 @@ +mod common; + +use primitive_types::U256; + +use common::u256_wrapper::U256Wrapper; +use common::uint::TestUint; + +use simplicityhl_std::artifacts::u256_test::U256TestProgram; +use simplicityhl_std::artifacts::u256_test::derived_u256_test::{ + U256TestArguments, U256TestWitness, +}; + +// The only per-width code for the common operations. +impl TestUint for U256Wrapper { + type Program = U256TestProgram; + type Witness = U256TestWitness; + + const ZERO: U256Wrapper = U256Wrapper(U256::zero()); + const ONE: U256Wrapper = U256Wrapper(U256::one()); + const MAX: U256Wrapper = U256Wrapper(U256::MAX); + const HALF_MAX: U256Wrapper = U256Wrapper(U256([u64::MAX, u64::MAX, u64::MAX, u64::MAX >> 1])); + const MUL_BOUND: U256Wrapper = U256Wrapper(U256([0, 0, 1, 0])); // 2^(256/2) + + fn program() -> U256TestProgram { + U256TestProgram::new(U256TestArguments {}) + } + + fn witness( + op: u8, + a: U256Wrapper, + b: U256Wrapper, + expected: Option, + ) -> U256TestWitness { + U256TestWitness { + function_index: op, + first_arg: a.to_big_endian(), + second_arg: b.to_big_endian(), + expected: expected.map(|w| w.to_be_bytes()), + } + } +} + +mod u256_tests { + use super::*; + + // Stamps the 16 `#[simplex::test]` entry points for U256Wrapper. Logic lives in common::uint. + uint_tests!(U256Wrapper); +} diff --git a/tests/u256_test_arithmetic.rs b/tests/u256_test_arithmetic.rs new file mode 100644 index 0000000..bf54439 --- /dev/null +++ b/tests/u256_test_arithmetic.rs @@ -0,0 +1,842 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use crate::common::helper::{DEFAULT_BOOL, generate_u256}; +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_test_arithmetic::U256TestArithmeticProgram; +use simplicityhl_std::artifacts::u256_test_arithmetic::derived_u256_test_arithmetic::{ + U256TestArithmeticArguments, U256TestArithmeticWitness, +}; + +enum FunctionToTest { + IsZero256, + Lt256, + Le256, + Split256Into64, + Add256, + Add256_128, + Sub256, + Mul256, + DivMod256_64, + DivMod256_128, + DivMod256, + Div256, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestArithmeticProgram { + U256TestArithmeticProgram::new(U256TestArithmeticArguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, + expected_bool: bool, + second_expected: [u8; 32], +) -> U256TestArithmeticWitness { + U256TestArithmeticWitness { + function_index: function, + first_arg: a, + second_arg: b, + expected, + expected_bool, + second_expected, + } +} + +fn split_u512(a: [u8; 64]) -> ([u8; 32], [u8; 32]) { + let high = U256::from_big_endian(&a[0..32]); + let low = U256::from_big_endian(&a[32..64]); + + (high.to_big_endian(), low.to_big_endian()) +} + +mod u256_tests_arithmetic { + use super::*; + + #[simplex::test] + fn u256_test_is_zero_256_true(context: simplex::TestContext) -> anyhow::Result<()> { + let a = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::IsZero256), + a, + DEFAULT_EXPECTED, + Some(DEFAULT_EXPECTED), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_is_zero_256_false(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::IsZero256), + a, + DEFAULT_EXPECTED, + Some(DEFAULT_EXPECTED), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_lt_256_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX - 1); + let b = a + 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Lt256), + a.to_big_endian(), + b.to_big_endian(), + Some(DEFAULT_EXPECTED), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_lt_256_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Lt256), + a, + a, + Some(DEFAULT_EXPECTED), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_lt_256_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = a - 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Lt256), + a.to_big_endian(), + b.to_big_endian(), + Some(DEFAULT_EXPECTED), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_le_256_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX - 1); + let b = a + 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Le256), + a.to_big_endian(), + b.to_big_endian(), + Some(DEFAULT_EXPECTED), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_le_256_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Le256), + a, + a, + Some(DEFAULT_EXPECTED), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_le_256_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = a - 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Le256), + a.to_big_endian(), + b.to_big_endian(), + Some(DEFAULT_EXPECTED), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_split_256_into_64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Split256Into64), + a, + DEFAULT_EXPECTED, + Some(a), + DEFAULT_BOOL, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_add_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX / 2); + let b = generate_u256(U256::zero(), U256::MAX / 2); + let result = (a + b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Add256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_add_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::MAX); + let result = (b - 1).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Add256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_add_256_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX / 2); + let b = generate_u256(U256::one(), U256::from(u128::MAX)) as U256; + let result = (a + b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Add256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_add_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::from(u128::MAX)) as U256; + let result = (b - 1).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Add256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::zero(), a); + let result = (a - b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a, + a, + Some([0; 32]), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b_high = rand::thread_rng().gen_range(0..=u128::MAX); + + let low: u128 = a.low_u128(); + let b = (U256::from(b_high) << 128) | U256::from(low); + + let (result, carry) = a.overflowing_sub(b); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result.to_big_endian()), + carry, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_diff_is_u128_max(context: simplex::TestContext) -> anyhow::Result<()> { + let a_low: u128 = u128::MAX; + + let a_high = rand::thread_rng().gen_range(0..=u128::MAX); + let b_high = rand::thread_rng().gen_range(0..=u128::MAX); + + let a = (U256::from(a_high) << 128) | U256::from(a_low); + let b = (U256::from(b_high)) << 128; // b_low is 0 + + //let carry = a < b; + let (result, carry) = a.overflowing_sub(b); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result.to_big_endian()), + carry, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_diff_is_u256_max(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX.to_big_endian(); + let b = U256::zero(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a, + b.to_big_endian(), + Some(a), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX - 1); + let b = U256::MAX; + let result = a + 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result.to_big_endian()), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::MAX); + let result = a.full_mul(b).to_big_endian(); + + let (result_high, result_low) = split_u512(result); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Mul256), + a.to_big_endian(), + b.to_big_endian(), + Some(result_high), + DEFAULT_BOOL, + result_low, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u64::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_64), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + DEFAULT_BOOL, + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_64), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_BOOL, + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn test_div_mod_256_128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::from(u64::MAX) + 1, U256::from(u128::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + DEFAULT_BOOL, + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_128_b_fits_into_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u64::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + DEFAULT_BOOL, + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_128), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_BOOL, + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn test_div_mod_256_128_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = (generate_u256(U256::one(), U256::from(u128::MAX))).to_big_endian(); + + let q = U256::one().to_big_endian(); + let r = U256::zero().to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_128), + a, + a, + Some(q), + DEFAULT_BOOL, + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_a_less_than_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX - 1); + let b = generate_u256(a + 1, U256::MAX); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + DEFAULT_BOOL, + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_div_128(context: simplex::TestContext) -> anyhow::Result<()> { + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let a = generate_u256(b, U256::from(u128::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + DEFAULT_BOOL, + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_q_is_1(context: simplex::TestContext) -> anyhow::Result<()> { + // case where a >= b and a_high = b_high != 0 + let b_low = generate_u256(U256::zero(), U256::from(u128::MAX)); + let a_low = generate_u256(b_low, U256::from(u128::MAX)); + let high = generate_u256(U256::one(), U256::from(u128::MAX)); + + let a = ((high as U256) << 128) | (a_low as U256); + let b = ((high as U256) << 128) | (b_low as U256); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + DEFAULT_BOOL, + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_b_fits_into_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + DEFAULT_BOOL, + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_b_is_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let b = generate_u256(U256::one(), U256::MAX - 1); + let a = generate_u256(b + 1, U256::MAX); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + DEFAULT_BOOL, + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_a_equal_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a, + a, + Some(U256::one().to_big_endian()), + DEFAULT_BOOL, + [0; 32], + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_equal_high_words_max_low_diff( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let high = generate_u256(U256::one(), U256::from(u128::MAX)); + + let a = ((high << 128) | (U256::from(u128::MAX))).to_big_endian(); + let b = (high << 128).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a, + b, + Some(U256::one().to_big_endian()), + DEFAULT_BOOL, + U256::from(u128::MAX).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_eq_high_words_a_less_than_b( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let high = generate_u256(U256::one(), U256::from(u128::MAX)); + + let a = ((high as U256) << 128).to_big_endian(); + let b = (((high as U256) << 128) | (U256::from(u128::MAX))).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a, + b, + Some([0; 32]), + DEFAULT_BOOL, + a, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::one(), U256::MAX); + let result = (a / b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Div256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + DEFAULT_BOOL, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Div256), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_BOOL, + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u256_test_bits.rs b/tests/u256_test_bits.rs new file mode 100644 index 0000000..a1df22c --- /dev/null +++ b/tests/u256_test_bits.rs @@ -0,0 +1,248 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use crate::common::helper::{DEFAULT_BOOL, generate_u256}; +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_test_bits::U256TestBitsProgram; +use simplicityhl_std::artifacts::u256_test_bits::derived_u256_test_bits::{ + U256TestBitsArguments, U256TestBitsWitness, +}; + +enum FunctionToTest { + And256, + Or256, + Eq256, + LeftShift256, + RightShift256, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestBitsProgram { + U256TestBitsProgram::new(U256TestBitsArguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, + expected_bool: bool, +) -> U256TestBitsWitness { + U256TestBitsWitness { + function_index: function, + first_arg: a, + second_arg: b, + expected, + expected_bool, + } +} + +mod u256_tests_bits { + use super::*; + + #[simplex::test] + fn u256_test_and_256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::zero(), U256::MAX); + let result = (a & b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::And256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + DEFAULT_BOOL, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_or_256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::zero(), U256::MAX); + let result = (a | b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Or256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + DEFAULT_BOOL, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_eq_256_true(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Eq256), + a, + a, + Some(DEFAULT_EXPECTED), + true, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_eq_256_false(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = a - 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Eq256), + a.to_big_endian(), + b.to_big_endian(), + Some(DEFAULT_EXPECTED), + false, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_left_shift_256(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = rand::thread_rng().gen_range(1..=127_u8); + let val = generate_u256(U256::zero(), U256::MAX); + let result = (val << shift).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::LeftShift256), + U256::from(shift).to_big_endian(), + val.to_big_endian(), + Some(result), + DEFAULT_BOOL, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_left_shift_256_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = 0; + let val = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + let result = val; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::LeftShift256), + U256::from(shift).to_big_endian(), + val, + Some(result), + DEFAULT_BOOL, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_left_shift_256_max(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = u8::MAX; + let val = generate_u256(U256::zero(), U256::MAX); + let result = (val << shift).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::LeftShift256), + U256::from(shift).to_big_endian(), + val.to_big_endian(), + Some(result), + DEFAULT_BOOL, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_right_shift_256(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = rand::thread_rng().gen_range(1..=127_u128); + let val = generate_u256(U256::zero(), U256::MAX); + let result = (val >> shift).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::RightShift256), + U256::from(shift).to_big_endian(), + val.to_big_endian(), + Some(result), + DEFAULT_BOOL, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_right_shift_256_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = 0; + let val = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + let result = val; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::RightShift256), + U256::from(shift).to_big_endian(), + val, + Some(result), + DEFAULT_BOOL, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_right_shift_256_max(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = u8::MAX; + let val = generate_u256(U256::zero(), U256::MAX); + let result = (val >> shift).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::RightShift256), + U256::from(shift).to_big_endian(), + val.to_big_endian(), + Some(result), + DEFAULT_BOOL, + ), + Expect::Ok, + ) + } +} From 655d4507b5e3e7540f2e7fecfa4943957e36fb33 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Sat, 1 Aug 2026 16:49:11 +0300 Subject: [PATCH 04/15] fixed typo --- tests/u128_test.rs | 2 +- tests/u16_test.rs | 2 +- tests/u256_test.rs | 2 +- tests/u32_test.rs | 2 +- tests/u64_test.rs | 2 +- tests/u8_test.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/u128_test.rs b/tests/u128_test.rs index 5791165..f2411bb 100644 --- a/tests/u128_test.rs +++ b/tests/u128_test.rs @@ -35,6 +35,6 @@ impl TestUint for u128 { mod u128_tests { use super::*; - // Stamps the 16 `#[simplex::test]` entry points for u128. Logic lives in common::uint. + // Stamps the 22 `#[simplex::test]` entry points for u128. Logic lives in common::uint. uint_tests!(u128); } diff --git a/tests/u16_test.rs b/tests/u16_test.rs index 7274e4e..49d64f5 100644 --- a/tests/u16_test.rs +++ b/tests/u16_test.rs @@ -33,6 +33,6 @@ impl TestUint for u16 { mod u16_tests { use super::*; - // Stamps the 16 `#[simplex::test]` entry points for u16. Logic lives in common::uint. + // Stamps the 22 `#[simplex::test]` entry points for u16. Logic lives in common::uint. uint_tests!(u16); } diff --git a/tests/u256_test.rs b/tests/u256_test.rs index 3cf5d18..d15c05d 100644 --- a/tests/u256_test.rs +++ b/tests/u256_test.rs @@ -43,6 +43,6 @@ impl TestUint for U256Wrapper { mod u256_tests { use super::*; - // Stamps the 16 `#[simplex::test]` entry points for U256Wrapper. Logic lives in common::uint. + // Stamps the 22 `#[simplex::test]` entry points for U256Wrapper. Logic lives in common::uint. uint_tests!(U256Wrapper); } diff --git a/tests/u32_test.rs b/tests/u32_test.rs index 7c76910..bf3fd70 100644 --- a/tests/u32_test.rs +++ b/tests/u32_test.rs @@ -33,6 +33,6 @@ impl TestUint for u32 { mod u32_tests { use super::*; - // Stamps the 16 `#[simplex::test]` entry points for u32. Logic lives in common::uint. + // Stamps the 22 `#[simplex::test]` entry points for u32. Logic lives in common::uint. uint_tests!(u32); } diff --git a/tests/u64_test.rs b/tests/u64_test.rs index 59cbed8..bb30a03 100644 --- a/tests/u64_test.rs +++ b/tests/u64_test.rs @@ -50,7 +50,7 @@ mod u64_tests { use super::*; - // Stamps the 16 `#[simplex::test]` entry points for u64. Logic lives in common::uint. + // Stamps the 22 `#[simplex::test]` entry points for u64. Logic lives in common::uint. uint_tests!(u64); #[simplex::test] diff --git a/tests/u8_test.rs b/tests/u8_test.rs index 90bfaff..4509c5a 100644 --- a/tests/u8_test.rs +++ b/tests/u8_test.rs @@ -33,6 +33,6 @@ impl TestUint for u8 { mod u8_tests { use super::*; - // Stamps the 16 `#[simplex::test]` entry points for u8. Logic lives in common::uint. + // Stamps the 22 `#[simplex::test]` entry points for u8. Logic lives in common::uint. uint_tests!(u8); } From c724bc6e21e06f3b0a2e932e0cd56f4b6c0e191c Mon Sep 17 00:00:00 2001 From: aritkulova Date: Mon, 3 Aug 2026 12:20:51 +0300 Subject: [PATCH 05/15] splitted u256 arithmetic tests into two batches for faster tests --- simf/u256_test_arithmetic_1.simf | 64 ++++ ...metic.simf => u256_test_arithmetic_2.simf} | 43 +-- tests/u256_test_arithmetic_1.rs | 312 ++++++++++++++++++ ...rithmetic.rs => u256_test_arithmetic_2.rs} | 279 +--------------- 4 files changed, 390 insertions(+), 308 deletions(-) create mode 100644 simf/u256_test_arithmetic_1.simf rename simf/{u256_test_arithmetic.simf => u256_test_arithmetic_2.simf} (56%) create mode 100644 tests/u256_test_arithmetic_1.rs rename tests/{u256_test_arithmetic.rs => u256_test_arithmetic_2.rs} (67%) diff --git a/simf/u256_test_arithmetic_1.simf b/simf/u256_test_arithmetic_1.simf new file mode 100644 index 0000000..8e9542d --- /dev/null +++ b/simf/u256_test_arithmetic_1.simf @@ -0,0 +1,64 @@ +use crate::lib::u256::{ + eq_256, + is_zero_256, + lt_256, + le_256, + split_256_into_64, + add_256, + add_256_128 +}; +use crate::helper::{ + if_test_this_function, + assert_bool +}; + +/// Asserts a result equals expected u256 and bool values. +/// Used for functions that return carry or borrow bool value. +fn assert_eq_uint_bool( + result: (bool, u256), + expected: u256, + expected_bool: bool +) { + let (bool_res, uint_res): (bool, u256) = result; + + assert_bool(bool_res, expected_bool); + + assert!(eq_256(uint_res, expected)); +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + let expected_bool: bool = witness::EXPECTED_BOOL; + let second_expected: u256 = witness::SECOND_EXPECTED; + + /// Arithmetic + + match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_256(a), expected_bool); }, false => {}, }; + match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_256(a, b), expected_bool); }, false => {}, }; + match if_test_this_function(2, fn_idx) { true => { assert_bool(le_256(a, b), expected_bool); }, false => {}, }; + + match if_test_this_function(3, fn_idx) { + true => { + let ((res1, res2), (res3, res4)): ((u64, u64), (u64, u64)) = split_256_into_64(a); + + let (high, low): (u128, u128) = ::into(unwrap(expected)); + + let (expected1, expected2): (u64, u64) = ::into(high); + let (expected3, expected4): (u64, u64) = ::into(low); + + assert!(jet::eq_64(res1, expected1)); + assert!(jet::eq_64(res2, expected2)); + assert!(jet::eq_64(res3, expected3)); + assert!(jet::eq_64(res4, expected4)); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { true => { assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; + match if_test_this_function(5, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); assert_eq_uint_bool(add_256_128(a, b), unwrap(expected), expected_bool); }, false => {}, }; +} diff --git a/simf/u256_test_arithmetic.simf b/simf/u256_test_arithmetic_2.simf similarity index 56% rename from simf/u256_test_arithmetic.simf rename to simf/u256_test_arithmetic_2.simf index aa25b60..1e10a12 100644 --- a/simf/u256_test_arithmetic.simf +++ b/simf/u256_test_arithmetic_2.simf @@ -1,11 +1,6 @@ use crate::lib::u256::{ eq_256, - is_zero_256, - lt_256, - le_256, split_256_into_64, - add_256, - add_256_128, sub_256, mul_256, div_mod_256_64, @@ -13,7 +8,7 @@ use crate::lib::u256::{ div_mod_256, div_256 }; -use crate::lib::u128::{eq_128}; +use crate::lib::u128::eq_128; use crate::helper::{ if_test_this_function, assert_bool @@ -45,33 +40,9 @@ fn main() { /// Arithmetic - match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_256(a), expected_bool); }, false => {}, }; - match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_256(a, b), expected_bool); }, false => {}, }; - match if_test_this_function(2, fn_idx) { true => { assert_bool(le_256(a, b), expected_bool); }, false => {}, }; + match if_test_this_function(0, fn_idx) { true => { assert_eq_uint_bool(sub_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; - match if_test_this_function(3, fn_idx) { - true => { - let ((res1, res2), (res3, res4)): ((u64, u64), (u64, u64)) = split_256_into_64(a); - - let (high, low): (u128, u128) = ::into(unwrap(expected)); - - let (expected1, expected2): (u64, u64) = ::into(high); - let (expected3, expected4): (u64, u64) = ::into(low); - - assert!(jet::eq_64(res1, expected1)); - assert!(jet::eq_64(res2, expected2)); - assert!(jet::eq_64(res3, expected3)); - assert!(jet::eq_64(res4, expected4)); - }, - false => {}, - }; - - match if_test_this_function(4, fn_idx) { true => { assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; - match if_test_this_function(5, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); assert_eq_uint_bool(add_256_128(a, b), unwrap(expected), expected_bool); }, false => {}, }; - - match if_test_this_function(6, fn_idx) { true => { assert_eq_uint_bool(sub_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; - - match if_test_this_function(7, fn_idx) { + match if_test_this_function(1, fn_idx) { true => { let (result_high, result_low): (u256,u256) = mul_256(a, b); @@ -81,7 +52,7 @@ fn main() { false => {}, }; - match if_test_this_function(8, fn_idx) { + match if_test_this_function(2, fn_idx) { true => { let ((_, _), (_, b)): ((u64, u64), (u64, u64)) = split_256_into_64(b); let ((_, _), (_, expected_r)): ((u64, u64), (u64, u64)) = split_256_into_64(second_expected); @@ -94,7 +65,7 @@ fn main() { false => {}, }; - match if_test_this_function(9, fn_idx) { + match if_test_this_function(3, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); let (_, expected_r): (u128, u128) = ::into(second_expected); @@ -107,7 +78,7 @@ fn main() { false => {}, }; - match if_test_this_function(10, fn_idx) { + match if_test_this_function(4, fn_idx) { true => { let (q, r): (u256, u256) = div_mod_256(a, b); @@ -117,5 +88,5 @@ fn main() { false => {}, }; - match if_test_this_function(11, fn_idx) { true => { assert!(eq_256(div_256(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(5, fn_idx) { true => { assert!(eq_256(div_256(a, b), unwrap(expected))); }, false => {}, }; } diff --git a/tests/u256_test_arithmetic_1.rs b/tests/u256_test_arithmetic_1.rs new file mode 100644 index 0000000..1f5443a --- /dev/null +++ b/tests/u256_test_arithmetic_1.rs @@ -0,0 +1,312 @@ +mod common; + +use primitive_types::U256; + +use crate::common::helper::{DEFAULT_BOOL, generate_u256}; +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_test_arithmetic_1::U256TestArithmetic1Program; +use simplicityhl_std::artifacts::u256_test_arithmetic_1::derived_u256_test_arithmetic_1::{ + U256TestArithmetic1Arguments, U256TestArithmetic1Witness, +}; + +enum FunctionToTest { + IsZero256, + Lt256, + Le256, + Split256Into64, + Add256, + Add256_128, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestArithmetic1Program { + U256TestArithmetic1Program::new(U256TestArithmetic1Arguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, + expected_bool: bool, + second_expected: [u8; 32], +) -> U256TestArithmetic1Witness { + U256TestArithmetic1Witness { + function_index: function, + first_arg: a, + second_arg: b, + expected, + expected_bool, + second_expected, + } +} + +mod u256_tests_arithmetic { + use super::*; + + #[simplex::test] + fn u256_test_is_zero_256_true(context: simplex::TestContext) -> anyhow::Result<()> { + let a = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::IsZero256), + a, + DEFAULT_EXPECTED, + Some(DEFAULT_EXPECTED), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_is_zero_256_false(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::IsZero256), + a, + DEFAULT_EXPECTED, + Some(DEFAULT_EXPECTED), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_lt_256_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX - 1); + let b = a + 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Lt256), + a.to_big_endian(), + b.to_big_endian(), + Some(DEFAULT_EXPECTED), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_lt_256_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Lt256), + a, + a, + Some(DEFAULT_EXPECTED), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_lt_256_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = a - 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Lt256), + a.to_big_endian(), + b.to_big_endian(), + Some(DEFAULT_EXPECTED), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_le_256_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX - 1); + let b = a + 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Le256), + a.to_big_endian(), + b.to_big_endian(), + Some(DEFAULT_EXPECTED), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_le_256_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Le256), + a, + a, + Some(DEFAULT_EXPECTED), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_le_256_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = a - 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Le256), + a.to_big_endian(), + b.to_big_endian(), + Some(DEFAULT_EXPECTED), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_split_256_into_64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Split256Into64), + a, + DEFAULT_EXPECTED, + Some(a), + DEFAULT_BOOL, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_add_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX / 2); + let b = generate_u256(U256::zero(), U256::MAX / 2); + let result = (a + b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Add256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_add_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::MAX); + let result = (b - 1).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Add256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_add_256_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX / 2); + let b = generate_u256(U256::one(), U256::from(u128::MAX)) as U256; + let result = (a + b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Add256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_add_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::from(u128::MAX)) as U256; + let result = (b - 1).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Add256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } +} diff --git a/tests/u256_test_arithmetic.rs b/tests/u256_test_arithmetic_2.rs similarity index 67% rename from tests/u256_test_arithmetic.rs rename to tests/u256_test_arithmetic_2.rs index bf54439..7016aea 100644 --- a/tests/u256_test_arithmetic.rs +++ b/tests/u256_test_arithmetic_2.rs @@ -6,18 +6,12 @@ use rand::Rng; use crate::common::helper::{DEFAULT_BOOL, generate_u256}; use common::core::{Expect, run}; -use simplicityhl_std::artifacts::u256_test_arithmetic::U256TestArithmeticProgram; -use simplicityhl_std::artifacts::u256_test_arithmetic::derived_u256_test_arithmetic::{ - U256TestArithmeticArguments, U256TestArithmeticWitness, +use simplicityhl_std::artifacts::u256_test_arithmetic_2::U256TestArithmetic2Program; +use simplicityhl_std::artifacts::u256_test_arithmetic_2::derived_u256_test_arithmetic_2::{ + U256TestArithmetic2Arguments, U256TestArithmetic2Witness, }; enum FunctionToTest { - IsZero256, - Lt256, - Le256, - Split256Into64, - Add256, - Add256_128, Sub256, Mul256, DivMod256_64, @@ -33,8 +27,8 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; -fn program() -> U256TestArithmeticProgram { - U256TestArithmeticProgram::new(U256TestArithmeticArguments {}) +fn program() -> U256TestArithmetic2Program { + U256TestArithmetic2Program::new(U256TestArithmetic2Arguments {}) } fn build_witness( @@ -44,8 +38,8 @@ fn build_witness( expected: Option<[u8; 32]>, expected_bool: bool, second_expected: [u8; 32], -) -> U256TestArithmeticWitness { - U256TestArithmeticWitness { +) -> U256TestArithmetic2Witness { + U256TestArithmetic2Witness { function_index: function, first_arg: a, second_arg: b, @@ -65,265 +59,6 @@ fn split_u512(a: [u8; 64]) -> ([u8; 32], [u8; 32]) { mod u256_tests_arithmetic { use super::*; - #[simplex::test] - fn u256_test_is_zero_256_true(context: simplex::TestContext) -> anyhow::Result<()> { - let a = [0; 32]; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::IsZero256), - a, - DEFAULT_EXPECTED, - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_is_zero_256_false(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::IsZero256), - a, - DEFAULT_EXPECTED, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_lt_256_less(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX - 1); - let b = a + 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Lt256), - a.to_big_endian(), - b.to_big_endian(), - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_lt_256_eq(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Lt256), - a, - a, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_lt_256_bigger(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX); - let b = a - 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Lt256), - a.to_big_endian(), - b.to_big_endian(), - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_le_256_less(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX - 1); - let b = a + 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Le256), - a.to_big_endian(), - b.to_big_endian(), - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_le_256_eq(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Le256), - a, - a, - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_le_256_bigger(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX); - let b = a - 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Le256), - a.to_big_endian(), - b.to_big_endian(), - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_split_256_into_64(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Split256Into64), - a, - DEFAULT_EXPECTED, - Some(a), - DEFAULT_BOOL, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_add_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX / 2); - let b = generate_u256(U256::zero(), U256::MAX / 2); - let result = (a + b).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add256), - a.to_big_endian(), - b.to_big_endian(), - Some(result), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_add_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = U256::MAX; - let b = generate_u256(U256::one(), U256::MAX); - let result = (b - 1).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add256), - a.to_big_endian(), - b.to_big_endian(), - Some(result), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_add_256_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX / 2); - let b = generate_u256(U256::one(), U256::from(u128::MAX)) as U256; - let result = (a + b).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add256_128), - a.to_big_endian(), - b.to_big_endian(), - Some(result), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_add_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = U256::MAX; - let b = generate_u256(U256::one(), U256::from(u128::MAX)) as U256; - let result = (b - 1).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add256_128), - a.to_big_endian(), - b.to_big_endian(), - Some(result), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - #[simplex::test] fn u256_test_sub_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); From b3d44a1a8b1138080626c09c72bac4b9bdf4963f Mon Sep 17 00:00:00 2001 From: aritkulova Date: Mon, 3 Aug 2026 12:31:03 +0300 Subject: [PATCH 06/15] refactored split_256_into_64 --- simf/lib/u256.simf | 16 +++++++--------- simf/u256_test_arithmetic_1.simf | 2 +- simf/u256_test_arithmetic_2.simf | 4 ++-- simf/u256_test_bits.simf | 4 ++-- 4 files changed, 12 insertions(+), 14 deletions(-) diff --git a/simf/lib/u256.simf b/simf/lib/u256.simf index 4f22fc6..fd9168a 100644 --- a/simf/lib/u256.simf +++ b/simf/lib/u256.simf @@ -176,10 +176,8 @@ pub fn ge_256( /// Splits the u256 integer into four u64 integers pub fn split_256_into_64( a: u256 -) -> ((u64, u64), (u64, u64)) { - let (high, low): (u128, u128) = ::into(a); - - (::into(high), ::into(low)) +) -> (u64, u64, u64, u64) { + ::into(a) } /// Adds two integers and returns the carry @@ -411,7 +409,7 @@ fn normalize_to_threshold_256_127(a: u256, b: u256, is_b_u256: bool) -> (u256, u /// Divides the 256-bit integer by the 64-bit integer, /// returns a tuple of the u256 quotient and the u64 remainder pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { - let ((u3, u2), (u1, u0)): ((u64, u64), (u64, u64)) = split_256_into_64(dividend); + let (u3, u2, u1, u0): (u64, u64, u64, u64) = split_256_into_64(dividend); // calculate the upper part of the quotient let (q3, remainder): (u64, u64) = jet::div_mod_64(u3, divisor); @@ -423,7 +421,7 @@ pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { normalize_to_threshold_256_63(dividend, divisor, false); // normalized dividend fits into 256 bits - let ((u3, u2), (u1, u0)): ((u64, u64), (u64, u64)) = split_256_into_64(norm_dividend_low); + let (u3, u2, u1, u0): (u64, u64, u64, u64) = split_256_into_64(norm_dividend_low); let (_, v0): (u64, u64) = ::into(norm_divisor); let (q2, remainder): (u64, u64) = jet::div_mod_128_64( <(u64, u64)>::into((u3, u2)), v0); @@ -445,7 +443,7 @@ fn mul_and_sub(q: u64, u2: u64, u1: u64, u0: u64, v: u128) -> (u64, u64) { let q_v: u256 = mul_128(<(u64, u64)>::into((0, q)), v); let u_updated: u256 = safe_sub_256(u, q_v); - let ((_, _), (u1, u0)): ((u64, u64), (u64, u64)) = split_256_into_64(u_updated); + let (_, _, u1, u0): (u64, u64, u64, u64) = split_256_into_64(u_updated); (u1, u0) } @@ -459,8 +457,8 @@ fn algorithm_d_256_128(dividend: u256, divisor: u128) -> (u256, u128) { normalize_to_threshold_256_63(dividend, divisor, true); // normalized dividend fits into 320 bits - let ((_, _), (_, u4)): ((u64, u64), (u64, u64)) = split_256_into_64(norm_dividend_high); - let ((u3, u2), (u1, u0)): ((u64, u64), (u64, u64)) = split_256_into_64(norm_dividend_low); + let (_, _, _, u4): (u64, u64, u64, u64) = split_256_into_64(norm_dividend_high); + let (u3, u2, u1, u0): (u64, u64, u64, u64) = split_256_into_64(norm_dividend_low); let (v1, v0): (u64, u64) = ::into(norm_divisor); let q2: u64 = estimate_quotient_digit_base_64(u4, u3, u2, v1, v0); diff --git a/simf/u256_test_arithmetic_1.simf b/simf/u256_test_arithmetic_1.simf index 8e9542d..9af64fe 100644 --- a/simf/u256_test_arithmetic_1.simf +++ b/simf/u256_test_arithmetic_1.simf @@ -44,7 +44,7 @@ fn main() { match if_test_this_function(3, fn_idx) { true => { - let ((res1, res2), (res3, res4)): ((u64, u64), (u64, u64)) = split_256_into_64(a); + let (res1, res2, res3, res4): (u64, u64, u64, u64) = split_256_into_64(a); let (high, low): (u128, u128) = ::into(unwrap(expected)); diff --git a/simf/u256_test_arithmetic_2.simf b/simf/u256_test_arithmetic_2.simf index 1e10a12..7172f4c 100644 --- a/simf/u256_test_arithmetic_2.simf +++ b/simf/u256_test_arithmetic_2.simf @@ -54,8 +54,8 @@ fn main() { match if_test_this_function(2, fn_idx) { true => { - let ((_, _), (_, b)): ((u64, u64), (u64, u64)) = split_256_into_64(b); - let ((_, _), (_, expected_r)): ((u64, u64), (u64, u64)) = split_256_into_64(second_expected); + let (_, _, _, b): (u64, u64, u64, u64) = split_256_into_64(b); + let (_, _, _, expected_r): (u64, u64, u64, u64) = split_256_into_64(second_expected); let (q, r): (u256, u64) = div_mod_256_64(a, b); diff --git a/simf/u256_test_bits.simf b/simf/u256_test_bits.simf index 9ff1490..ca624b4 100644 --- a/simf/u256_test_bits.simf +++ b/simf/u256_test_bits.simf @@ -18,7 +18,7 @@ fn main() { match if_test_this_function(3, fn_idx) { true => { - let ((_, _,), (_, a)): ((u64, u64), (u64, u64)) = split_256_into_64(a); + let (_, _, _, a): (u64, u64, u64, u64) = split_256_into_64(a); let shift: u8 = jet::rightmost_64_8(a); assert!(eq_256(left_shift_256(shift, b), unwrap(expected))); @@ -27,7 +27,7 @@ fn main() { match if_test_this_function(4, fn_idx) { true => { - let ((_, _,), (_, a)): ((u64, u64), (u64, u64)) = split_256_into_64(a); + let (_, _, _, a): (u64, u64, u64, u64) = split_256_into_64(a); let shift: u8 = jet::rightmost_64_8(a); assert!(eq_256(right_shift_256(shift, b), unwrap(expected))); From 373c18cc2c879e0d1599fb391c2a4218f67fc0c7 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Mon, 3 Aug 2026 15:39:13 +0300 Subject: [PATCH 07/15] added full_sub_128 --- simf/lib/u128.simf | 24 +++++--- simf/lib/u256.simf | 30 ++++------ simf/u128_test_arithmetic.simf | 14 +++-- tests/u128_test_arithmetic.rs | 103 +++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 31 deletions(-) diff --git a/simf/lib/u128.simf b/simf/lib/u128.simf index 60bd00e..4e5092f 100644 --- a/simf/lib/u128.simf +++ b/simf/lib/u128.simf @@ -151,16 +151,16 @@ pub fn add_128_64(a: u128, b: u64) -> (bool, u128) { (carry_high, <(u64, u64)>::into((res_high, res_low))) } -/// Add two integers. Take a carry-in and return a carry-out -pub fn full_add_128(carry_low: bool, a: u128, b: u128) -> (bool, u128) { +/// Adds two integers. Takes a carry-in and returns a carry-out +pub fn full_add_128(carry_in: bool, a: u128, b: u128) -> (bool, u128) { let (a_high, a_low): (u64, u64) = ::into(a); let (b_high, b_low): (u64, u64) = ::into(b); - let (carry_low, sum_low): (bool, u64) = jet::full_add_64(carry_low, a_low, b_low); - let (carry_high, sum_high): (bool, u64) = jet::full_add_64(carry_low, a_high, b_high); + let (carry_low, sum_low): (bool, u64) = jet::full_add_64(carry_in, a_low, b_low); + let (carry_out, sum_high): (bool, u64) = jet::full_add_64(carry_low, a_high, b_high); let res: u128 = <(u64, u64)>::into((sum_high, sum_low)); - (carry_high, res) + (carry_out, res) } /// Returns the sum of two u128 values wrapped in Some, or None if the result overflows u128 @@ -190,7 +190,17 @@ pub fn sub_128(a: u128, b: u128) -> (bool, u128) { (borrow_high, res) } -// todo full_sub_128 +/// Subtracts the second integer from the first integer, takes a borrow-in and returns a borrow-out +pub fn full_sub_128(borrow_in: bool, a: u128, b: u128) -> (bool, u128) { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + let (borrow_low, diff_low): (bool, u64) = jet::full_subtract_64(borrow_in, a_low, b_low); + let (borrow_out, diff_high): (bool, u64) = jet::full_subtract_64(borrow_low, a_high, b_high); + + let res: u128 = <(u64, u64)>::into((diff_high, diff_low)); + (borrow_out, res) +} /// Returns the difference of two u128 values wrapped in Some, or None if the result overflows u128 pub fn checked_sub_128(a: u128, b: u128) -> Option { @@ -361,7 +371,7 @@ pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u fn algorithm_d_128_128(dividend: u128, divisor: u128) -> (u64, u128) { let (norm_dividend, norm_divisor, _): (u256, u128, u64) = normalize_to_threshold_128_63(dividend, divisor, true); - // normalized dividend fits into 192 bits + // normalized dividend fits into 192 bits let (_, u2, u1, u0): (u64, u64, u64, u64) = ::into(norm_dividend); let (v1, v0): (u64, u64) = ::into(norm_divisor); diff --git a/simf/lib/u256.simf b/simf/lib/u256.simf index fd9168a..7633296 100644 --- a/simf/lib/u256.simf +++ b/simf/lib/u256.simf @@ -11,6 +11,7 @@ use crate::lib::u128::{ add_128, full_add_128, sub_128, + full_sub_128, mul_128, safe_add_128, calculate_normalizer_base_64, @@ -235,24 +236,11 @@ pub fn sub_256( let (a_high, a_low): (u128, u128) = ::into(a); let (b_high, b_low): (u128, u128) = ::into(b); - let (borrow_high, diff_high): (bool, u128) = sub_128(a_high, b_high); let (borrow_low, diff_low): (bool, u128) = sub_128(a_low, b_low); + let (borrow_high, diff_high): (bool, u128) = full_sub_128(borrow_low, a_high, b_high); - match borrow_low { - false => { - let res: u256 = <(u128, u128)>::into((diff_high, diff_low)); - - (borrow_high, res) - }, - true => { - let (borrow, diff_high): (bool, u128) = sub_128(diff_high, 1); - - let final_borrow: bool = or(borrow, borrow_high); - let res: u256 = <(u128, u128)>::into((diff_high, diff_low)); - - (final_borrow, res) - } - } + let res: u256 = <(u128, u128)>::into((diff_high, diff_low)); + (borrow_high, res) } /// Returns the difference of two u256 values wrapped in Some, or None if the result overflows u256 @@ -356,7 +344,7 @@ fn normalize_to_threshold_256_63(a: u256, b: u128, is_b_u128: bool) -> (u256, u2 false => { let (high, low): (u256, u256) = mul_256(a, <(u128, u128)>::into((0, norm_128))); let b_norm: u256 = mul_128(b, norm_128); - let( _, b_norm): (u128, u128) = ::into(b_norm); + let (_, b_norm): (u128, u128) = ::into(b_norm); (high, low, b_norm, norm) }, @@ -437,7 +425,13 @@ pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { /// Multiplies the divisor by the quotient digit and subtracts the result from /// the corresponding dividend limbs. Returns the updated dividend segment, /// which is used by Algorithm D -fn mul_and_sub(q: u64, u2: u64, u1: u64, u0: u64, v: u128) -> (u64, u64) { +fn mul_and_sub( + q: u64, + u2: u64, + u1: u64, + u0: u64, + v: u128 +) -> (u64, u64) { let u: u256 = <(u64, u64, u64, u64)>::into((0, u2, u1, u0)); let q_v: u256 = mul_128(<(u64, u64)>::into((0, q)), v); diff --git a/simf/u128_test_arithmetic.simf b/simf/u128_test_arithmetic.simf index 193c00e..c507967 100644 --- a/simf/u128_test_arithmetic.simf +++ b/simf/u128_test_arithmetic.simf @@ -7,6 +7,7 @@ use crate::lib::u128::{ add_128_64, full_add_128, sub_128, + full_sub_128, mul_128, calculate_normalizer_base_64, estimate_quotient_digit_base_64, @@ -55,8 +56,9 @@ fn main() { match if_test_this_function(5, fn_idx) { true => { assert_eq_uint_bool(full_add_128(eq_128(second_expected, 1), a, b), unwrap(expected), expected_bool); }, false => {}, }; match if_test_this_function(6, fn_idx) { true => { assert_eq_uint_bool(sub_128(a, b), unwrap(expected), expected_bool); }, false => {}, }; + match if_test_this_function(7, fn_idx) { true => { assert_eq_uint_bool(full_sub_128(eq_128(second_expected, 1), a, b), unwrap(expected), expected_bool); }, false => {}, }; - match if_test_this_function(7, fn_idx) { + match if_test_this_function(8, fn_idx) { true => { let result: u256 = mul_128(a, b); @@ -68,7 +70,7 @@ fn main() { false => {}, }; - match if_test_this_function(8, fn_idx) { + match if_test_this_function(9, fn_idx) { true => { let norm: u64 = calculate_normalizer_base_64(b, expected_bool); @@ -79,7 +81,7 @@ fn main() { false => (), }; - match if_test_this_function(9, fn_idx) { + match if_test_this_function(10, fn_idx) { true => { let (_, u2): (u64, u64) = ::into(a); let (u1, u0): (u64, u64) = ::into(b); @@ -94,7 +96,7 @@ fn main() { false => (), }; - match if_test_this_function(10, fn_idx) { + match if_test_this_function(11, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); let (_, expected_r): (u64, u64) = ::into(second_expected); @@ -107,7 +109,7 @@ fn main() { false => {}, }; - match if_test_this_function(11, fn_idx) { + match if_test_this_function(12, fn_idx) { true => { let (q, r): (u128, u128) = div_mod_128(a, b); @@ -117,5 +119,5 @@ fn main() { false => {}, }; - match if_test_this_function(12, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(13, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => {}, }; } diff --git a/tests/u128_test_arithmetic.rs b/tests/u128_test_arithmetic.rs index e160cfb..620d2b1 100644 --- a/tests/u128_test_arithmetic.rs +++ b/tests/u128_test_arithmetic.rs @@ -19,6 +19,7 @@ enum FunctionToTest { Add128_64, FullAdd128, Sub128, + FullSub128, Mul128, CalculateNormalizerBase64, EstimateQuotientDigitBase64, @@ -567,6 +568,108 @@ mod u128_tests_arithmetic { ) } + #[simplex::test] + fn u128_test_full_sub_128_borrow_low_false( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + let b = rand::thread_rng().gen_range(0..=a); + let result = a - b; + let result_borrow = false; + let borrow_low = 0_u128; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullSub128), + a, + b, + Some(result), + result_borrow, + borrow_low, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_full_sub_128_overflow_borrow_low_false( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = u128::MAX; + let result = a + 1; + let result_borrow = true; + let borrow_low = 0_u128; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullSub128), + a, + b, + Some(result), + result_borrow, + borrow_low, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_full_sub_128_borrow_low_true(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + let b = rand::thread_rng().gen_range(0..a); + let result = a - b - 1; + let result_borrow = false; + let borrow_low = 1_u128; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullSub128), + a, + b, + Some(result), + result_borrow, + borrow_low, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_full_sub_128_overflow_borrow_low_true( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = u128::MAX; + let (result, result_borrow) = a.overflowing_div(b); + + let borrow_low = 1_u128; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullSub128), + a, + b, + Some(result - 1), + result_borrow, + borrow_low, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + #[simplex::test] fn u128_test_mul_128(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..u128::MAX); From 7a2ec0c31b6b461a24068d9d52caae1a3d190a12 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Mon, 3 Aug 2026 17:37:49 +0300 Subject: [PATCH 08/15] linting --- simf/lib/u256.simf | 76 ++++++++++++++-------------------------------- 1 file changed, 22 insertions(+), 54 deletions(-) diff --git a/simf/lib/u256.simf b/simf/lib/u256.simf index 7633296..c919926 100644 --- a/simf/lib/u256.simf +++ b/simf/lib/u256.simf @@ -13,7 +13,6 @@ use crate::lib::u128::{ sub_128, full_sub_128, mul_128, - safe_add_128, calculate_normalizer_base_64, estimate_quotient_digit_base_64, div_mod_128, @@ -78,9 +77,7 @@ pub fn left_shift_256(shift: u8, a: u256) -> u256 { /// Right-shift a 256-bit value by the given amount. Bits are filled with zeroes pub fn right_shift_256(shift: u8, a: u256) -> u256 { match jet::is_zero_8(shift) { - true => { - a - }, + true => a, false => { let (a_high, a_low): (u128, u128) = ::into(a); @@ -130,9 +127,7 @@ pub fn lt_256(a: u256, b: u256) -> bool { } } }, - true => { - true - } + true => true, } } @@ -155,9 +150,7 @@ pub fn le_256( } } }, - true => { - true - } + true => true, } } @@ -211,12 +204,8 @@ pub fn checked_add_256( let (carry, sum): (bool, u256) = add_256(a, b); match carry { - false => { - Some(sum) - }, - true => { - None - } + false => Some(sum), + true => None, } } @@ -251,12 +240,8 @@ pub fn checked_sub_256( let (borrow, diff): (bool, u256) = sub_256(a, b); match borrow { - false => { - Some(diff) - }, - true => { - None - } + false => Some(diff), + true => None, } } @@ -315,12 +300,8 @@ pub fn checked_mul_256( let (result_high, result_low): (u256, u256) = mul_256(a, b); match is_zero_256(result_high) { - false => { - None - }, - true => { - Some(result_low) - } + false => None, + true => Some(result_low), } } @@ -335,7 +316,11 @@ pub fn safe_mul_256( /// Normalizes the dividend and divisor for Algorithm D by multiplying /// both u256 and u128 by the same factor, ensuring that /// the most significant non-zero word of `b` is at least 2^63. -fn normalize_to_threshold_256_63(a: u256, b: u128, is_b_u128: bool) -> (u256, u256, u128, u64) { +fn normalize_to_threshold_256_63( + a: u256, + b: u128, + is_b_u128: bool +) -> (u256, u256, u128, u64) { let norm: u64 = calculate_normalizer_base_64(b, is_b_u128); let norm_128: u128 = <(u64, u64)>::into((0, norm)); @@ -372,9 +357,7 @@ fn normalize_to_threshold_256_127(a: u256, b: u256, is_b_u256: bool) -> (u256, u let (norm, remainder): (u128, u128) = div_mod_128(threshold, b_highest_word); let norm: u128 = match is_zero_128(remainder) { - true => { - norm - }, + true => norm, false => { let (_, norm): (bool, u128) = add_128(norm, 1); // norm <= 2^127, so norm + 1 can not overflow norm @@ -388,9 +371,7 @@ fn normalize_to_threshold_256_127(a: u256, b: u256, is_b_u256: bool) -> (u256, u (high, low, safe_mul_256(b, norm)) }, - false => { - (0, a, b) - }, + false => (0, a, b), } } @@ -516,9 +497,7 @@ fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { let (carry, r_hat): (bool, u128) = add_128(r_hat, v1); match carry { - true => { - q_hat - }, + true => q_hat, false => { let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); let (_, u_hat): (bool, u256) = sub_256(u_hat, <(u128, u128)>::into((0, v0))); @@ -530,17 +509,12 @@ fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { q_hat } - false => { - q_hat - }, + false => q_hat, } } - } }, - false => { - q_hat - }, + false => q_hat, }; let remainder: u256 = safe_sub_256(dividend, safe_mul_256(divisor, <(u128, u128)>::into((0, q)))); @@ -554,9 +528,7 @@ pub fn div_mod_256(a: u256, b: u256) -> (u256, u256) { let (b_high, b_low): (u128, u128) = ::into(b); match lt_256(a, b) { - true => { - (0, a) - }, + true => (0, a), false => { match and(is_zero_128(a_high), is_zero_128(b_high)) { true => { @@ -607,12 +579,8 @@ pub fn checked_div_256( b: u256 ) -> Option { match is_zero_256(b) { - false => { - Some(div_256(a, b)) - }, - true => { - None - } + false => Some(div_256(a, b)), + true => None, } } From c683922f647ef8038a136764970db1c8c2d159a3 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Mon, 3 Aug 2026 19:15:51 +0300 Subject: [PATCH 09/15] splitted u256 arithmetic tests into more batches --- simf/u256_test_arithmetic_1.simf | 52 +--- simf/u256_test_arithmetic_2.simf | 66 +--- simf/u256_test_arithmetic_3.simf | 57 ++++ simf/u256_test_arithmetic_4.simf | 40 +++ tests/u256_test_arithmetic_1.rs | 156 +--------- tests/u256_test_arithmetic_2.rs | 497 ++----------------------------- tests/u256_test_arithmetic_3.rs | 257 ++++++++++++++++ tests/u256_test_arithmetic_4.rs | 338 +++++++++++++++++++++ 8 files changed, 744 insertions(+), 719 deletions(-) create mode 100644 simf/u256_test_arithmetic_3.simf create mode 100644 simf/u256_test_arithmetic_4.simf create mode 100644 tests/u256_test_arithmetic_3.rs create mode 100644 tests/u256_test_arithmetic_4.rs diff --git a/simf/u256_test_arithmetic_1.simf b/simf/u256_test_arithmetic_1.simf index 9af64fe..a835485 100644 --- a/simf/u256_test_arithmetic_1.simf +++ b/simf/u256_test_arithmetic_1.simf @@ -1,64 +1,16 @@ -use crate::lib::u256::{ - eq_256, - is_zero_256, - lt_256, - le_256, - split_256_into_64, - add_256, - add_256_128 -}; -use crate::helper::{ - if_test_this_function, - assert_bool -}; - -/// Asserts a result equals expected u256 and bool values. -/// Used for functions that return carry or borrow bool value. -fn assert_eq_uint_bool( - result: (bool, u256), - expected: u256, - expected_bool: bool -) { - let (bool_res, uint_res): (bool, u256) = result; - - assert_bool(bool_res, expected_bool); - - assert!(eq_256(uint_res, expected)); -} +use crate::lib::u256::{is_zero_256, lt_256, le_256}; +use crate::helper::{if_test_this_function, assert_bool}; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; let a: u256 = witness::FIRST_ARG; let b: u256 = witness::SECOND_ARG; - let expected: Option = witness::EXPECTED; - let expected_bool: bool = witness::EXPECTED_BOOL; - let second_expected: u256 = witness::SECOND_EXPECTED; /// Arithmetic match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_256(a), expected_bool); }, false => {}, }; match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_256(a, b), expected_bool); }, false => {}, }; match if_test_this_function(2, fn_idx) { true => { assert_bool(le_256(a, b), expected_bool); }, false => {}, }; - - match if_test_this_function(3, fn_idx) { - true => { - let (res1, res2, res3, res4): (u64, u64, u64, u64) = split_256_into_64(a); - - let (high, low): (u128, u128) = ::into(unwrap(expected)); - - let (expected1, expected2): (u64, u64) = ::into(high); - let (expected3, expected4): (u64, u64) = ::into(low); - - assert!(jet::eq_64(res1, expected1)); - assert!(jet::eq_64(res2, expected2)); - assert!(jet::eq_64(res3, expected3)); - assert!(jet::eq_64(res4, expected4)); - }, - false => {}, - }; - - match if_test_this_function(4, fn_idx) { true => { assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; - match if_test_this_function(5, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); assert_eq_uint_bool(add_256_128(a, b), unwrap(expected), expected_bool); }, false => {}, }; } diff --git a/simf/u256_test_arithmetic_2.simf b/simf/u256_test_arithmetic_2.simf index 7172f4c..98c889b 100644 --- a/simf/u256_test_arithmetic_2.simf +++ b/simf/u256_test_arithmetic_2.simf @@ -1,14 +1,4 @@ -use crate::lib::u256::{ - eq_256, - split_256_into_64, - sub_256, - mul_256, - div_mod_256_64, - div_mod_256_128, - div_mod_256, - div_256 -}; -use crate::lib::u128::eq_128; +use crate::lib::u256::{eq_256, split_256_into_64, add_256, add_256_128}; use crate::helper::{ if_test_this_function, assert_bool @@ -34,59 +24,27 @@ fn main() { let a: u256 = witness::FIRST_ARG; let b: u256 = witness::SECOND_ARG; let expected: Option = witness::EXPECTED; - let expected_bool: bool = witness::EXPECTED_BOOL; - let second_expected: u256 = witness::SECOND_EXPECTED; /// Arithmetic - match if_test_this_function(0, fn_idx) { true => { assert_eq_uint_bool(sub_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; - - match if_test_this_function(1, fn_idx) { + match if_test_this_function(0, fn_idx) { true => { - let (result_high, result_low): (u256,u256) = mul_256(a, b); + let (res1, res2, res3, res4): (u64, u64, u64, u64) = split_256_into_64(a); - assert!(eq_256(result_high, unwrap(expected))); - assert!(eq_256(result_low, second_expected)); - }, - false => {}, - }; + let (high, low): (u128, u128) = ::into(unwrap(expected)); - match if_test_this_function(2, fn_idx) { - true => { - let (_, _, _, b): (u64, u64, u64, u64) = split_256_into_64(b); - let (_, _, _, expected_r): (u64, u64, u64, u64) = split_256_into_64(second_expected); - - let (q, r): (u256, u64) = div_mod_256_64(a, b); - - assert!(eq_256(q, unwrap(expected))); - assert!(jet::eq_64(r, expected_r)); - }, - false => {}, - }; - - match if_test_this_function(3, fn_idx) { - true => { - let (_, b): (u128, u128) = ::into(b); - let (_, expected_r): (u128, u128) = ::into(second_expected); - - let (q, r): (u256, u128) = div_mod_256_128(a, b); - - assert!(eq_256(q, unwrap(expected))); - assert!(eq_128(r, expected_r)); - }, - false => {}, - }; - - match if_test_this_function(4, fn_idx) { - true => { - let (q, r): (u256, u256) = div_mod_256(a, b); + let (expected1, expected2): (u64, u64) = ::into(high); + let (expected3, expected4): (u64, u64) = ::into(low); - assert!(eq_256(q, unwrap(expected))); - assert!(eq_256(r, second_expected)); + assert!(jet::eq_64(res1, expected1)); + assert!(jet::eq_64(res2, expected2)); + assert!(jet::eq_64(res3, expected3)); + assert!(jet::eq_64(res4, expected4)); }, false => {}, }; - match if_test_this_function(5, fn_idx) { true => { assert!(eq_256(div_256(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(1, fn_idx) { true => { assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; + match if_test_this_function(2, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); assert_eq_uint_bool(add_256_128(a, b), unwrap(expected), expected_bool); }, false => {}, }; } diff --git a/simf/u256_test_arithmetic_3.simf b/simf/u256_test_arithmetic_3.simf new file mode 100644 index 0000000..9d322b6 --- /dev/null +++ b/simf/u256_test_arithmetic_3.simf @@ -0,0 +1,57 @@ +use crate::lib::u256::{eq_256, split_256_into_64, sub_256, mul_256, div_mod_256_64}; +use crate::helper::{ + if_test_this_function, + assert_bool +}; + +/// Asserts a result equals expected u256 and bool values. +/// Used for functions that return carry or borrow bool value. +fn assert_eq_uint_bool( + result: (bool, u256), + expected: u256, + expected_bool: bool +) { + let (bool_res, uint_res): (bool, u256) = result; + + assert_bool(bool_res, expected_bool); + + assert!(eq_256(uint_res, expected)); +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + let expected_bool: bool = witness::EXPECTED_BOOL; + let second_expected: u256 = witness::SECOND_EXPECTED; + + /// Arithmetic + + match if_test_this_function(0, fn_idx) { true => { assert_eq_uint_bool(sub_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; + + match if_test_this_function(1, fn_idx) { + true => { + let (result_high, result_low): (u256,u256) = mul_256(a, b); + + assert!(eq_256(result_high, unwrap(expected))); + assert!(eq_256(result_low, second_expected)); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + let (_, _, _, b): (u64, u64, u64, u64) = split_256_into_64(b); + let (_, _, _, expected_r): (u64, u64, u64, u64) = split_256_into_64(second_expected); + + let (q, r): (u256, u64) = div_mod_256_64(a, b); + + assert!(eq_256(q, unwrap(expected))); + assert!(jet::eq_64(r, expected_r)); + }, + false => {}, + }; +} diff --git a/simf/u256_test_arithmetic_4.simf b/simf/u256_test_arithmetic_4.simf new file mode 100644 index 0000000..4e11355 --- /dev/null +++ b/simf/u256_test_arithmetic_4.simf @@ -0,0 +1,40 @@ +use crate::lib::u256::{eq_256, div_mod_256_128, div_mod_256, div_256}; +use crate::lib::u128::eq_128; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + let second_expected: u256 = witness::SECOND_EXPECTED; + + /// Arithmetic + + match if_test_this_function(0, fn_idx) { + true => { + let (_, b): (u128, u128) = ::into(b); + let (_, expected_r): (u128, u128) = ::into(second_expected); + + let (q, r): (u256, u128) = div_mod_256_128(a, b); + + assert!(eq_256(q, unwrap(expected))); + assert!(eq_128(r, expected_r)); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + let (q, r): (u256, u256) = div_mod_256(a, b); + + assert!(eq_256(q, unwrap(expected))); + assert!(eq_256(r, second_expected)); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { true => { assert!(eq_256(div_256(a, b), unwrap(expected))); }, false => {}, }; +} diff --git a/tests/u256_test_arithmetic_1.rs b/tests/u256_test_arithmetic_1.rs index 1f5443a..dcf71e6 100644 --- a/tests/u256_test_arithmetic_1.rs +++ b/tests/u256_test_arithmetic_1.rs @@ -2,7 +2,7 @@ mod common; use primitive_types::U256; -use crate::common::helper::{DEFAULT_BOOL, generate_u256}; +use crate::common::helper::generate_u256; use common::core::{Expect, run}; use simplicityhl_std::artifacts::u256_test_arithmetic_1::U256TestArithmetic1Program; @@ -14,9 +14,6 @@ enum FunctionToTest { IsZero256, Lt256, Le256, - Split256Into64, - Add256, - Add256_128, } #[inline] @@ -34,17 +31,13 @@ fn build_witness( function: u8, a: [u8; 32], b: [u8; 32], - expected: Option<[u8; 32]>, expected_bool: bool, - second_expected: [u8; 32], ) -> U256TestArithmetic1Witness { U256TestArithmetic1Witness { function_index: function, first_arg: a, second_arg: b, - expected, expected_bool, - second_expected, } } @@ -58,14 +51,7 @@ mod u256_tests_arithmetic { run( &context, program(), - build_witness( - op(FunctionToTest::IsZero256), - a, - DEFAULT_EXPECTED, - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - ), + build_witness(op(FunctionToTest::IsZero256), a, DEFAULT_EXPECTED, true), Expect::Ok, ) } @@ -77,14 +63,7 @@ mod u256_tests_arithmetic { run( &context, program(), - build_witness( - op(FunctionToTest::IsZero256), - a, - DEFAULT_EXPECTED, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - ), + build_witness(op(FunctionToTest::IsZero256), a, DEFAULT_EXPECTED, false), Expect::Ok, ) } @@ -101,9 +80,7 @@ mod u256_tests_arithmetic { op(FunctionToTest::Lt256), a.to_big_endian(), b.to_big_endian(), - Some(DEFAULT_EXPECTED), true, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -116,14 +93,7 @@ mod u256_tests_arithmetic { run( &context, program(), - build_witness( - op(FunctionToTest::Lt256), - a, - a, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - ), + build_witness(op(FunctionToTest::Lt256), a, a, false), Expect::Ok, ) } @@ -140,9 +110,7 @@ mod u256_tests_arithmetic { op(FunctionToTest::Lt256), a.to_big_endian(), b.to_big_endian(), - Some(DEFAULT_EXPECTED), false, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -160,9 +128,7 @@ mod u256_tests_arithmetic { op(FunctionToTest::Le256), a.to_big_endian(), b.to_big_endian(), - Some(DEFAULT_EXPECTED), true, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -175,14 +141,7 @@ mod u256_tests_arithmetic { run( &context, program(), - build_witness( - op(FunctionToTest::Le256), - a, - a, - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - ), + build_witness(op(FunctionToTest::Le256), a, a, true), Expect::Ok, ) } @@ -199,112 +158,7 @@ mod u256_tests_arithmetic { op(FunctionToTest::Le256), a.to_big_endian(), b.to_big_endian(), - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_split_256_into_64(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Split256Into64), - a, - DEFAULT_EXPECTED, - Some(a), - DEFAULT_BOOL, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_add_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX / 2); - let b = generate_u256(U256::zero(), U256::MAX / 2); - let result = (a + b).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add256), - a.to_big_endian(), - b.to_big_endian(), - Some(result), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_add_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = U256::MAX; - let b = generate_u256(U256::one(), U256::MAX); - let result = (b - 1).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add256), - a.to_big_endian(), - b.to_big_endian(), - Some(result), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_add_256_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX / 2); - let b = generate_u256(U256::one(), U256::from(u128::MAX)) as U256; - let result = (a + b).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add256_128), - a.to_big_endian(), - b.to_big_endian(), - Some(result), false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_add_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = U256::MAX; - let b = generate_u256(U256::one(), U256::from(u128::MAX)) as U256; - let result = (b - 1).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add256_128), - a.to_big_endian(), - b.to_big_endian(), - Some(result), - true, - DEFAULT_EXPECTED, ), Expect::Ok, ) diff --git a/tests/u256_test_arithmetic_2.rs b/tests/u256_test_arithmetic_2.rs index 7016aea..0def283 100644 --- a/tests/u256_test_arithmetic_2.rs +++ b/tests/u256_test_arithmetic_2.rs @@ -1,7 +1,6 @@ mod common; use primitive_types::U256; -use rand::Rng; use crate::common::helper::{DEFAULT_BOOL, generate_u256}; use common::core::{Expect, run}; @@ -12,12 +11,9 @@ use simplicityhl_std::artifacts::u256_test_arithmetic_2::derived_u256_test_arith }; enum FunctionToTest { - Sub256, - Mul256, - DivMod256_64, - DivMod256_128, - DivMod256, - Div256, + Split256Into64, + Add256, + Add256_128, } #[inline] @@ -37,7 +33,6 @@ fn build_witness( b: [u8; 32], expected: Option<[u8; 32]>, expected_bool: bool, - second_expected: [u8; 32], ) -> U256TestArithmetic2Witness { U256TestArithmetic2Witness { function_index: function, @@ -45,533 +40,107 @@ fn build_witness( second_arg: b, expected, expected_bool, - second_expected, } } -fn split_u512(a: [u8; 64]) -> ([u8; 32], [u8; 32]) { - let high = U256::from_big_endian(&a[0..32]); - let low = U256::from_big_endian(&a[32..64]); - - (high.to_big_endian(), low.to_big_endian()) -} - mod u256_tests_arithmetic { use super::*; #[simplex::test] - fn u256_test_sub_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX); - let b = generate_u256(U256::zero(), a); - let result = (a - b).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Sub256), - a.to_big_endian(), - b.to_big_endian(), - Some(result), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_sub_256_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + fn u128_test_split_256_into_64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); run( &context, program(), build_witness( - op(FunctionToTest::Sub256), - a, + op(FunctionToTest::Split256Into64), a, - Some([0; 32]), - false, DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_sub_256_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX); - let b_high = rand::thread_rng().gen_range(0..=u128::MAX); - - let low: u128 = a.low_u128(); - let b = (U256::from(b_high) << 128) | U256::from(low); - - let (result, carry) = a.overflowing_sub(b); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Sub256), - a.to_big_endian(), - b.to_big_endian(), - Some(result.to_big_endian()), - carry, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_sub_256_diff_is_u128_max(context: simplex::TestContext) -> anyhow::Result<()> { - let a_low: u128 = u128::MAX; - - let a_high = rand::thread_rng().gen_range(0..=u128::MAX); - let b_high = rand::thread_rng().gen_range(0..=u128::MAX); - - let a = (U256::from(a_high) << 128) | U256::from(a_low); - let b = (U256::from(b_high)) << 128; // b_low is 0 - - //let carry = a < b; - let (result, carry) = a.overflowing_sub(b); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Sub256), - a.to_big_endian(), - b.to_big_endian(), - Some(result.to_big_endian()), - carry, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_sub_256_diff_is_u256_max(context: simplex::TestContext) -> anyhow::Result<()> { - let a = U256::MAX.to_big_endian(); - let b = U256::zero(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Sub256), - a, - b.to_big_endian(), Some(a), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_sub_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX - 1); - let b = U256::MAX; - let result = a + 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Sub256), - a.to_big_endian(), - b.to_big_endian(), - Some(result.to_big_endian()), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_mul_256(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX); - let b = generate_u256(U256::one(), U256::MAX); - let result = a.full_mul(b).to_big_endian(); - - let (result_high, result_low) = split_u512(result); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Mul256), - a.to_big_endian(), - b.to_big_endian(), - Some(result_high), DEFAULT_BOOL, - result_low, ), Expect::Ok, ) } #[simplex::test] - fn test_div_mod_256_64(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX); - let b = generate_u256(U256::one(), U256::from(u64::MAX)); - - let q = (a / b).to_big_endian(); - let r = (a % b).to_big_endian(); + fn u256_test_add_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX / 2); + let b = generate_u256(U256::zero(), U256::MAX / 2); + let result = (a + b).to_big_endian(); run( &context, program(), build_witness( - op(FunctionToTest::DivMod256_64), + op(FunctionToTest::Add256), a.to_big_endian(), b.to_big_endian(), - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn test_div_mod_256_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX); - let b = [0; 32]; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256_64), - a.to_big_endian(), - b, - Some(DEFAULT_EXPECTED), - DEFAULT_BOOL, - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn test_div_mod_256_128(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX); - let b = generate_u256(U256::from(u64::MAX) + 1, U256::from(u128::MAX)); - - let q = (a / b).to_big_endian(); - let r = (a % b).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256_128), - a.to_big_endian(), - b.to_big_endian(), - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn test_div_mod_256_128_b_fits_into_u64(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX); - let b = generate_u256(U256::one(), U256::from(u64::MAX)); - - let q = (a / b).to_big_endian(); - let r = (a % b).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256_128), - a.to_big_endian(), - b.to_big_endian(), - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn test_div_mod_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX); - let b = [0; 32]; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256_128), - a.to_big_endian(), - b, - Some(DEFAULT_EXPECTED), - DEFAULT_BOOL, - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn test_div_mod_256_128_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { - let a = (generate_u256(U256::one(), U256::from(u128::MAX))).to_big_endian(); - - let q = U256::one().to_big_endian(); - let r = U256::zero().to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256_128), - a, - a, - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_div_mod_256_a_less_than_b(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX - 1); - let b = generate_u256(a + 1, U256::MAX); - - let q = (a / b).to_big_endian(); - let r = (a % b).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256), - a.to_big_endian(), - b.to_big_endian(), - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_div_mod_256_div_128(context: simplex::TestContext) -> anyhow::Result<()> { - let b = generate_u256(U256::one(), U256::from(u128::MAX)); - let a = generate_u256(b, U256::from(u128::MAX)); - - let q = (a / b).to_big_endian(); - let r = (a % b).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256), - a.to_big_endian(), - b.to_big_endian(), - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_div_mod_256_q_is_1(context: simplex::TestContext) -> anyhow::Result<()> { - // case where a >= b and a_high = b_high != 0 - let b_low = generate_u256(U256::zero(), U256::from(u128::MAX)); - let a_low = generate_u256(b_low, U256::from(u128::MAX)); - let high = generate_u256(U256::one(), U256::from(u128::MAX)); - - let a = ((high as U256) << 128) | (a_low as U256); - let b = ((high as U256) << 128) | (b_low as U256); - - let q = (a / b).to_big_endian(); - let r = (a % b).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256), - a.to_big_endian(), - b.to_big_endian(), - Some(q), - DEFAULT_BOOL, - r, + Some(result), + false, ), Expect::Ok, ) } #[simplex::test] - fn u256_test_div_mod_256_b_fits_into_u128(context: simplex::TestContext) -> anyhow::Result<()> { - let b = generate_u256(U256::one(), U256::from(u128::MAX)); - let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); - - let q = (a / b).to_big_endian(); - let r = (a % b).to_big_endian(); + fn u256_test_add_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::MAX); + let result = (b - 1).to_big_endian(); run( &context, program(), build_witness( - op(FunctionToTest::DivMod256), + op(FunctionToTest::Add256), a.to_big_endian(), b.to_big_endian(), - Some(q), - DEFAULT_BOOL, - r, + Some(result), + true, ), Expect::Ok, ) } #[simplex::test] - fn u256_test_div_mod_256_b_is_u256(context: simplex::TestContext) -> anyhow::Result<()> { - let b = generate_u256(U256::one(), U256::MAX - 1); - let a = generate_u256(b + 1, U256::MAX); - - let q = (a / b).to_big_endian(); - let r = (a % b).to_big_endian(); + fn u256_test_add_256_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX / 2); + let b = generate_u256(U256::one(), U256::from(u128::MAX)) as U256; + let result = (a + b).to_big_endian(); run( &context, program(), build_witness( - op(FunctionToTest::DivMod256), + op(FunctionToTest::Add256_128), a.to_big_endian(), b.to_big_endian(), - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_div_mod_256_a_equal_b(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256), - a, - a, - Some(U256::one().to_big_endian()), - DEFAULT_BOOL, - [0; 32], - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_div_mod_256_equal_high_words_max_low_diff( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let high = generate_u256(U256::one(), U256::from(u128::MAX)); - - let a = ((high << 128) | (U256::from(u128::MAX))).to_big_endian(); - let b = (high << 128).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256), - a, - b, - Some(U256::one().to_big_endian()), - DEFAULT_BOOL, - U256::from(u128::MAX).to_big_endian(), - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_div_mod_256_eq_high_words_a_less_than_b( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let high = generate_u256(U256::one(), U256::from(u128::MAX)); - - let a = ((high as U256) << 128).to_big_endian(); - let b = (((high as U256) << 128) | (U256::from(u128::MAX))).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256), - a, - b, - Some([0; 32]), - DEFAULT_BOOL, - a, + Some(result), + false, ), Expect::Ok, ) } #[simplex::test] - fn u256_test_div_256(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX); - let b = generate_u256(U256::one(), U256::MAX); - let result = (a / b).to_big_endian(); + fn u256_test_add_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::from(u128::MAX)) as U256; + let result = (b - 1).to_big_endian(); run( &context, program(), build_witness( - op(FunctionToTest::Div256), + op(FunctionToTest::Add256_128), a.to_big_endian(), b.to_big_endian(), Some(result), - DEFAULT_BOOL, - DEFAULT_EXPECTED, + true, ), Expect::Ok, ) } - - #[simplex::test] - fn u256_test_div_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX); - let b = [0; 32]; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Div256), - a.to_big_endian(), - b, - Some(DEFAULT_EXPECTED), - DEFAULT_BOOL, - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } } diff --git a/tests/u256_test_arithmetic_3.rs b/tests/u256_test_arithmetic_3.rs new file mode 100644 index 0000000..eacd589 --- /dev/null +++ b/tests/u256_test_arithmetic_3.rs @@ -0,0 +1,257 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use crate::common::helper::{DEFAULT_BOOL, generate_u256}; +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_test_arithmetic_3::U256TestArithmetic3Program; +use simplicityhl_std::artifacts::u256_test_arithmetic_3::derived_u256_test_arithmetic_3::{ + U256TestArithmetic3Arguments, U256TestArithmetic3Witness, +}; + +enum FunctionToTest { + Sub256, + Mul256, + DivMod256_64, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestArithmetic3Program { + U256TestArithmetic3Program::new(U256TestArithmetic3Arguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, + expected_bool: bool, + second_expected: [u8; 32], +) -> U256TestArithmetic3Witness { + U256TestArithmetic3Witness { + function_index: function, + first_arg: a, + second_arg: b, + expected, + expected_bool, + second_expected, + } +} + +fn split_u512(a: [u8; 64]) -> ([u8; 32], [u8; 32]) { + let high = U256::from_big_endian(&a[0..32]); + let low = U256::from_big_endian(&a[32..64]); + + (high.to_big_endian(), low.to_big_endian()) +} + +mod u256_tests_arithmetic { + use super::*; + + #[simplex::test] + fn u256_test_sub_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::zero(), a); + let result = (a - b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a, + a, + Some([0; 32]), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b_high = rand::thread_rng().gen_range(0..=u128::MAX); + + let low: u128 = a.low_u128(); + let b = (U256::from(b_high) << 128) | U256::from(low); + + let (result, carry) = a.overflowing_sub(b); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result.to_big_endian()), + carry, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_diff_is_u128_max(context: simplex::TestContext) -> anyhow::Result<()> { + let a_low: u128 = u128::MAX; + + let a_high = rand::thread_rng().gen_range(0..=u128::MAX); + let b_high = rand::thread_rng().gen_range(0..=u128::MAX); + + let a = (U256::from(a_high) << 128) | U256::from(a_low); + let b = (U256::from(b_high)) << 128; // b_low is 0 + + let (result, carry) = a.overflowing_sub(b); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result.to_big_endian()), + carry, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_diff_is_u256_max(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX.to_big_endian(); + let b = U256::zero(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a, + b.to_big_endian(), + Some(a), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX - 1); + let b = U256::MAX; + let result = a + 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result.to_big_endian()), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::MAX); + let result = a.full_mul(b).to_big_endian(); + + let (result_high, result_low) = split_u512(result); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Mul256), + a.to_big_endian(), + b.to_big_endian(), + Some(result_high), + DEFAULT_BOOL, + result_low, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u64::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_64), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + DEFAULT_BOOL, + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_64), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_BOOL, + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u256_test_arithmetic_4.rs b/tests/u256_test_arithmetic_4.rs new file mode 100644 index 0000000..e197266 --- /dev/null +++ b/tests/u256_test_arithmetic_4.rs @@ -0,0 +1,338 @@ +mod common; + +use primitive_types::U256; + +use crate::common::helper::generate_u256; +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_test_arithmetic_4::U256TestArithmetic4Program; +use simplicityhl_std::artifacts::u256_test_arithmetic_4::derived_u256_test_arithmetic_4::{ + U256TestArithmetic4Arguments, U256TestArithmetic4Witness, +}; + +enum FunctionToTest { + DivMod256_128, + DivMod256, + Div256, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestArithmetic4Program { + U256TestArithmetic4Program::new(U256TestArithmetic4Arguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, + second_expected: [u8; 32], +) -> U256TestArithmetic4Witness { + U256TestArithmetic4Witness { + function_index: function, + first_arg: a, + second_arg: b, + expected, + second_expected, + } +} + +mod u256_tests_arithmetic { + use super::*; + + #[simplex::test] + fn test_div_mod_256_128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::from(u64::MAX) + 1, U256::from(u128::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_128_b_fits_into_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u64::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_128), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn test_div_mod_256_128_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = (generate_u256(U256::one(), U256::from(u128::MAX))).to_big_endian(); + + let q = U256::one().to_big_endian(); + let r = U256::zero().to_big_endian(); + + run( + &context, + program(), + build_witness(op(FunctionToTest::DivMod256_128), a, a, Some(q), r), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_a_less_than_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX - 1); + let b = generate_u256(a + 1, U256::MAX); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_div_128(context: simplex::TestContext) -> anyhow::Result<()> { + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let a = generate_u256(b, U256::from(u128::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_q_is_1(context: simplex::TestContext) -> anyhow::Result<()> { + // case where a >= b and a_high = b_high != 0 + let b_low = generate_u256(U256::zero(), U256::from(u128::MAX)); + let a_low = generate_u256(b_low, U256::from(u128::MAX)); + let high = generate_u256(U256::one(), U256::from(u128::MAX)); + + let a = ((high as U256) << 128) | (a_low as U256); + let b = ((high as U256) << 128) | (b_low as U256); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_b_fits_into_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_b_is_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let b = generate_u256(U256::one(), U256::MAX - 1); + let a = generate_u256(b + 1, U256::MAX); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_a_equal_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a, + a, + Some(U256::one().to_big_endian()), + [0; 32], + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_equal_high_words_max_low_diff( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let high = generate_u256(U256::one(), U256::from(u128::MAX)); + + let a = ((high << 128) | (U256::from(u128::MAX))).to_big_endian(); + let b = (high << 128).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a, + b, + Some(U256::one().to_big_endian()), + U256::from(u128::MAX).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_eq_high_words_a_less_than_b( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let high = generate_u256(U256::one(), U256::from(u128::MAX)); + + let a = ((high as U256) << 128).to_big_endian(); + let b = (((high as U256) << 128) | (U256::from(u128::MAX))).to_big_endian(); + + run( + &context, + program(), + build_witness(op(FunctionToTest::DivMod256), a, b, Some([0; 32]), a), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::one(), U256::MAX); + let result = (a / b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Div256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Div256), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } +} From d7761098adaf78e2cdd5814d2c5674b53846dc15 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Mon, 3 Aug 2026 19:37:42 +0300 Subject: [PATCH 10/15] typo --- tests/u128_test_arithmetic.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/u128_test_arithmetic.rs b/tests/u128_test_arithmetic.rs index 620d2b1..e3efe9b 100644 --- a/tests/u128_test_arithmetic.rs +++ b/tests/u128_test_arithmetic.rs @@ -650,7 +650,7 @@ mod u128_tests_arithmetic { ) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..u128::MAX); let b = u128::MAX; - let (result, result_borrow) = a.overflowing_div(b); + let (result, result_borrow) = a.overflowing_sub(b); let borrow_low = 1_u128; From 1318844aef3c1faab0179af81a1cc4333e7b42d0 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Tue, 4 Aug 2026 17:25:34 +0300 Subject: [PATCH 11/15] removed unnecessary eq_256 --- simf/lib/u256.simf | 8 ------- simf/u256_test.simf | 19 ++++++++++------ simf/u256_test_arithmetic_2.simf | 5 +++-- simf/u256_test_arithmetic_3.simf | 11 ++++----- simf/u256_test_arithmetic_4.simf | 11 ++++----- simf/u256_test_bits.simf | 16 +++++++------- tests/u256_test_bits.rs | 38 -------------------------------- 7 files changed, 35 insertions(+), 73 deletions(-) diff --git a/simf/lib/u256.simf b/simf/lib/u256.simf index c919926..2463c3f 100644 --- a/simf/lib/u256.simf +++ b/simf/lib/u256.simf @@ -40,14 +40,6 @@ pub fn or_256(a: u256, b: u256) -> u256 { <(u128, u128)>::into((or_128(a_high, b_high), or_128(a_low, b_low))) } -/// Checks if two 256-bit values are equal -pub fn eq_256(a: u256, b: u256) -> bool { - let (a_high, a_low): (u128, u128) = ::into(a); - let (b_high, b_low): (u128, u128) = ::into(b); - - and(eq_128(a_high, b_high), eq_128(a_low, b_low)) -} - /// Left-shift a 256-bit value by the given amount. Bits are filled with zeroes pub fn left_shift_256(shift: u8, a: u256) -> u256 { match jet::is_zero_8(shift) { diff --git a/simf/u256_test.simf b/simf/u256_test.simf index 9725a48..12984c7 100644 --- a/simf/u256_test.simf +++ b/simf/u256_test.simf @@ -7,7 +7,6 @@ use crate::lib::u256::{ safe_mul_256, checked_div_256, safe_div_256, - eq_256, gt_256, ge_256 }; @@ -18,7 +17,10 @@ use crate::helper::if_test_this_function; /// Asserts a `checked_*` result equals the expected Option. /// `None` encodes the overflow case, `Some(e)` the fitting case, so a single /// witness value carries both, removing the need for a separate overflow flag. -fn assert_eq_opt(result: Option, expected: Option) { +fn assert_eq_opt( + result: Option, + expected: Option +) { match expected { None => assert_none_256(result), Some(e: u256) => assert_eq_256(unwrap(result), e), @@ -27,7 +29,10 @@ fn assert_eq_opt(result: Option, expected: Option) { /// Asserts a result equals the expected bool value. /// `None` encodes `false`, and `Some(_)` encodes `true`. -fn assert_bool_by_opt(result: bool, expected: Option) { +fn assert_bool_by_opt( + result: bool, + expected: Option +) { match expected { Some(_: u256) => assert!(result), None => assert!(not(result)), @@ -45,19 +50,19 @@ fn main() { // add match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_256(a, b), expected); }, false => {}, }; - match if_test_this_function(1, fn_idx) { true => { assert!(eq_256(safe_add_256(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(1, fn_idx) { true => {assert_eq_256(safe_add_256(a, b), unwrap(expected)); }, false => {}, }; // sub match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_256(a, b), expected); }, false => {}, }; - match if_test_this_function(3, fn_idx) { true => { assert!(eq_256(safe_sub_256(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(3, fn_idx) { true => {assert_eq_256(safe_sub_256(a, b), unwrap(expected)); }, false => {}, }; // mul match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_256(a, b), expected); }, false => {}, }; - match if_test_this_function(5, fn_idx) { true => { assert!(eq_256(safe_mul_256(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(5, fn_idx) { true => {assert_eq_256(safe_mul_256(a, b), unwrap(expected)); }, false => {}, }; // div match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_256(a, b), expected); }, false => {}, }; - match if_test_this_function(7, fn_idx) { true => { assert!(eq_256(safe_div_256(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(7, fn_idx) { true => {assert_eq_256(safe_div_256(a, b), unwrap(expected)); }, false => {}, }; // gt, ge match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_256(a, b), expected); }, false => {}, }; diff --git a/simf/u256_test_arithmetic_2.simf b/simf/u256_test_arithmetic_2.simf index 98c889b..214ab19 100644 --- a/simf/u256_test_arithmetic_2.simf +++ b/simf/u256_test_arithmetic_2.simf @@ -1,4 +1,5 @@ -use crate::lib::u256::{eq_256, split_256_into_64, add_256, add_256_128}; +use crate::lib::u256::{split_256_into_64, add_256, add_256_128}; +use crate::lib::asserts::assert_eq_256; use crate::helper::{ if_test_this_function, assert_bool @@ -15,7 +16,7 @@ fn assert_eq_uint_bool( assert_bool(bool_res, expected_bool); - assert!(eq_256(uint_res, expected)); + assert_eq_256(uint_res, expected); } fn main() { diff --git a/simf/u256_test_arithmetic_3.simf b/simf/u256_test_arithmetic_3.simf index 9d322b6..2512dbe 100644 --- a/simf/u256_test_arithmetic_3.simf +++ b/simf/u256_test_arithmetic_3.simf @@ -1,4 +1,5 @@ -use crate::lib::u256::{eq_256, split_256_into_64, sub_256, mul_256, div_mod_256_64}; +use crate::lib::u256::{split_256_into_64, sub_256, mul_256, div_mod_256_64}; +use crate::lib::asserts::assert_eq_256; use crate::helper::{ if_test_this_function, assert_bool @@ -15,7 +16,7 @@ fn assert_eq_uint_bool( assert_bool(bool_res, expected_bool); - assert!(eq_256(uint_res, expected)); + assert_eq_256(uint_res, expected); } fn main() { @@ -36,8 +37,8 @@ fn main() { true => { let (result_high, result_low): (u256,u256) = mul_256(a, b); - assert!(eq_256(result_high, unwrap(expected))); - assert!(eq_256(result_low, second_expected)); + assert_eq_256(result_high, unwrap(expected)); + assert_eq_256(result_low, second_expected); }, false => {}, }; @@ -49,7 +50,7 @@ fn main() { let (q, r): (u256, u64) = div_mod_256_64(a, b); - assert!(eq_256(q, unwrap(expected))); + assert_eq_256(q, unwrap(expected)); assert!(jet::eq_64(r, expected_r)); }, false => {}, diff --git a/simf/u256_test_arithmetic_4.simf b/simf/u256_test_arithmetic_4.simf index 4e11355..044babe 100644 --- a/simf/u256_test_arithmetic_4.simf +++ b/simf/u256_test_arithmetic_4.simf @@ -1,5 +1,6 @@ -use crate::lib::u256::{eq_256, div_mod_256_128, div_mod_256, div_256}; +use crate::lib::u256::{div_mod_256_128, div_mod_256, div_256}; use crate::lib::u128::eq_128; +use crate::lib::asserts::assert_eq_256; use crate::helper::if_test_this_function; fn main() { @@ -20,7 +21,7 @@ fn main() { let (q, r): (u256, u128) = div_mod_256_128(a, b); - assert!(eq_256(q, unwrap(expected))); + assert_eq_256(q, unwrap(expected)); assert!(eq_128(r, expected_r)); }, false => {}, @@ -30,11 +31,11 @@ fn main() { true => { let (q, r): (u256, u256) = div_mod_256(a, b); - assert!(eq_256(q, unwrap(expected))); - assert!(eq_256(r, second_expected)); + assert_eq_256(q, unwrap(expected)); + assert_eq_256(r, second_expected); }, false => {}, }; - match if_test_this_function(2, fn_idx) { true => { assert!(eq_256(div_256(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(2, fn_idx) { true => { assert_eq_256(div_256(a, b), unwrap(expected)); }, false => {}, }; } diff --git a/simf/u256_test_bits.simf b/simf/u256_test_bits.simf index ca624b4..c0273d4 100644 --- a/simf/u256_test_bits.simf +++ b/simf/u256_test_bits.simf @@ -1,4 +1,5 @@ -use crate::lib::u256::{and_256, or_256, eq_256, left_shift_256, right_shift_256, split_256_into_64}; +use crate::lib::u256::{and_256, or_256, left_shift_256, right_shift_256, split_256_into_64}; +use crate::lib::asserts::assert_eq_256; use crate::helper::{if_test_this_function, assert_bool}; fn main() { @@ -12,25 +13,24 @@ fn main() { /// Bit logic - match if_test_this_function(0, fn_idx) { true => { assert!(eq_256(and_256(a, b), unwrap(expected))); }, false => {}, }; - match if_test_this_function(1, fn_idx) { true => { assert!(eq_256(or_256(a, b), unwrap(expected))); }, false => {}, }; - match if_test_this_function(2, fn_idx) { true => { assert_bool(eq_256(a, b), expected_bool); }, false => {}, }; + match if_test_this_function(0, fn_idx) { true => { assert_eq_256(and_256(a, b), unwrap(expected)); }, false => {}, }; + match if_test_this_function(1, fn_idx) { true => { assert_eq_256(or_256(a, b), unwrap(expected)); }, false => {}, }; - match if_test_this_function(3, fn_idx) { + match if_test_this_function(2, fn_idx) { true => { let (_, _, _, a): (u64, u64, u64, u64) = split_256_into_64(a); let shift: u8 = jet::rightmost_64_8(a); - assert!(eq_256(left_shift_256(shift, b), unwrap(expected))); + assert_eq_256(left_shift_256(shift, b), unwrap(expected)); }, false => {}, }; - match if_test_this_function(4, fn_idx) { + match if_test_this_function(3, fn_idx) { true => { let (_, _, _, a): (u64, u64, u64, u64) = split_256_into_64(a); let shift: u8 = jet::rightmost_64_8(a); - assert!(eq_256(right_shift_256(shift, b), unwrap(expected))); + assert_eq_256(right_shift_256(shift, b), unwrap(expected)); }, false => {}, }; } diff --git a/tests/u256_test_bits.rs b/tests/u256_test_bits.rs index a1df22c..35eed9f 100644 --- a/tests/u256_test_bits.rs +++ b/tests/u256_test_bits.rs @@ -14,7 +14,6 @@ use simplicityhl_std::artifacts::u256_test_bits::derived_u256_test_bits::{ enum FunctionToTest { And256, Or256, - Eq256, LeftShift256, RightShift256, } @@ -89,43 +88,6 @@ mod u256_tests_bits { ) } - #[simplex::test] - fn u256_test_eq_256_true(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Eq256), - a, - a, - Some(DEFAULT_EXPECTED), - true, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_eq_256_false(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX); - let b = a - 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Eq256), - a.to_big_endian(), - b.to_big_endian(), - Some(DEFAULT_EXPECTED), - false, - ), - Expect::Ok, - ) - } - #[simplex::test] fn u256_test_left_shift_256(context: simplex::TestContext) -> anyhow::Result<()> { let shift = rand::thread_rng().gen_range(1..=127_u8); From 2ee8d1bc062f06a1e83e79b7290c5cc53f7ad2cc Mon Sep 17 00:00:00 2001 From: aritkulova Date: Thu, 6 Aug 2026 17:23:13 +0300 Subject: [PATCH 12/15] linting --- simf/lib/u128.simf | 40 +++------ simf/lib/u256.simf | 138 ++++++++----------------------- simf/u128_test.simf | 16 +++- simf/u128_test_arithmetic.simf | 31 +++---- simf/u128_test_bits.simf | 4 +- simf/u256_test.simf | 32 +++---- simf/u256_test_arithmetic_1.simf | 6 +- simf/u256_test_arithmetic_2.simf | 15 +--- simf/u256_test_arithmetic_3.simf | 13 +-- simf/u256_test_arithmetic_4.simf | 2 +- simf/u256_test_bits.simf | 8 +- 11 files changed, 102 insertions(+), 203 deletions(-) diff --git a/simf/lib/u128.simf b/simf/lib/u128.simf index 4e5092f..c97c7ce 100644 --- a/simf/lib/u128.simf +++ b/simf/lib/u128.simf @@ -94,12 +94,10 @@ pub fn lt_128(a: u128, b: u128) -> bool { match jet::lt_64(a_high, b_high) { true => true, - false => { - match jet::eq_64(a_high, b_high) { - true => jet::lt_64(a_low, b_low), - false => false, - } - } + false => match jet::eq_64(a_high, b_high) { + true => jet::lt_64(a_low, b_low), + false => false, + }, } } @@ -110,13 +108,11 @@ pub fn le_128(a: u128, b: u128) -> bool { match jet::lt_64(a_high, b_high) { true => true, - false => { - match jet::eq_64(a_high, b_high) { - true => jet::le_64(a_low, b_low), - false => false, - } - } - } + false => match jet::eq_64(a_high, b_high) { + true => jet::le_64(a_low, b_low), + false => false, + }, + } } /// Check if an integer is greater than another integer @@ -308,11 +304,7 @@ pub fn calculate_normalizer_base_64(b: u128, is_b_u128: bool) -> u64 { /// If `is_b_u128` is false, expects `b` to fit into u64. /// /// Division algorithms operate in base 2^64, so the normalization threshold is 2^63 -fn normalize_to_threshold_128_63( - a: u128, - b: u128, - is_b_u128: bool -) -> (u256, u128, u64) { +fn normalize_to_threshold_128_63(a: u128, b: u128, is_b_u128: bool) -> (u256, u128, u64) { let norm: u64 = calculate_normalizer_base_64(b, is_b_u128); let norm_128: u128 = <(u64, u64)>::into((0, norm)); @@ -338,9 +330,7 @@ pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u let (carry, r_hat): (bool, u64) = jet::add_64(r_hat, v1); match carry { - true => { - q_hat - }, + true => q_hat, false => { let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); @@ -351,16 +341,12 @@ pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u q_hat } - false => { - q_hat - }, + false => q_hat, } } } }, - false => { - q_hat - }, + false => q_hat, } } diff --git a/simf/lib/u256.simf b/simf/lib/u256.simf index 2463c3f..92a7cf6 100644 --- a/simf/lib/u256.simf +++ b/simf/lib/u256.simf @@ -18,9 +18,7 @@ use crate::lib::u128::{ div_mod_128, div_mod_128_64 }; -use crate::lib::u64::{ - u64_into_u256 -}; +use crate::lib::u64::{u64_into_u256}; /// Bit logic @@ -95,9 +93,7 @@ pub fn right_shift_256(shift: u8, a: u256) -> u256 { /// Arithmetic /// Checks if an integer is zero -pub fn is_zero_256( - a: u256 -) -> bool { +pub fn is_zero_256(a: u256) -> bool { let (a_high, a_low): (u128, u128) = ::into(a); and(is_zero_128(a_high), is_zero_128(a_low)) @@ -109,40 +105,25 @@ pub fn lt_256(a: u256, b: u256) -> bool { let (b_high, b_low): (u128, u128) = ::into(b); match lt_128(a_high, b_high) { - false => { - match eq_128(a_high, b_high) { - false => { - false - }, - true => { - lt_128(a_low, b_low) - } - } - }, true => true, + false => match eq_128(a_high, b_high) { + true => lt_128(a_low, b_low), + false => false, + }, } } /// Checks if an integer is less than or equal to another integer -pub fn le_256( - a: u256, - b: u256 -) -> bool { +pub fn le_256(a: u256, b: u256) -> bool { let (a_high, a_low): (u128, u128) = ::into(a); let (b_high, b_low): (u128, u128) = ::into(b); match lt_128(a_high, b_high) { - false => { - match eq_128(a_high, b_high) { - false => { - false - }, - true => { - le_128(a_low, b_low) - } - } - }, true => true, + false => match eq_128(a_high, b_high) { + true => le_128(a_low, b_low), + false => false, + }, } } @@ -152,17 +133,12 @@ pub fn gt_256(a: u256, b: u256) -> bool { } /// Check if an integer is greater than or equal to another integer -pub fn ge_256( - a: u256, - b: u256 -) -> bool { +pub fn ge_256(a: u256, b: u256) -> bool { le_256(b, a) } /// Splits the u256 integer into four u64 integers -pub fn split_256_into_64( - a: u256 -) -> (u64, u64, u64, u64) { +pub fn split_256_into_64(a: u256) -> (u64, u64, u64, u64) { ::into(a) } @@ -179,41 +155,29 @@ pub fn add_256(a: u256, b: u256) -> (bool, u256) { } /// Adds the 128-bit integer with the 64-bit integer and returns the carry -pub fn add_256_128( - a: u256, - b: u128 -) -> (bool, u256) { +pub fn add_256_128(a: u256, b: u128) -> (bool, u256) { let b: u256 = <(u128, u128)>::into((0, b)); add_256(a, b) } /// Returns the sum of two u256 values wrapped in Some, or None if the result overflows u256 -pub fn checked_add_256( - a: u256, - b: u256 -) -> Option { +pub fn checked_add_256(a: u256, b: u256) -> Option { let (carry, sum): (bool, u256) = add_256(a, b); match carry { - false => Some(sum), true => None, + false => Some(sum), } } /// Returns the sum of two u256 values, panics if the result overflows u256 -pub fn safe_add_256( - a: u256, - b: u256 -) -> u256 { +pub fn safe_add_256(a: u256, b: u256) -> u256 { unwrap(checked_add_256(a, b)) } /// Subtracts the second integer from the first integer, and returns the borrow bit -pub fn sub_256( - a: u256, - b: u256 -) -> (bool, u256) { +pub fn sub_256(a: u256, b: u256) -> (bool, u256) { let (a_high, a_low): (u128, u128) = ::into(a); let (b_high, b_low): (u128, u128) = ::into(b); @@ -225,23 +189,17 @@ pub fn sub_256( } /// Returns the difference of two u256 values wrapped in Some, or None if the result overflows u256 -pub fn checked_sub_256( - a: u256, - b: u256 -) -> Option { +pub fn checked_sub_256(a: u256, b: u256) -> Option { let (borrow, diff): (bool, u256) = sub_256(a, b); match borrow { - false => Some(diff), true => None, + false => Some(diff), } } /// Returns the difference of two u256 values, panics if the result overflows u256 -pub fn safe_sub_256( - a: u256, - b: u256 -) -> u256 { +pub fn safe_sub_256(a: u256, b: u256) -> u256 { unwrap(checked_sub_256(a, b)) } @@ -285,34 +243,24 @@ pub fn mul_256(a: u256, b: u256) -> (u256, u256) { } /// Returns the product of two u256 values wrapped in Some, or None if the result overflows u256 -pub fn checked_mul_256( - a: u256, - b: u256 -) -> Option { +pub fn checked_mul_256(a: u256, b: u256) -> Option { let (result_high, result_low): (u256, u256) = mul_256(a, b); match is_zero_256(result_high) { - false => None, true => Some(result_low), + false => None, } } /// Returns the product of two u256 values, panics if the result overflows u256 -pub fn safe_mul_256( - a: u256, - b: u256 -) -> u256 { +pub fn safe_mul_256(a: u256, b: u256) -> u256 { unwrap(checked_mul_256(a, b)) } /// Normalizes the dividend and divisor for Algorithm D by multiplying /// both u256 and u128 by the same factor, ensuring that /// the most significant non-zero word of `b` is at least 2^63. -fn normalize_to_threshold_256_63( - a: u256, - b: u128, - is_b_u128: bool -) -> (u256, u256, u128, u64) { +fn normalize_to_threshold_256_63(a: u256, b: u128, is_b_u128: bool) -> (u256, u256, u128, u64) { let norm: u64 = calculate_normalizer_base_64(b, is_b_u128); let norm_128: u128 = <(u64, u64)>::into((0, norm)); @@ -398,13 +346,7 @@ pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { /// Multiplies the divisor by the quotient digit and subtracts the result from /// the corresponding dividend limbs. Returns the updated dividend segment, /// which is used by Algorithm D -fn mul_and_sub( - q: u64, - u2: u64, - u1: u64, - u0: u64, - v: u128 -) -> (u64, u64) { +fn mul_and_sub(q: u64, u2: u64, u1: u64, u0: u64, v: u128) -> (u64, u64) { let u: u256 = <(u64, u64, u64, u64)>::into((0, u2, u1, u0)); let q_v: u256 = mul_128(<(u64, u64)>::into((0, q)), v); @@ -445,21 +387,16 @@ fn algorithm_d_256_128(dividend: u256, divisor: u128) -> (u256, u128) { /// Divides the 256-bit integer by the 128-bit integer, /// returns a tuple of the u256 quotient and the u128 remainder -pub fn div_mod_256_128( - dividend: u256, - divisor: u128 -) -> (u256, u128) { +pub fn div_mod_256_128(dividend: u256, divisor: u128) -> (u256, u128) { let (divisor_high, divisor_low): (u64, u64) = ::into(divisor); match jet::is_zero_64(divisor_high) { - false => { - algorithm_d_256_128(dividend, divisor) - }, true => { let (q, r): (u256, u64) = div_mod_256_64(dividend, divisor_low); (q, <(u64, u64)>::into((0, r))) - } + }, + false => algorithm_d_256_128(dividend, divisor), } } @@ -556,30 +493,21 @@ pub fn div_mod_256(a: u256, b: u256) -> (u256, u256) { } /// Divide the first integer by the second integer, returns the quotient -pub fn div_256( - a: u256, - b: u256 -) -> u256 { +pub fn div_256(a: u256, b: u256) -> u256 { let (q, _): (u256, u256) = div_mod_256(a, b); q } /// Returns the quotient of two u256 values wrapped in Some, or None if the result overflows u256 -pub fn checked_div_256( - a: u256, - b: u256 -) -> Option { +pub fn checked_div_256(a: u256, b: u256) -> Option { match is_zero_256(b) { - false => Some(div_256(a, b)), true => None, + false => Some(div_256(a, b)), } } /// Returns the quotient of two u256 values, panics if the result overflows u256 -pub fn safe_div_256( - a: u256, - b: u256 -) -> u256 { +pub fn safe_div_256(a: u256, b: u256) -> u256 { unwrap(checked_div_256(a, b)) } diff --git a/simf/u128_test.simf b/simf/u128_test.simf index cbaeedf..15b99fe 100644 --- a/simf/u128_test.simf +++ b/simf/u128_test.simf @@ -1,4 +1,16 @@ -use crate::lib::u128::{ checked_add_128, safe_add_128, checked_sub_128, safe_sub_128, checked_mul_128, safe_mul_128, checked_div_128, safe_div_128, eq_128, gt_128, ge_128 }; +use crate::lib::u128::{ + checked_add_128, + safe_add_128, + checked_sub_128, + safe_sub_128, + checked_mul_128, + safe_mul_128, + checked_div_128, + safe_div_128, + eq_128, + gt_128, + ge_128 +}; use crate::lib::asserts::{assert_none_128, assert_eq_128}; use crate::lib::binary::not; use crate::helper::if_test_this_function; @@ -8,8 +20,8 @@ use crate::helper::if_test_this_function; /// witness value carries both, removing the need for a separate overflow flag. fn assert_eq_opt(result: Option, expected: Option) { match expected { - None => assert_none_128(result), Some(e: u128) => assert_eq_128(unwrap(result), e), + None => assert_none_128(result), } } diff --git a/simf/u128_test_arithmetic.simf b/simf/u128_test_arithmetic.simf index c507967..10ae533 100644 --- a/simf/u128_test_arithmetic.simf +++ b/simf/u128_test_arithmetic.simf @@ -15,18 +15,11 @@ use crate::lib::u128::{ div_mod_128, div_128 }; -use crate::helper::{ - if_test_this_function, - assert_bool -}; +use crate::helper::{if_test_this_function, assert_bool}; /// Asserts a result equals expected u128 and bool values. /// Used for functions that return carry or borrow bool value. -fn assert_eq_uint_bool( - result: (bool, u128), - expected: u128, - expected_bool: bool -) { +fn assert_eq_uint_bool(result: (bool, u128), expected: u128, expected_bool: bool) { let (bool_res, uint_res): (bool, u128) = result; assert_bool(bool_res, expected_bool); @@ -47,16 +40,16 @@ fn main() { /// Arithmetic - match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_128(a), expected_bool); }, false => {}, }; - match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_128(a, b), expected_bool); }, false => {}, }; - match if_test_this_function(2, fn_idx) { true => { assert_bool(le_128(a, b), expected_bool); }, false => {}, }; + match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_128(a), expected_bool); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_128(a, b), expected_bool); }, false => (), }; + match if_test_this_function(2, fn_idx) { true => { assert_bool(le_128(a, b), expected_bool); }, false => (), }; - match if_test_this_function(3, fn_idx) { true => { assert_eq_uint_bool(add_128(a, b), unwrap(expected), expected_bool); }, false => {}, }; - match if_test_this_function(4, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); assert_eq_uint_bool(add_128_64(a, b), unwrap(expected), expected_bool); }, false => {}, }; - match if_test_this_function(5, fn_idx) { true => { assert_eq_uint_bool(full_add_128(eq_128(second_expected, 1), a, b), unwrap(expected), expected_bool); }, false => {}, }; + match if_test_this_function(3, fn_idx) { true => { assert_eq_uint_bool(add_128(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(4, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); assert_eq_uint_bool(add_128_64(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => { assert_eq_uint_bool(full_add_128(eq_128(second_expected, 1), a, b), unwrap(expected), expected_bool); }, false => (), }; - match if_test_this_function(6, fn_idx) { true => { assert_eq_uint_bool(sub_128(a, b), unwrap(expected), expected_bool); }, false => {}, }; - match if_test_this_function(7, fn_idx) { true => { assert_eq_uint_bool(full_sub_128(eq_128(second_expected, 1), a, b), unwrap(expected), expected_bool); }, false => {}, }; + match if_test_this_function(6, fn_idx) { true => { assert_eq_uint_bool(sub_128(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => { assert_eq_uint_bool(full_sub_128(eq_128(second_expected, 1), a, b), unwrap(expected), expected_bool); }, false => (), }; match if_test_this_function(8, fn_idx) { true => { @@ -86,7 +79,7 @@ fn main() { let (_, u2): (u64, u64) = ::into(a); let (u1, u0): (u64, u64) = ::into(b); let (v1, v0): (u64, u64) = ::into(second_expected); - + let q: u64 = estimate_quotient_digit_base_64(u2, u1, u0, v1, v0); let (_, expected_q): (u64, u64) = ::into(unwrap(expected)); @@ -119,5 +112,5 @@ fn main() { false => {}, }; - match if_test_this_function(13, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(13, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => (), }; } diff --git a/simf/u128_test_bits.simf b/simf/u128_test_bits.simf index 725cad9..d492b02 100644 --- a/simf/u128_test_bits.simf +++ b/simf/u128_test_bits.simf @@ -1,4 +1,4 @@ -use crate::lib::u128::{ and_128, or_128, eq_128, left_shift_128, right_shift_128 }; +use crate::lib::u128::{and_128, or_128, eq_128, left_shift_128, right_shift_128}; use crate::helper::{if_test_this_function, assert_bool}; fn main() { @@ -9,7 +9,7 @@ fn main() { let expected: Option = witness::EXPECTED; let expected_bool: bool = witness::EXPECTED_BOOL; - + /// Bit logic match if_test_this_function(0, fn_idx) { true => { assert!(eq_128(and_128(a, b), unwrap(expected))); }, false => (), }; diff --git a/simf/u256_test.simf b/simf/u256_test.simf index 12984c7..3cb186d 100644 --- a/simf/u256_test.simf +++ b/simf/u256_test.simf @@ -17,22 +17,16 @@ use crate::helper::if_test_this_function; /// Asserts a `checked_*` result equals the expected Option. /// `None` encodes the overflow case, `Some(e)` the fitting case, so a single /// witness value carries both, removing the need for a separate overflow flag. -fn assert_eq_opt( - result: Option, - expected: Option -) { +fn assert_eq_opt(result: Option, expected: Option) { match expected { - None => assert_none_256(result), Some(e: u256) => assert_eq_256(unwrap(result), e), + None => assert_none_256(result), } } /// Asserts a result equals the expected bool value. /// `None` encodes `false`, and `Some(_)` encodes `true`. -fn assert_bool_by_opt( - result: bool, - expected: Option -) { +fn assert_bool_by_opt(result: bool, expected: Option) { match expected { Some(_: u256) => assert!(result), None => assert!(not(result)), @@ -49,22 +43,22 @@ fn main() { /// Safe functions // add - match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_256(a, b), expected); }, false => {}, }; - match if_test_this_function(1, fn_idx) { true => {assert_eq_256(safe_add_256(a, b), unwrap(expected)); }, false => {}, }; + match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_256(a, b), expected); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => {assert_eq_256(safe_add_256(a, b), unwrap(expected)); }, false => (), }; // sub - match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_256(a, b), expected); }, false => {}, }; - match if_test_this_function(3, fn_idx) { true => {assert_eq_256(safe_sub_256(a, b), unwrap(expected)); }, false => {}, }; + match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_256(a, b), expected); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => {assert_eq_256(safe_sub_256(a, b), unwrap(expected)); }, false => (), }; // mul - match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_256(a, b), expected); }, false => {}, }; - match if_test_this_function(5, fn_idx) { true => {assert_eq_256(safe_mul_256(a, b), unwrap(expected)); }, false => {}, }; + match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_256(a, b), expected); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => {assert_eq_256(safe_mul_256(a, b), unwrap(expected)); }, false => (), }; // div - match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_256(a, b), expected); }, false => {}, }; - match if_test_this_function(7, fn_idx) { true => {assert_eq_256(safe_div_256(a, b), unwrap(expected)); }, false => {}, }; + match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_256(a, b), expected); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => {assert_eq_256(safe_div_256(a, b), unwrap(expected)); }, false => (), }; // gt, ge - match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_256(a, b), expected); }, false => {}, }; - match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_256(a, b), expected); }, false => {}, }; + match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_256(a, b), expected); }, false => (), }; + match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_256(a, b), expected); }, false => (), }; } diff --git a/simf/u256_test_arithmetic_1.simf b/simf/u256_test_arithmetic_1.simf index a835485..7deedb5 100644 --- a/simf/u256_test_arithmetic_1.simf +++ b/simf/u256_test_arithmetic_1.simf @@ -10,7 +10,7 @@ fn main() { /// Arithmetic - match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_256(a), expected_bool); }, false => {}, }; - match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_256(a, b), expected_bool); }, false => {}, }; - match if_test_this_function(2, fn_idx) { true => { assert_bool(le_256(a, b), expected_bool); }, false => {}, }; + match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_256(a), expected_bool); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_256(a, b), expected_bool); }, false => (), }; + match if_test_this_function(2, fn_idx) { true => { assert_bool(le_256(a, b), expected_bool); }, false => (), }; } diff --git a/simf/u256_test_arithmetic_2.simf b/simf/u256_test_arithmetic_2.simf index 214ab19..6e84113 100644 --- a/simf/u256_test_arithmetic_2.simf +++ b/simf/u256_test_arithmetic_2.simf @@ -1,17 +1,10 @@ use crate::lib::u256::{split_256_into_64, add_256, add_256_128}; use crate::lib::asserts::assert_eq_256; -use crate::helper::{ - if_test_this_function, - assert_bool -}; +use crate::helper::{if_test_this_function, assert_bool}; /// Asserts a result equals expected u256 and bool values. /// Used for functions that return carry or borrow bool value. -fn assert_eq_uint_bool( - result: (bool, u256), - expected: u256, - expected_bool: bool -) { +fn assert_eq_uint_bool(result: (bool, u256), expected: u256, expected_bool: bool) { let (bool_res, uint_res): (bool, u256) = result; assert_bool(bool_res, expected_bool); @@ -46,6 +39,6 @@ fn main() { false => {}, }; - match if_test_this_function(1, fn_idx) { true => { assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; - match if_test_this_function(2, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); assert_eq_uint_bool(add_256_128(a, b), unwrap(expected), expected_bool); }, false => {}, }; + match if_test_this_function(1, fn_idx) { true => { assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(2, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); assert_eq_uint_bool(add_256_128(a, b), unwrap(expected), expected_bool); }, false => (), }; } diff --git a/simf/u256_test_arithmetic_3.simf b/simf/u256_test_arithmetic_3.simf index 2512dbe..20773e2 100644 --- a/simf/u256_test_arithmetic_3.simf +++ b/simf/u256_test_arithmetic_3.simf @@ -1,17 +1,10 @@ use crate::lib::u256::{split_256_into_64, sub_256, mul_256, div_mod_256_64}; use crate::lib::asserts::assert_eq_256; -use crate::helper::{ - if_test_this_function, - assert_bool -}; +use crate::helper::{if_test_this_function, assert_bool}; /// Asserts a result equals expected u256 and bool values. /// Used for functions that return carry or borrow bool value. -fn assert_eq_uint_bool( - result: (bool, u256), - expected: u256, - expected_bool: bool -) { +fn assert_eq_uint_bool(result: (bool, u256), expected: u256, expected_bool: bool) { let (bool_res, uint_res): (bool, u256) = result; assert_bool(bool_res, expected_bool); @@ -31,7 +24,7 @@ fn main() { /// Arithmetic - match if_test_this_function(0, fn_idx) { true => { assert_eq_uint_bool(sub_256(a, b), unwrap(expected), expected_bool); }, false => {}, }; + match if_test_this_function(0, fn_idx) { true => { assert_eq_uint_bool(sub_256(a, b), unwrap(expected), expected_bool); }, false => (), }; match if_test_this_function(1, fn_idx) { true => { diff --git a/simf/u256_test_arithmetic_4.simf b/simf/u256_test_arithmetic_4.simf index 044babe..86cb4eb 100644 --- a/simf/u256_test_arithmetic_4.simf +++ b/simf/u256_test_arithmetic_4.simf @@ -37,5 +37,5 @@ fn main() { false => {}, }; - match if_test_this_function(2, fn_idx) { true => { assert_eq_256(div_256(a, b), unwrap(expected)); }, false => {}, }; + match if_test_this_function(2, fn_idx) { true => { assert_eq_256(div_256(a, b), unwrap(expected)); }, false => (), }; } diff --git a/simf/u256_test_bits.simf b/simf/u256_test_bits.simf index c0273d4..cc8e004 100644 --- a/simf/u256_test_bits.simf +++ b/simf/u256_test_bits.simf @@ -13,8 +13,8 @@ fn main() { /// Bit logic - match if_test_this_function(0, fn_idx) { true => { assert_eq_256(and_256(a, b), unwrap(expected)); }, false => {}, }; - match if_test_this_function(1, fn_idx) { true => { assert_eq_256(or_256(a, b), unwrap(expected)); }, false => {}, }; + match if_test_this_function(0, fn_idx) { true => { assert_eq_256(and_256(a, b), unwrap(expected)); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert_eq_256(or_256(a, b), unwrap(expected)); }, false => (), }; match if_test_this_function(2, fn_idx) { true => { @@ -22,7 +22,7 @@ fn main() { let shift: u8 = jet::rightmost_64_8(a); assert_eq_256(left_shift_256(shift, b), unwrap(expected)); - }, false => {}, + }, false => (), }; match if_test_this_function(3, fn_idx) { @@ -31,6 +31,6 @@ fn main() { let shift: u8 = jet::rightmost_64_8(a); assert_eq_256(right_shift_256(shift, b), unwrap(expected)); - }, false => {}, + }, false => (), }; } From 81b8c283360b83876b355b7cc916edc308a00bd2 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Thu, 6 Aug 2026 18:12:22 +0300 Subject: [PATCH 13/15] review fixes: typos, helper optimization --- simf/lib/u256.simf | 2 +- tests/common/helper.rs | 33 +++++++++++---------------------- tests/helper_test.rs | 23 +++++++++++++++++++++++ tests/op_return_test.rs | 2 +- tests/u256_test_arithmetic_2.rs | 2 +- 5 files changed, 37 insertions(+), 25 deletions(-) create mode 100644 tests/helper_test.rs diff --git a/simf/lib/u256.simf b/simf/lib/u256.simf index 92a7cf6..e3addab 100644 --- a/simf/lib/u256.simf +++ b/simf/lib/u256.simf @@ -1,4 +1,4 @@ -use crate::lib::binary::{not, or, and}; +use crate::lib::binary::{not, and}; use crate::lib::u128::{ and_128, or_128, diff --git a/tests/common/helper.rs b/tests/common/helper.rs index d1c6106..6b5a568 100644 --- a/tests/common/helper.rs +++ b/tests/common/helper.rs @@ -1,33 +1,22 @@ +// Each `tests/*.rs` is a separate crate that mounts this module but uses only +// part of it, so per-crate dead-code analysis would warn about the rest. +#![allow(dead_code)] + use primitive_types::U256; use rand::Rng; -#[allow(dead_code)] +use crate::common::u256_wrapper::U256Wrapper; + +// Shared constants and helper functions used across integration tests + pub const DEFAULT_BOOL: bool = false; -#[allow(dead_code)] pub fn generate_u256(lower_bound: U256, upper_bound: U256) -> U256 { assert!( lower_bound <= upper_bound, "Error: lower bound is greater than upper bound" ); - - let (a_high, a_low): (u128, u128) = if lower_bound > U256::from(u128::MAX) { - ( - rand::thread_rng() - .gen_range((lower_bound >> 128).as_u128()..=(upper_bound >> 128).as_u128()), - rand::thread_rng().gen_range(0..=u128::MAX), - ) - } else if upper_bound > U256::from(u128::MAX) { - ( - rand::thread_rng().gen_range(0_u128..=(upper_bound >> 128).as_u128()), - rand::thread_rng().gen_range(lower_bound.as_u128()..=u128::MAX), - ) - } else { - ( - 0, - rand::thread_rng().gen_range(lower_bound.as_u128()..=upper_bound.as_u128()), - ) - }; - - (U256::from(a_high) << 128) | U256::from(a_low) + rand::thread_rng() + .gen_range(U256Wrapper(lower_bound)..=U256Wrapper(upper_bound)) + .0 } diff --git a/tests/helper_test.rs b/tests/helper_test.rs new file mode 100644 index 0000000..8843942 --- /dev/null +++ b/tests/helper_test.rs @@ -0,0 +1,23 @@ +mod common; + +use primitive_types::U256; + +use crate::common::helper::generate_u256; + +#[test] +fn generate_u256_respects_bounds() { + let cases = [ + (U256::zero(), U256::MAX), + (U256::one(), U256::from(u128::MAX)), + (U256::from(u128::MAX) + 1, U256::MAX), + (U256::from(u64::MAX) + 1, U256::from(u128::MAX)), + (U256::MAX, U256::MAX), + ]; + + for (lo, hi) in cases { + for _ in 0..10_000 { + let v = generate_u256(lo, hi); + assert!(lo <= v && v <= hi, "{v:#x} outside [{lo:#x}, {hi:#x}]"); + } + } +} diff --git a/tests/op_return_test.rs b/tests/op_return_test.rs index 917bd49..e61268e 100644 --- a/tests/op_return_test.rs +++ b/tests/op_return_test.rs @@ -3,6 +3,7 @@ mod common; use rand::Rng; use crate::common::core::{run, run_with_op_return}; +use crate::common::helper::DEFAULT_BOOL; use common::core::Expect; use simplicityhl_std::artifacts::op_return_test::OpReturnTestProgram; @@ -20,7 +21,6 @@ fn op(o: FunctionToTest) -> u8 { o as u8 } -const DEFAULT_BOOL: bool = false; const DEFAULT_DATA: &[u8; 1] = &[1]; fn program() -> OpReturnTestProgram { diff --git a/tests/u256_test_arithmetic_2.rs b/tests/u256_test_arithmetic_2.rs index 0def283..f41c8a8 100644 --- a/tests/u256_test_arithmetic_2.rs +++ b/tests/u256_test_arithmetic_2.rs @@ -47,7 +47,7 @@ mod u256_tests_arithmetic { use super::*; #[simplex::test] - fn u128_test_split_256_into_64(context: simplex::TestContext) -> anyhow::Result<()> { + fn u256_test_split_256_into_64(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); run( From e3bb4a415041c5b84c28e981f1adee71f03ad12f Mon Sep 17 00:00:00 2001 From: aritkulova Date: Thu, 6 Aug 2026 18:16:05 +0300 Subject: [PATCH 14/15] renamed arithmetic tests files into more specific ones --- ...ithmetic_1.simf => u256_test_compare.simf} | 0 ...t_arithmetic_4.simf => u256_test_div.simf} | 24 ++++++-- ...hmetic_2.simf => u256_test_split_add.simf} | 0 ...ithmetic_3.simf => u256_test_sub_mul.simf} | 15 +---- tests/u256_test_bits.rs | 2 - ...t_arithmetic_1.rs => u256_test_compare.rs} | 14 ++--- ..._test_arithmetic_4.rs => u256_test_div.rs} | 56 +++++++++++++++--- ...arithmetic_2.rs => u256_test_split_add.rs} | 14 ++--- ...t_arithmetic_3.rs => u256_test_sub_mul.rs} | 58 +++---------------- 9 files changed, 89 insertions(+), 94 deletions(-) rename simf/{u256_test_arithmetic_1.simf => u256_test_compare.simf} (100%) rename simf/{u256_test_arithmetic_4.simf => u256_test_div.simf} (58%) rename simf/{u256_test_arithmetic_2.simf => u256_test_split_add.simf} (100%) rename simf/{u256_test_arithmetic_3.simf => u256_test_sub_mul.simf} (70%) rename tests/{u256_test_arithmetic_1.rs => u256_test_compare.rs} (89%) rename tests/{u256_test_arithmetic_4.rs => u256_test_div.rs} (85%) rename tests/{u256_test_arithmetic_2.rs => u256_test_split_add.rs} (89%) rename tests/{u256_test_arithmetic_3.rs => u256_test_sub_mul.rs} (76%) diff --git a/simf/u256_test_arithmetic_1.simf b/simf/u256_test_compare.simf similarity index 100% rename from simf/u256_test_arithmetic_1.simf rename to simf/u256_test_compare.simf diff --git a/simf/u256_test_arithmetic_4.simf b/simf/u256_test_div.simf similarity index 58% rename from simf/u256_test_arithmetic_4.simf rename to simf/u256_test_div.simf index 86cb4eb..c559a40 100644 --- a/simf/u256_test_arithmetic_4.simf +++ b/simf/u256_test_div.simf @@ -1,6 +1,5 @@ -use crate::lib::u256::{div_mod_256_128, div_mod_256, div_256}; -use crate::lib::u128::eq_128; -use crate::lib::asserts::assert_eq_256; +use crate::lib::u256::{div_mod_256_64, div_mod_256_128, div_mod_256, div_256, split_256_into_64}; +use crate::lib::asserts::{assert_eq_128, assert_eq_256}; use crate::helper::if_test_this_function; fn main() { @@ -15,6 +14,19 @@ fn main() { /// Arithmetic match if_test_this_function(0, fn_idx) { + true => { + let (_, _, _, b): (u64, u64, u64, u64) = split_256_into_64(b); + let (_, _, _, expected_r): (u64, u64, u64, u64) = split_256_into_64(second_expected); + + let (q, r): (u256, u64) = div_mod_256_64(a, b); + + assert_eq_256(q, unwrap(expected)); + assert!(jet::eq_64(r, expected_r)); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); let (_, expected_r): (u128, u128) = ::into(second_expected); @@ -22,12 +34,12 @@ fn main() { let (q, r): (u256, u128) = div_mod_256_128(a, b); assert_eq_256(q, unwrap(expected)); - assert!(eq_128(r, expected_r)); + assert_eq_128(r, expected_r); }, false => {}, }; - match if_test_this_function(1, fn_idx) { + match if_test_this_function(2, fn_idx) { true => { let (q, r): (u256, u256) = div_mod_256(a, b); @@ -37,5 +49,5 @@ fn main() { false => {}, }; - match if_test_this_function(2, fn_idx) { true => { assert_eq_256(div_256(a, b), unwrap(expected)); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => { assert_eq_256(div_256(a, b), unwrap(expected)); }, false => (), }; } diff --git a/simf/u256_test_arithmetic_2.simf b/simf/u256_test_split_add.simf similarity index 100% rename from simf/u256_test_arithmetic_2.simf rename to simf/u256_test_split_add.simf diff --git a/simf/u256_test_arithmetic_3.simf b/simf/u256_test_sub_mul.simf similarity index 70% rename from simf/u256_test_arithmetic_3.simf rename to simf/u256_test_sub_mul.simf index 20773e2..4812a46 100644 --- a/simf/u256_test_arithmetic_3.simf +++ b/simf/u256_test_sub_mul.simf @@ -1,4 +1,4 @@ -use crate::lib::u256::{split_256_into_64, sub_256, mul_256, div_mod_256_64}; +use crate::lib::u256::{sub_256, mul_256}; use crate::lib::asserts::assert_eq_256; use crate::helper::{if_test_this_function, assert_bool}; @@ -35,17 +35,4 @@ fn main() { }, false => {}, }; - - match if_test_this_function(2, fn_idx) { - true => { - let (_, _, _, b): (u64, u64, u64, u64) = split_256_into_64(b); - let (_, _, _, expected_r): (u64, u64, u64, u64) = split_256_into_64(second_expected); - - let (q, r): (u256, u64) = div_mod_256_64(a, b); - - assert_eq_256(q, unwrap(expected)); - assert!(jet::eq_64(r, expected_r)); - }, - false => {}, - }; } diff --git a/tests/u256_test_bits.rs b/tests/u256_test_bits.rs index 35eed9f..d874a28 100644 --- a/tests/u256_test_bits.rs +++ b/tests/u256_test_bits.rs @@ -23,8 +23,6 @@ fn op(o: FunctionToTest) -> u8 { o as u8 } -const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; - fn program() -> U256TestBitsProgram { U256TestBitsProgram::new(U256TestBitsArguments {}) } diff --git a/tests/u256_test_arithmetic_1.rs b/tests/u256_test_compare.rs similarity index 89% rename from tests/u256_test_arithmetic_1.rs rename to tests/u256_test_compare.rs index dcf71e6..0ed9e83 100644 --- a/tests/u256_test_arithmetic_1.rs +++ b/tests/u256_test_compare.rs @@ -5,9 +5,9 @@ use primitive_types::U256; use crate::common::helper::generate_u256; use common::core::{Expect, run}; -use simplicityhl_std::artifacts::u256_test_arithmetic_1::U256TestArithmetic1Program; -use simplicityhl_std::artifacts::u256_test_arithmetic_1::derived_u256_test_arithmetic_1::{ - U256TestArithmetic1Arguments, U256TestArithmetic1Witness, +use simplicityhl_std::artifacts::u256_test_compare::U256TestCompareProgram; +use simplicityhl_std::artifacts::u256_test_compare::derived_u256_test_compare::{ + U256TestCompareArguments, U256TestCompareWitness, }; enum FunctionToTest { @@ -23,8 +23,8 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; -fn program() -> U256TestArithmetic1Program { - U256TestArithmetic1Program::new(U256TestArithmetic1Arguments {}) +fn program() -> U256TestCompareProgram { + U256TestCompareProgram::new(U256TestCompareArguments {}) } fn build_witness( @@ -32,8 +32,8 @@ fn build_witness( a: [u8; 32], b: [u8; 32], expected_bool: bool, -) -> U256TestArithmetic1Witness { - U256TestArithmetic1Witness { +) -> U256TestCompareWitness { + U256TestCompareWitness { function_index: function, first_arg: a, second_arg: b, diff --git a/tests/u256_test_arithmetic_4.rs b/tests/u256_test_div.rs similarity index 85% rename from tests/u256_test_arithmetic_4.rs rename to tests/u256_test_div.rs index e197266..68bb70f 100644 --- a/tests/u256_test_arithmetic_4.rs +++ b/tests/u256_test_div.rs @@ -5,12 +5,13 @@ use primitive_types::U256; use crate::common::helper::generate_u256; use common::core::{Expect, run}; -use simplicityhl_std::artifacts::u256_test_arithmetic_4::U256TestArithmetic4Program; -use simplicityhl_std::artifacts::u256_test_arithmetic_4::derived_u256_test_arithmetic_4::{ - U256TestArithmetic4Arguments, U256TestArithmetic4Witness, +use simplicityhl_std::artifacts::u256_test_div::U256TestDivProgram; +use simplicityhl_std::artifacts::u256_test_div::derived_u256_test_div::{ + U256TestDivArguments, U256TestDivWitness, }; enum FunctionToTest { + DivMod256_64, DivMod256_128, DivMod256, Div256, @@ -23,8 +24,8 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; -fn program() -> U256TestArithmetic4Program { - U256TestArithmetic4Program::new(U256TestArithmetic4Arguments {}) +fn program() -> U256TestDivProgram { + U256TestDivProgram::new(U256TestDivArguments {}) } fn build_witness( @@ -33,8 +34,8 @@ fn build_witness( b: [u8; 32], expected: Option<[u8; 32]>, second_expected: [u8; 32], -) -> U256TestArithmetic4Witness { - U256TestArithmetic4Witness { +) -> U256TestDivWitness { + U256TestDivWitness { function_index: function, first_arg: a, second_arg: b, @@ -46,6 +47,47 @@ fn build_witness( mod u256_tests_arithmetic { use super::*; + #[simplex::test] + fn test_div_mod_256_64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u64::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_64), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_64), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + #[simplex::test] fn test_div_mod_256_128(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); diff --git a/tests/u256_test_arithmetic_2.rs b/tests/u256_test_split_add.rs similarity index 89% rename from tests/u256_test_arithmetic_2.rs rename to tests/u256_test_split_add.rs index f41c8a8..7620b73 100644 --- a/tests/u256_test_arithmetic_2.rs +++ b/tests/u256_test_split_add.rs @@ -5,9 +5,9 @@ use primitive_types::U256; use crate::common::helper::{DEFAULT_BOOL, generate_u256}; use common::core::{Expect, run}; -use simplicityhl_std::artifacts::u256_test_arithmetic_2::U256TestArithmetic2Program; -use simplicityhl_std::artifacts::u256_test_arithmetic_2::derived_u256_test_arithmetic_2::{ - U256TestArithmetic2Arguments, U256TestArithmetic2Witness, +use simplicityhl_std::artifacts::u256_test_split_add::U256TestSplitAddProgram; +use simplicityhl_std::artifacts::u256_test_split_add::derived_u256_test_split_add::{ + U256TestSplitAddArguments, U256TestSplitAddWitness, }; enum FunctionToTest { @@ -23,8 +23,8 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; -fn program() -> U256TestArithmetic2Program { - U256TestArithmetic2Program::new(U256TestArithmetic2Arguments {}) +fn program() -> U256TestSplitAddProgram { + U256TestSplitAddProgram::new(U256TestSplitAddArguments {}) } fn build_witness( @@ -33,8 +33,8 @@ fn build_witness( b: [u8; 32], expected: Option<[u8; 32]>, expected_bool: bool, -) -> U256TestArithmetic2Witness { - U256TestArithmetic2Witness { +) -> U256TestSplitAddWitness { + U256TestSplitAddWitness { function_index: function, first_arg: a, second_arg: b, diff --git a/tests/u256_test_arithmetic_3.rs b/tests/u256_test_sub_mul.rs similarity index 76% rename from tests/u256_test_arithmetic_3.rs rename to tests/u256_test_sub_mul.rs index eacd589..5d97b44 100644 --- a/tests/u256_test_arithmetic_3.rs +++ b/tests/u256_test_sub_mul.rs @@ -6,15 +6,14 @@ use rand::Rng; use crate::common::helper::{DEFAULT_BOOL, generate_u256}; use common::core::{Expect, run}; -use simplicityhl_std::artifacts::u256_test_arithmetic_3::U256TestArithmetic3Program; -use simplicityhl_std::artifacts::u256_test_arithmetic_3::derived_u256_test_arithmetic_3::{ - U256TestArithmetic3Arguments, U256TestArithmetic3Witness, +use simplicityhl_std::artifacts::u256_test_sub_mul::U256TestSubMulProgram; +use simplicityhl_std::artifacts::u256_test_sub_mul::derived_u256_test_sub_mul::{ + U256TestSubMulArguments, U256TestSubMulWitness, }; enum FunctionToTest { Sub256, Mul256, - DivMod256_64, } #[inline] @@ -24,8 +23,8 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; -fn program() -> U256TestArithmetic3Program { - U256TestArithmetic3Program::new(U256TestArithmetic3Arguments {}) +fn program() -> U256TestSubMulProgram { + U256TestSubMulProgram::new(U256TestSubMulArguments {}) } fn build_witness( @@ -35,8 +34,8 @@ fn build_witness( expected: Option<[u8; 32]>, expected_bool: bool, second_expected: [u8; 32], -) -> U256TestArithmetic3Witness { - U256TestArithmetic3Witness { +) -> U256TestSubMulWitness { + U256TestSubMulWitness { function_index: function, first_arg: a, second_arg: b, @@ -211,47 +210,4 @@ mod u256_tests_arithmetic { Expect::Ok, ) } - - #[simplex::test] - fn test_div_mod_256_64(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX); - let b = generate_u256(U256::one(), U256::from(u64::MAX)); - - let q = (a / b).to_big_endian(); - let r = (a % b).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256_64), - a.to_big_endian(), - b.to_big_endian(), - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn test_div_mod_256_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::MAX); - let b = [0; 32]; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod256_64), - a.to_big_endian(), - b, - Some(DEFAULT_EXPECTED), - DEFAULT_BOOL, - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } } From a4b3633611e975b5790b6880973018770b0eb6f7 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Fri, 7 Aug 2026 13:55:58 +0300 Subject: [PATCH 15/15] linting for consistency --- simf/u128_test_arithmetic.simf | 6 +++--- simf/u1_convert_test.simf | 14 +++++++------- simf/u256_test_div.simf | 6 +++--- simf/u256_test_split_add.simf | 2 +- simf/u256_test_sub_mul.simf | 2 +- simf/u8_convert_test.simf | 14 +++++++------- simf/u8_math_test.simf | 20 ++++++++++---------- 7 files changed, 32 insertions(+), 32 deletions(-) diff --git a/simf/u128_test_arithmetic.simf b/simf/u128_test_arithmetic.simf index 10ae533..28902c7 100644 --- a/simf/u128_test_arithmetic.simf +++ b/simf/u128_test_arithmetic.simf @@ -60,7 +60,7 @@ fn main() { assert!(eq_128(result_high, unwrap(expected))); assert!(eq_128(result_low, second_expected)); }, - false => {}, + false => (), }; match if_test_this_function(9, fn_idx) { @@ -99,7 +99,7 @@ fn main() { assert!(eq_128(q, unwrap(expected))); assert!(jet::eq_64(r, expected_r)); }, - false => {}, + false => (), }; match if_test_this_function(12, fn_idx) { @@ -109,7 +109,7 @@ fn main() { assert!(eq_128(q, unwrap(expected))); assert!(eq_128(r, second_expected)); }, - false => {}, + false => (), }; match if_test_this_function(13, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => (), }; diff --git a/simf/u1_convert_test.simf b/simf/u1_convert_test.simf index a432dd9..fff7a92 100644 --- a/simf/u1_convert_test.simf +++ b/simf/u1_convert_test.simf @@ -26,7 +26,7 @@ fn main() { assert!(jet::eq_8(u1_to_u8(a), expected)); }, - false => {}, + false => (), }; match if_test_this_function(1, fn_idx) { @@ -36,7 +36,7 @@ fn main() { assert!(jet::eq_16(u1_to_u16(a), expected)); }, - false => {}, + false => (), }; match if_test_this_function(2, fn_idx) { @@ -46,7 +46,7 @@ fn main() { assert!(jet::eq_32(u1_to_u32(a), expected)); }, - false => {}, + false => (), }; match if_test_this_function(3, fn_idx) { @@ -55,7 +55,7 @@ fn main() { assert!(jet::eq_64(u1_to_u64(a), expected)); }, - false => {}, + false => (), }; match if_test_this_function(4, fn_idx) { @@ -64,14 +64,14 @@ fn main() { assert!(eq_128(u1_to_u128(a), expected)); }, - false => {}, + false => (), }; match if_test_this_function(5, fn_idx) { true => { assert!(jet::eq_256(u1_to_u256(a), expected)); }, - false => {}, + false => (), }; match if_test_this_function(6, fn_idx) { @@ -79,6 +79,6 @@ fn main() { let expected_bool: bool = jet::eq_256(expected, 1); assert_eq_bool(u1_to_bool(a), expected_bool); }, - false => {}, + false => (), }; } diff --git a/simf/u256_test_div.simf b/simf/u256_test_div.simf index c559a40..50576b1 100644 --- a/simf/u256_test_div.simf +++ b/simf/u256_test_div.simf @@ -23,7 +23,7 @@ fn main() { assert_eq_256(q, unwrap(expected)); assert!(jet::eq_64(r, expected_r)); }, - false => {}, + false => (), }; match if_test_this_function(1, fn_idx) { @@ -36,7 +36,7 @@ fn main() { assert_eq_256(q, unwrap(expected)); assert_eq_128(r, expected_r); }, - false => {}, + false => (), }; match if_test_this_function(2, fn_idx) { @@ -46,7 +46,7 @@ fn main() { assert_eq_256(q, unwrap(expected)); assert_eq_256(r, second_expected); }, - false => {}, + false => (), }; match if_test_this_function(3, fn_idx) { true => { assert_eq_256(div_256(a, b), unwrap(expected)); }, false => (), }; diff --git a/simf/u256_test_split_add.simf b/simf/u256_test_split_add.simf index 6e84113..da49048 100644 --- a/simf/u256_test_split_add.simf +++ b/simf/u256_test_split_add.simf @@ -36,7 +36,7 @@ fn main() { assert!(jet::eq_64(res3, expected3)); assert!(jet::eq_64(res4, expected4)); }, - false => {}, + false => (), }; match if_test_this_function(1, fn_idx) { true => { assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); }, false => (), }; diff --git a/simf/u256_test_sub_mul.simf b/simf/u256_test_sub_mul.simf index 4812a46..a078585 100644 --- a/simf/u256_test_sub_mul.simf +++ b/simf/u256_test_sub_mul.simf @@ -33,6 +33,6 @@ fn main() { assert_eq_256(result_high, unwrap(expected)); assert_eq_256(result_low, second_expected); }, - false => {}, + false => (), }; } diff --git a/simf/u8_convert_test.simf b/simf/u8_convert_test.simf index 7589160..928baf8 100644 --- a/simf/u8_convert_test.simf +++ b/simf/u8_convert_test.simf @@ -26,7 +26,7 @@ fn main() { assert!(jet::eq_16(u8_to_u16(a), expected)); }, - false => {}, + false => (), }; match if_test_this_function(1, fn_idx) { @@ -36,7 +36,7 @@ fn main() { assert!(jet::eq_32(u8_to_u32(a), expected)); }, - false => {}, + false => (), }; match if_test_this_function(2, fn_idx) { @@ -45,7 +45,7 @@ fn main() { assert!(jet::eq_64(u8_to_u64(a), expected)); }, - false => {}, + false => (), }; match if_test_this_function(3, fn_idx) { @@ -54,14 +54,14 @@ fn main() { assert!(eq_128(u8_to_u128(a), expected)); }, - false => {}, + false => (), }; match if_test_this_function(4, fn_idx) { true => { assert!(jet::eq_256(u8_to_u256(a), expected)); }, - false => {}, + false => (), }; match if_test_this_function(5, fn_idx) { @@ -99,7 +99,7 @@ fn main() { assert_eq_1(a1, expected1); assert_eq_1(a0, expected0); }, - false => {}, + false => (), }; match if_test_this_function(6, fn_idx) { @@ -109,6 +109,6 @@ fn main() { assert_eq_1(safe_u8_to_u1(a), expected); }, - false => {}, + false => (), }; } diff --git a/simf/u8_math_test.simf b/simf/u8_math_test.simf index 3bd2eef..d8c4f44 100644 --- a/simf/u8_math_test.simf +++ b/simf/u8_math_test.simf @@ -41,22 +41,22 @@ fn main() { let expected: Option = witness::EXPECTED; // add - match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_8(a, b), expected); }, false => {}, }; - match if_test_this_function(1, fn_idx) { true => { assert!(jet::eq_8(safe_add_8(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_8(a, b), expected); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert!(jet::eq_8(safe_add_8(a, b), unwrap(expected))); }, false => (), }; // sub - match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_8(a, b), expected); }, false => {}, }; - match if_test_this_function(3, fn_idx) { true => { assert!(jet::eq_8(safe_sub_8(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_8(a, b), expected); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => { assert!(jet::eq_8(safe_sub_8(a, b), unwrap(expected))); }, false => (), }; // mul - match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_8(a, b), expected); }, false => {}, }; - match if_test_this_function(5, fn_idx) { true => { assert!(jet::eq_8(safe_mul_8(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_8(a, b), expected); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => { assert!(jet::eq_8(safe_mul_8(a, b), unwrap(expected))); }, false => (), }; // div - match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_8(a, b), expected); }, false => {}, }; - match if_test_this_function(7, fn_idx) { true => { assert!(jet::eq_8(safe_div_8(a, b), unwrap(expected))); }, false => {}, }; + match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_8(a, b), expected); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => { assert!(jet::eq_8(safe_div_8(a, b), unwrap(expected))); }, false => (), }; // gt, ge - match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_8(a, b), expected); }, false => {}, }; - match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_8(a, b), expected); }, false => {}, }; + match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_8(a, b), expected); }, false => (), }; + match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_8(a, b), expected); }, false => (), }; }