From 3e4698ce3f0ac77a161b67b995105f151c9a8c30 Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Thu, 6 Aug 2026 17:47:02 +0300 Subject: [PATCH 01/11] Convert functions for u8 (#34) * added convert functions for u8 * commented out eq_256 (will be available with pr#30) * added split and safe_convert functions * refactored widening functions * linting * refactored u8 functions * typo * removed unused import --- simf/lib/u8/convert.simf | 49 +++++++ simf/lib/{u8.simf => u8/math.simf} | 0 simf/u8_convert_test.simf | 114 +++++++++++++++ simf/{u8_test.simf => u8_math_test.simf} | 35 +++-- tests/u8_convert_test.rs | 170 +++++++++++++++++++++++ tests/{u8_test.rs => u8_math_tests.rs} | 20 +-- 6 files changed, 367 insertions(+), 21 deletions(-) create mode 100644 simf/lib/u8/convert.simf rename simf/lib/{u8.simf => u8/math.simf} (100%) create mode 100644 simf/u8_convert_test.simf rename simf/{u8_test.simf => u8_math_test.simf} (68%) create mode 100644 tests/u8_convert_test.rs rename tests/{u8_test.rs => u8_math_tests.rs} (59%) diff --git a/simf/lib/u8/convert.simf b/simf/lib/u8/convert.simf new file mode 100644 index 0000000..fee231c --- /dev/null +++ b/simf/lib/u8/convert.simf @@ -0,0 +1,49 @@ +/// Widening uint conversions + +/// Converts u8 to u16 +pub fn u8_to_u16(a: u8) -> u16 { + jet::left_pad_low_8_16(a) +} + +/// Converts u8 to u32 +pub fn u8_to_u32(a: u8) -> u32 { + jet::left_pad_low_8_32(a) +} + +/// Converts u8 to u64 +pub fn u8_to_u64(a: u8) -> u64 { + jet::left_pad_low_8_64(a) +} + +/// Converts u8 to u128 +pub fn u8_to_u128(a: u8) -> u128 { + let a_u64: u64 = u8_to_u64(a); + + <(u64, u64)>::into((0, a_u64)) +} + +/// Converts u8 to u256 +pub fn u8_to_u256(a: u8) -> u256 { + let a_u128: u128 = u8_to_u128(a); + + <(u128, u128)>::into((0, a_u128)) +} + +/// Splitting uint conversions + +/// Splits u8 into eight u1 +pub fn split_u8_to_u1(a: u8) -> (u1, u1, u1, u1, u1, u1, u1, u1) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u8 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u8_to_u1(a: u8) -> u1 { + let u1_max: u8 = jet::left_pad_low_1_8(jet::high_1()); + + assert!(jet::le_8(a, u1_max)); + + jet::rightmost_8_1(a) +} diff --git a/simf/lib/u8.simf b/simf/lib/u8/math.simf similarity index 100% rename from simf/lib/u8.simf rename to simf/lib/u8/math.simf diff --git a/simf/u8_convert_test.simf b/simf/u8_convert_test.simf new file mode 100644 index 0000000..7589160 --- /dev/null +++ b/simf/u8_convert_test.simf @@ -0,0 +1,114 @@ +use crate::lib::u8::convert::{ + u8_to_u16, + u8_to_u32, + u8_to_u64, + u8_to_u128, + u8_to_u256, + split_u8_to_u1, + safe_u8_to_u1 +}; +// use crate::lib::u256::{split_256_into_64}; TODO: uncomment when u256 functions are merged +use crate::lib::u128::eq_128; +use crate::lib::asserts::assert_eq_1; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u8 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); + + assert!(jet::eq_16(u8_to_u16(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, expected): (u32, u32) = ::into(expected); + + assert!(jet::eq_32(u8_to_u32(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert!(jet::eq_64(u8_to_u64(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert!(eq_128(u8_to_u128(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + assert!(jet::eq_256(u8_to_u256(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + let ( + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): (u1, u1, u1, u1, u1, u1, u1, u1) = split_u8_to_u1(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + let ( + expected7, + expected6, + expected5, + expected4, + expected3, + expected2, + expected1, + expected0 + ): (u1, u1, u1, u1, u1, u1, u1, u1) = ::into(expected); + + assert_eq_1(a7, expected7); + assert_eq_1(a6, expected6); + assert_eq_1(a5, expected5); + assert_eq_1(a4, expected4); + assert_eq_1(a3, expected3); + assert_eq_1(a2, expected2); + assert_eq_1(a1, expected1); + assert_eq_1(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u8_to_u1(a), expected); + }, + false => {}, + }; +} diff --git a/simf/u8_test.simf b/simf/u8_math_test.simf similarity index 68% rename from simf/u8_test.simf rename to simf/u8_math_test.simf index 37e8d32..3bd2eef 100644 --- a/simf/u8_test.simf +++ b/simf/u8_math_test.simf @@ -1,4 +1,15 @@ -use crate::lib::u8::{checked_add_8, safe_add_8, checked_sub_8, safe_sub_8, checked_mul_8, safe_mul_8, checked_div_8, safe_div_8, gt_8, ge_8}; +use crate::lib::u8::math::{ + checked_add_8, + safe_add_8, + checked_sub_8, + safe_sub_8, + checked_mul_8, + safe_mul_8, + checked_div_8, + safe_div_8, + gt_8, + ge_8 +}; use crate::lib::asserts::{assert_none_8, assert_eq_8}; use crate::lib::binary::not; use crate::helper::if_test_this_function; @@ -8,8 +19,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_8(result), Some(e: u8) => assert_eq_8(unwrap(result), e), + None => assert_none_8(result), } } @@ -30,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 => {}, }; } diff --git a/tests/u8_convert_test.rs b/tests/u8_convert_test.rs new file mode 100644 index 0000000..6331249 --- /dev/null +++ b/tests/u8_convert_test.rs @@ -0,0 +1,170 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u8_convert_test::U8ConvertTestProgram; +use simplicityhl_std::artifacts::u8_convert_test::derived_u8_convert_test::{ + U8ConvertTestArguments, U8ConvertTestWitness, +}; + +enum FunctionToTest { + U8ToU16, + U8ToU32, + U8ToU64, + U8ToU128, + U8ToU256, + SplitU8ToU1, + SafeU8ToU1, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U8ConvertTestProgram { + U8ConvertTestProgram::new(U8ConvertTestArguments {}) +} + +fn build_witness(function: u8, a: u8, expected: [u8; 32]) -> U8ConvertTestWitness { + U8ConvertTestWitness { + function_index: function, + first_arg: a, + expected, + } +} + +mod u8_convert_test { + use super::*; + + #[simplex::test] + fn u8_convert_test_u8_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U8ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u8_convert_test_u8_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U8ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u8_convert_test_u8_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U8ToU64), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u8_convert_test_u8_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U8ToU128), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u8_convert_test_u8_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U8ToU256), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u8_convert_test_split_u8_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU8ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u8_convert_test_safe_u8_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU8ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u8_convert_test_safe_u8_to_u1_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u8::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU8ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u8_test.rs b/tests/u8_math_tests.rs similarity index 59% rename from tests/u8_test.rs rename to tests/u8_math_tests.rs index 90bfaff..2d5c120 100644 --- a/tests/u8_test.rs +++ b/tests/u8_math_tests.rs @@ -2,13 +2,15 @@ mod common; use common::uint::TestUint; -use simplicityhl_std::artifacts::u8_test::U8TestProgram; -use simplicityhl_std::artifacts::u8_test::derived_u8_test::{U8TestArguments, U8TestWitness}; +use simplicityhl_std::artifacts::u8_math_test::U8MathTestProgram; +use simplicityhl_std::artifacts::u8_math_test::derived_u8_math_test::{ + U8MathTestArguments, U8MathTestWitness, +}; // The only per-width code for the common operations. impl TestUint for u8 { - type Program = U8TestProgram; - type Witness = U8TestWitness; + type Program = U8MathTestProgram; + type Witness = U8MathTestWitness; const ZERO: u8 = 0; const ONE: u8 = 1; @@ -16,12 +18,12 @@ impl TestUint for u8 { const HALF_MAX: u8 = u8::MAX / 2; const MUL_BOUND: u8 = 1 << 4; // 2^(8/2) - fn program() -> U8TestProgram { - U8TestProgram::new(U8TestArguments {}) + fn program() -> U8MathTestProgram { + U8MathTestProgram::new(U8MathTestArguments {}) } - fn witness(op: u8, a: u8, b: u8, expected: Option) -> U8TestWitness { - U8TestWitness { + fn witness(op: u8, a: u8, b: u8, expected: Option) -> U8MathTestWitness { + U8MathTestWitness { function_index: op, first_arg: a, second_arg: b, @@ -30,7 +32,7 @@ impl TestUint for u8 { } } -mod u8_tests { +mod u8_math_tests { use super::*; // Stamps the 16 `#[simplex::test]` entry points for u8. Logic lives in common::uint. From 276a10ed78c0bdfee1c85f273c602a1ffd7bf3b4 Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Thu, 6 Aug 2026 17:56:30 +0300 Subject: [PATCH 02/11] Convert functions for u1 (#35) * added assert_eq_bool * added convert functions for u1 --- simf/asserts_test.simf | 32 +++++--- simf/lib/asserts.simf | 5 ++ simf/lib/u1/convert.simf | 42 +++++++++++ simf/u1_convert_test.simf | 84 +++++++++++++++++++++ tests/asserts_test.rs | 27 +++++++ tests/u1_convert_test.rs | 150 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 331 insertions(+), 9 deletions(-) create mode 100644 simf/lib/u1/convert.simf create mode 100644 simf/u1_convert_test.simf create mode 100644 tests/u1_convert_test.rs diff --git a/simf/asserts_test.simf b/simf/asserts_test.simf index fafd2f1..36e1708 100644 --- a/simf/asserts_test.simf +++ b/simf/asserts_test.simf @@ -1,6 +1,19 @@ use crate::lib::asserts::{ - assert_eq_1, assert_eq_8, assert_eq_16, assert_eq_32, assert_eq_64,assert_eq_128, assert_eq_256, - assert_none_1, assert_none_8, assert_none_16, assert_none_32, assert_none_64, assert_none_128, assert_none_256 + assert_eq_1, + assert_eq_8, + assert_eq_16, + assert_eq_32, + assert_eq_64, + assert_eq_128, + assert_eq_256, + assert_eq_bool, + assert_none_1, + assert_none_8, + assert_none_16, + assert_none_32, + assert_none_64, + assert_none_128, + assert_none_256 }; use crate::helper::if_test_this_function; @@ -36,13 +49,14 @@ fn main() { match if_test_this_function(4, fn_idx) { true => { assert_eq_64(unwrap(a_u64), unwrap(b_u64)); }, false => (), }; match if_test_this_function(5, fn_idx) { true => { assert_eq_128(unwrap(a_u128), unwrap(b_u128)); }, false => (), }; match if_test_this_function(6, fn_idx) { true => { assert_eq_256(unwrap(a_u256), unwrap(b_u256)); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => { assert_eq_bool(::into(unwrap(a_u1)), ::into(unwrap(b_u1))); }, false => (), }; // Assert None - match if_test_this_function(7, fn_idx) { true => { assert_none_1(a_u1); }, false => (), }; - match if_test_this_function(8, fn_idx) { true => { assert_none_8(a_u8); }, false => (), }; - match if_test_this_function(9, fn_idx) { true => { assert_none_16(a_u16); }, false => (), }; - match if_test_this_function(10, fn_idx) { true => { assert_none_32(a_u32); }, false => (), }; - match if_test_this_function(11, fn_idx) { true => { assert_none_64(a_u64); }, false => (), }; - match if_test_this_function(12, fn_idx) { true => { assert_none_128(a_u128); }, false => (), }; - match if_test_this_function(13, fn_idx) { true => { assert_none_256(a_u256); }, false => (), }; + match if_test_this_function(8, fn_idx) { true => { assert_none_1(a_u1); }, false => (), }; + match if_test_this_function(9, fn_idx) { true => { assert_none_8(a_u8); }, false => (), }; + match if_test_this_function(10, fn_idx) { true => { assert_none_16(a_u16); }, false => (), }; + match if_test_this_function(11, fn_idx) { true => { assert_none_32(a_u32); }, false => (), }; + match if_test_this_function(12, fn_idx) { true => { assert_none_64(a_u64); }, false => (), }; + match if_test_this_function(13, fn_idx) { true => { assert_none_128(a_u128); }, false => (), }; + match if_test_this_function(14, fn_idx) { true => { assert_none_256(a_u256); }, false => (), }; } diff --git a/simf/lib/asserts.simf b/simf/lib/asserts.simf index 60c577c..63c4a9a 100644 --- a/simf/lib/asserts.simf +++ b/simf/lib/asserts.simf @@ -35,6 +35,11 @@ pub fn assert_eq_256(a: u256, b: u256) { assert!(jet::eq_256(a, b)); } +/// Asserts that two bool values are equal +pub fn assert_eq_bool(a: bool, b: bool) { + assert_eq_1(::into(a), ::into(b)); +} + /// Asserts that provided `Option` value is a `None` pub fn assert_none_1(val: Option) { assert!(is_none::(val)); diff --git a/simf/lib/u1/convert.simf b/simf/lib/u1/convert.simf new file mode 100644 index 0000000..f01b02e --- /dev/null +++ b/simf/lib/u1/convert.simf @@ -0,0 +1,42 @@ +/// Widening uint conversions + +/// Converts u1 to u8 +pub fn u1_to_u8(a: u1) -> u8 { + jet::left_pad_low_1_8(a) +} + +/// Converts u1 to u16 +pub fn u1_to_u16(a: u1) -> u16 { + jet::left_pad_low_1_16(a) +} + +/// Converts u1 to u32 +pub fn u1_to_u32(a: u1) -> u32 { + jet::left_pad_low_1_32(a) +} + +/// Converts u1 to u64 +pub fn u1_to_u64(a: u1) -> u64 { + jet::left_pad_low_1_64(a) +} + +/// Converts u1 to u128 +pub fn u1_to_u128(a: u1) -> u128 { + let a_u64: u64 = u1_to_u64(a); + + <(u64, u64)>::into((0, a_u64)) +} + +/// Converts u1 to u256 +pub fn u1_to_u256(a: u1) -> u256 { + let a_u128: u128 = u1_to_u128(a); + + <(u128, u128)>::into((0, a_u128)) +} + +/// Type conversions + +/// Converts u1 to bool +pub fn u1_to_bool(a: u1) -> bool { + ::into(a) +} diff --git a/simf/u1_convert_test.simf b/simf/u1_convert_test.simf new file mode 100644 index 0000000..a432dd9 --- /dev/null +++ b/simf/u1_convert_test.simf @@ -0,0 +1,84 @@ +use crate::lib::u1::convert::{ + u1_to_u8, + u1_to_u16, + u1_to_u32, + u1_to_u64, + u1_to_u128, + u1_to_u256, + u1_to_bool +}; +use crate::lib::u128::eq_128; +use crate::lib::asserts::assert_eq_bool; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u1 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); + let (_, expected): (u8, u8) = ::into(expected); + + assert!(jet::eq_8(u1_to_u8(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); + + assert!(jet::eq_16(u1_to_u16(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, expected): (u32, u32) = ::into(expected); + + assert!(jet::eq_32(u1_to_u32(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert!(jet::eq_64(u1_to_u64(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert!(eq_128(u1_to_u128(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + assert!(jet::eq_256(u1_to_u256(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let expected_bool: bool = jet::eq_256(expected, 1); + assert_eq_bool(u1_to_bool(a), expected_bool); + }, + false => {}, + }; +} diff --git a/tests/asserts_test.rs b/tests/asserts_test.rs index fad2ee3..e128ce9 100644 --- a/tests/asserts_test.rs +++ b/tests/asserts_test.rs @@ -19,6 +19,7 @@ enum FunctionToTest { AssertEq64, AssertEq128, AssertEq256, + AssertEqBool, AssertNone1, AssertNone8, @@ -113,6 +114,10 @@ fn build_witness(function: FunctionToTest, same: bool, none: bool) -> AssertsTes (witness.first_arg_u256, witness.second_arg_u256) = (Some([a as u8; 32]), Some([b as u8; 32])); } + FunctionToTest::AssertEqBool => { + let (a, b) = generate_uints_in_one_range(same, 0, 1u128); + (witness.first_arg_u1, witness.second_arg_u1) = (Some(a as u8), Some(b as u8)); + } FunctionToTest::AssertNone1 => { if none { witness.first_arg_u1 = None; @@ -315,6 +320,28 @@ mod asserts_test { ) } + #[simplex::test] + fn assert_eq_bool_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEqBool, + true, + false, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_eq_bool_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEqBool, + false, + false, + Expect::AssertFailed, + ) + } + // ---------- assert_none: happy = None arg, unhappy = Some arg ---------- #[simplex::test] fn assert_none_1_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { diff --git a/tests/u1_convert_test.rs b/tests/u1_convert_test.rs new file mode 100644 index 0000000..230acbf --- /dev/null +++ b/tests/u1_convert_test.rs @@ -0,0 +1,150 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u1_convert_test::U1ConvertTestProgram; +use simplicityhl_std::artifacts::u1_convert_test::derived_u1_convert_test::{ + U1ConvertTestArguments, U1ConvertTestWitness, +}; + +enum FunctionToTest { + U1ToU8, + U1ToU16, + U1ToU32, + U1ToU64, + U1ToU128, + U1ToU256, + U1ToBool, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U1ConvertTestProgram { + U1ConvertTestProgram::new(U1ConvertTestArguments {}) +} + +fn build_witness(function: u8, a: u8, expected: [u8; 32]) -> U1ConvertTestWitness { + U1ConvertTestWitness { + function_index: function, + first_arg: a, + expected, + } +} + +mod u1_convert_test { + use super::*; + + #[simplex::test] + fn u1_convert_test_u1_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness(op(FunctionToTest::U1ToU8), a, U256::from(a).to_big_endian()), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_u1_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_u1_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_u1_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToU64), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_u1_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToU128), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_u1_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToU256), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_split_u1_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToBool), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } +} From c90ab86367d3abd121eaef43bbb276a2d26ad3ee Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Mon, 10 Aug 2026 17:11:56 +0300 Subject: [PATCH 03/11] Convert functions for u16 (#36) * added convert functions for u16 * typo --- simf/lib/u16/convert.simf | 54 ++++++ simf/lib/{u16.simf => u16/math.simf} | 0 simf/u16_convert_test.simf | 89 ++++++++++ simf/{u16_test.simf => u16_math_test.simf} | 2 +- tests/u16_convert_test.rs | 190 +++++++++++++++++++++ tests/{u16_test.rs => u16_math_test.rs} | 20 ++- 6 files changed, 345 insertions(+), 10 deletions(-) create mode 100644 simf/lib/u16/convert.simf rename simf/lib/{u16.simf => u16/math.simf} (100%) create mode 100644 simf/u16_convert_test.simf rename simf/{u16_test.simf => u16_math_test.simf} (93%) create mode 100644 tests/u16_convert_test.rs rename tests/{u16_test.rs => u16_math_test.rs} (58%) diff --git a/simf/lib/u16/convert.simf b/simf/lib/u16/convert.simf new file mode 100644 index 0000000..f1a3b9f --- /dev/null +++ b/simf/lib/u16/convert.simf @@ -0,0 +1,54 @@ +/// Widening uint conversions + +/// Converts u16 to u32 +pub fn u16_to_u32(a: u16) -> u32 { + jet::left_pad_low_16_32(a) +} + +/// Converts u16 to u64 +pub fn u16_to_u64(a: u16) -> u64 { + jet::left_pad_low_16_64(a) +} + +/// Converts u16 to u128 +pub fn u16_to_u128(a: u16) -> u128 { + let a_u64: u64 = u16_to_u64(a); + + <(u64, u64)>::into((0, a_u64)) +} + +/// Converts u16 to u256 +pub fn u16_to_u256(a: u16) -> u256 { + let a_u128: u128 = u16_to_u128(a); + + <(u128, u128)>::into((0, a_u128)) +} + +/// Splitting uint conversions + +/// Splits u16 into two u8 +pub fn split_u16_to_u8(a: u16) -> (u8, u8) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u16 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u16_to_u1(a: u16) -> u1 { + let u1_max: u16 = jet::left_pad_low_1_16(jet::high_1()); + + assert!(jet::le_16(a, u1_max)); + + jet::rightmost_16_1(a) +} + +/// Converts u16 into u8. +/// Panics if the value does not fit in u8 +pub fn safe_u16_to_u8(a: u16) -> u8 { + let u8_max: u16 = jet::left_pad_low_8_16(jet::high_8()); + + assert!(jet::le_16(a, u8_max)); + + jet::rightmost_16_8(a) +} diff --git a/simf/lib/u16.simf b/simf/lib/u16/math.simf similarity index 100% rename from simf/lib/u16.simf rename to simf/lib/u16/math.simf diff --git a/simf/u16_convert_test.simf b/simf/u16_convert_test.simf new file mode 100644 index 0000000..7a59769 --- /dev/null +++ b/simf/u16_convert_test.simf @@ -0,0 +1,89 @@ +use crate::lib::u16::convert::{ + u16_to_u32, + u16_to_u64, + u16_to_u128, + u16_to_u256, + split_u16_to_u8, + safe_u16_to_u1, + safe_u16_to_u8 +}; +use crate::lib::u128::eq_128; +use crate::lib::asserts::{assert_eq_1, assert_eq_8}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u16 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, expected): (u32, u32) = ::into(expected); + + assert!(jet::eq_32(u16_to_u32(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert!(jet::eq_64(u16_to_u64(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert!(eq_128(u16_to_u128(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + assert!(jet::eq_256(u16_to_u256(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (a1, a0): (u8, u8) = split_u16_to_u8(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u16 = jet::rightmost_64_16(expected); + let (expected1, expected0): (u8, u8) = ::into(expected); + + assert_eq_8(a1, expected1); + assert_eq_8(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u16_to_u1(a), expected); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + + assert_eq_8(safe_u16_to_u8(a), expected); + }, + false => {}, + }; +} diff --git a/simf/u16_test.simf b/simf/u16_math_test.simf similarity index 93% rename from simf/u16_test.simf rename to simf/u16_math_test.simf index 43cb52e..bc7aece 100644 --- a/simf/u16_test.simf +++ b/simf/u16_math_test.simf @@ -1,4 +1,4 @@ -use crate::lib::u16::{checked_add_16, safe_add_16, checked_sub_16, safe_sub_16, checked_mul_16, safe_mul_16, checked_div_16, safe_div_16, gt_16, ge_16}; +use crate::lib::u16::math::{checked_add_16, safe_add_16, checked_sub_16, safe_sub_16, checked_mul_16, safe_mul_16, checked_div_16, safe_div_16, gt_16, ge_16}; use crate::lib::asserts::{assert_none_16, assert_eq_16}; use crate::lib::binary::not; use crate::helper::if_test_this_function; diff --git a/tests/u16_convert_test.rs b/tests/u16_convert_test.rs new file mode 100644 index 0000000..f9c222d --- /dev/null +++ b/tests/u16_convert_test.rs @@ -0,0 +1,190 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u16_convert_test::U16ConvertTestProgram; +use simplicityhl_std::artifacts::u16_convert_test::derived_u16_convert_test::{ + U16ConvertTestArguments, U16ConvertTestWitness, +}; + +enum FunctionToTest { + U16ToU32, + U16ToU64, + U16ToU128, + U16ToU256, + SplitU16ToU8, + SafeU16ToU1, + SafeU16ToU8, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U16ConvertTestProgram { + U16ConvertTestProgram::new(U16ConvertTestArguments {}) +} + +fn build_witness(function: u8, a: u16, expected: [u8; 32]) -> U16ConvertTestWitness { + U16ConvertTestWitness { + function_index: function, + first_arg: a, + expected, + } +} + +mod u16_convert_test { + use super::*; + + #[simplex::test] + fn u16_convert_test_u16_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U16ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u16_convert_test_u16_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U16ToU64), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u16_convert_test_u16_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U16ToU128), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u16_convert_test_u16_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U16ToU256), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u16_convert_test_split_u16_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU16ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u16_convert_test_safe_u16_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU16ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u16_convert_test_safe_u16_to_u1_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u16::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU16ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u16_convert_test_safe_u16_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX as u16); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU16ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u16_convert_test_safe_u16_to_u8_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u8::MAX as u16 + 1..=u16::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU16ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u16_test.rs b/tests/u16_math_test.rs similarity index 58% rename from tests/u16_test.rs rename to tests/u16_math_test.rs index 7274e4e..10cc47f 100644 --- a/tests/u16_test.rs +++ b/tests/u16_math_test.rs @@ -2,13 +2,15 @@ mod common; use common::uint::TestUint; -use simplicityhl_std::artifacts::u16_test::U16TestProgram; -use simplicityhl_std::artifacts::u16_test::derived_u16_test::{U16TestArguments, U16TestWitness}; +use simplicityhl_std::artifacts::u16_math_test::U16MathTestProgram; +use simplicityhl_std::artifacts::u16_math_test::derived_u16_math_test::{ + U16MathTestArguments, U16MathTestWitness, +}; // The only per-width code for the common operations. impl TestUint for u16 { - type Program = U16TestProgram; - type Witness = U16TestWitness; + type Program = U16MathTestProgram; + type Witness = U16MathTestWitness; const ZERO: u16 = 0; const ONE: u16 = 1; @@ -16,12 +18,12 @@ impl TestUint for u16 { const HALF_MAX: u16 = u16::MAX / 2; const MUL_BOUND: u16 = 1 << 8; // 2^(16/2) - fn program() -> U16TestProgram { - U16TestProgram::new(U16TestArguments {}) + fn program() -> U16MathTestProgram { + U16MathTestProgram::new(U16MathTestArguments {}) } - fn witness(op: u8, a: u16, b: u16, expected: Option) -> U16TestWitness { - U16TestWitness { + fn witness(op: u8, a: u16, b: u16, expected: Option) -> U16MathTestWitness { + U16MathTestWitness { function_index: op, first_arg: a, second_arg: b, @@ -30,7 +32,7 @@ impl TestUint for u16 { } } -mod u16_tests { +mod u16_math_tests { use super::*; // Stamps the 16 `#[simplex::test]` entry points for u16. Logic lives in common::uint. From 3b54160130a6a40fa1827233d1d49eb6cb56370c Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Mon, 10 Aug 2026 17:13:42 +0300 Subject: [PATCH 04/11] added convert functions for u32 (#37) --- simf/lib/u32/convert.simf | 64 ++++++ simf/lib/{u32.simf => u32/math.simf} | 0 simf/u32_convert_test.simf | 111 ++++++++++ simf/{u32_test.simf => u32_math_test.simf} | 2 +- tests/u32_convert_test.rs | 225 +++++++++++++++++++++ tests/{u32_test.rs => u32_math_test.rs} | 20 +- 6 files changed, 412 insertions(+), 10 deletions(-) create mode 100644 simf/lib/u32/convert.simf rename simf/lib/{u32.simf => u32/math.simf} (100%) create mode 100644 simf/u32_convert_test.simf rename simf/{u32_test.simf => u32_math_test.simf} (93%) create mode 100644 tests/u32_convert_test.rs rename tests/{u32_test.rs => u32_math_test.rs} (58%) diff --git a/simf/lib/u32/convert.simf b/simf/lib/u32/convert.simf new file mode 100644 index 0000000..b5028bb --- /dev/null +++ b/simf/lib/u32/convert.simf @@ -0,0 +1,64 @@ +/// Widening uint conversions + +/// Converts u32 to u64 +pub fn u32_to_u64(a: u32) -> u64 { + jet::left_pad_low_32_64(a) +} + +/// Converts u32 to u128 +pub fn u32_to_u128(a: u32) -> u128 { + let a_u64: u64 = u32_to_u64(a); + + <(u64, u64)>::into((0, a_u64)) +} + +/// Converts u32 to u256 +pub fn u32_to_u256(a: u32) -> u256 { + let a_u128: u128 = u32_to_u128(a); + + <(u128, u128)>::into((0, a_u128)) +} + +/// Splitting uint conversions + +/// Splits u32 into four u8 +pub fn split_u32_to_u8(a: u32) -> (u8, u8, u8, u8) { + ::into(a) +} + +/// Splits u32 into two u16 +pub fn split_u32_to_u16(a: u32) -> (u16, u16) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u32 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u32_to_u1(a: u32) -> u1 { + let u1_max: u32 = jet::left_pad_low_1_32(jet::high_1()); + + assert!(jet::le_32(a, u1_max)); + + jet::rightmost_32_1(a) +} + +/// Converts u32 into u8. +/// Panics if the value does not fit in u8 +pub fn safe_u32_to_u8(a: u32) -> u8 { + let u8_max: u32 = jet::left_pad_low_8_32(jet::high_8()); + + assert!(jet::le_32(a, u8_max)); + + jet::rightmost_32_8(a) +} + +/// Converts u32 into u16. +/// Panics if the value does not fit in u16 +pub fn safe_u32_to_u16(a: u32) -> u16 { + let u16_max: u32 = jet::left_pad_low_16_32(jet::high_16()); + + assert!(jet::le_32(a, u16_max)); + + jet::rightmost_32_16(a) +} diff --git a/simf/lib/u32.simf b/simf/lib/u32/math.simf similarity index 100% rename from simf/lib/u32.simf rename to simf/lib/u32/math.simf diff --git a/simf/u32_convert_test.simf b/simf/u32_convert_test.simf new file mode 100644 index 0000000..af21275 --- /dev/null +++ b/simf/u32_convert_test.simf @@ -0,0 +1,111 @@ +use crate::lib::u32::convert::{ + u32_to_u64, + u32_to_u128, + u32_to_u256, + split_u32_to_u8, + split_u32_to_u16, + safe_u32_to_u1, + safe_u32_to_u8, + safe_u32_to_u16 +}; +use crate::lib::u128::eq_128; +use crate::lib::asserts::{assert_eq_1, assert_eq_8, assert_eq_16}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u32 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert!(jet::eq_64(u32_to_u64(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert!(eq_128(u32_to_u128(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + assert!(jet::eq_256(u32_to_u256(a), expected)); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (a3, a2, a1, a0): (u8, u8, u8, u8) = split_u32_to_u8(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u32 = jet::rightmost_64_32(expected); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u8, u8, u8, u8) = ::into(expected); + + assert_eq_8(a3, expected3); + assert_eq_8(a2, expected2); + assert_eq_8(a1, expected1); + assert_eq_8(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (a1, a0): (u16, u16) = split_u32_to_u16(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u32 = jet::rightmost_64_32(expected); + let (expected1, expected0): (u16, u16) = ::into(expected); + + assert_eq_16(a1, expected1); + assert_eq_16(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u32_to_u1(a), expected); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + + assert_eq_8(safe_u32_to_u8(a), expected); + }, + false => {}, + }; + + match if_test_this_function(7, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u16 = jet::rightmost_64_16(expected); + + assert_eq_16(safe_u32_to_u16(a), expected); + }, + false => {}, + }; +} diff --git a/simf/u32_test.simf b/simf/u32_math_test.simf similarity index 93% rename from simf/u32_test.simf rename to simf/u32_math_test.simf index 8636003..0d8274e 100644 --- a/simf/u32_test.simf +++ b/simf/u32_math_test.simf @@ -1,4 +1,4 @@ -use crate::lib::u32::{checked_add_32, safe_add_32, checked_sub_32, safe_sub_32, checked_mul_32, safe_mul_32, checked_div_32, safe_div_32, gt_32, ge_32}; +use crate::lib::u32::math::{checked_add_32, safe_add_32, checked_sub_32, safe_sub_32, checked_mul_32, safe_mul_32, checked_div_32, safe_div_32, gt_32, ge_32}; use crate::lib::asserts::{assert_none_32, assert_eq_32}; use crate::lib::binary::not; use crate::helper::if_test_this_function; diff --git a/tests/u32_convert_test.rs b/tests/u32_convert_test.rs new file mode 100644 index 0000000..860cab9 --- /dev/null +++ b/tests/u32_convert_test.rs @@ -0,0 +1,225 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u32_convert_test::U32ConvertTestProgram; +use simplicityhl_std::artifacts::u32_convert_test::derived_u32_convert_test::{ + U32ConvertTestArguments, U32ConvertTestWitness, +}; + +enum FunctionToTest { + U32ToU64, + U32ToU128, + U32ToU256, + SplitU32ToU8, + SplitU32ToU16, + SafeU32ToU1, + SafeU32ToU8, + SafeU32ToU16, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U32ConvertTestProgram { + U32ConvertTestProgram::new(U32ConvertTestArguments {}) +} + +fn build_witness(function: u8, a: u32, expected: [u8; 32]) -> U32ConvertTestWitness { + U32ConvertTestWitness { + function_index: function, + first_arg: a, + expected, + } +} + +mod u32_convert_test { + use super::*; + + #[simplex::test] + fn u32_convert_test_u32_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U32ToU64), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_convert_test_u32_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U32ToU128), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_convert_test_u32_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U32ToU256), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_convert_test_split_u32_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU32ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_convert_test_split_u32_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU32ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_convert_test_safe_u32_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU32ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_convert_test_safe_u32_to_u1_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u32::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU32ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u32_convert_test_safe_u32_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX as u32); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU32ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_convert_test_safe_u32_to_u8_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u8::MAX as u32 + 1..=u32::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU32ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u32_convert_test_safe_u32_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX as u32); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU32ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_convert_test_safe_u32_to_u16_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u16::MAX as u32 + 1..=u32::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU32ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u32_test.rs b/tests/u32_math_test.rs similarity index 58% rename from tests/u32_test.rs rename to tests/u32_math_test.rs index 7c76910..5c6c009 100644 --- a/tests/u32_test.rs +++ b/tests/u32_math_test.rs @@ -2,13 +2,15 @@ mod common; use common::uint::TestUint; -use simplicityhl_std::artifacts::u32_test::U32TestProgram; -use simplicityhl_std::artifacts::u32_test::derived_u32_test::{U32TestArguments, U32TestWitness}; +use simplicityhl_std::artifacts::u32_math_test::U32MathTestProgram; +use simplicityhl_std::artifacts::u32_math_test::derived_u32_math_test::{ + U32MathTestArguments, U32MathTestWitness, +}; // The only per-width code for the common operations. impl TestUint for u32 { - type Program = U32TestProgram; - type Witness = U32TestWitness; + type Program = U32MathTestProgram; + type Witness = U32MathTestWitness; const ZERO: u32 = 0; const ONE: u32 = 1; @@ -16,12 +18,12 @@ impl TestUint for u32 { const HALF_MAX: u32 = u32::MAX / 2; const MUL_BOUND: u32 = 1 << 16; // 2^(32/2) - fn program() -> U32TestProgram { - U32TestProgram::new(U32TestArguments {}) + fn program() -> U32MathTestProgram { + U32MathTestProgram::new(U32MathTestArguments {}) } - fn witness(op: u8, a: u32, b: u32, expected: Option) -> U32TestWitness { - U32TestWitness { + fn witness(op: u8, a: u32, b: u32, expected: Option) -> U32MathTestWitness { + U32MathTestWitness { function_index: op, first_arg: a, second_arg: b, @@ -30,7 +32,7 @@ impl TestUint for u32 { } } -mod u32_tests { +mod u32_math_tests { use super::*; // Stamps the 16 `#[simplex::test]` entry points for u32. Logic lives in common::uint. From 497d410e9e20c0337c5a0beeaa3ec33ecdf4b1da Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Fri, 14 Aug 2026 16:01:17 +0300 Subject: [PATCH 05/11] Feat/add u256 (#30) * added full_add_128; refactored normalize_to_threshold to calculate_normalizer_base_64; made algorithm_d function private * added functions for uint256 * added U256Wrapper for u256 tests; added tests for u256 * fixed typo * splitted u256 arithmetic tests into two batches for faster tests * refactored split_256_into_64 * added full_sub_128 * linting * splitted u256 arithmetic tests into more batches * typo * removed unnecessary eq_256 * linting * review fixes: typos, helper optimization * renamed arithmetic tests files into more specific ones * linting for consistency * refactoring and typos * added missing check for division algorithm * added mul_128_64, mul_256_64 and mul_256_128 for optimization * doc fixes and small refactoring --- simf/lib/u128.simf | 223 ++++++++----- simf/lib/u256.simf | 563 +++++++++++++++++++++++++++++++++ simf/u128_test.simf | 16 +- simf/u128_test_arithmetic.simf | 76 +++-- simf/u128_test_bits.simf | 4 +- simf/u1_convert_test.simf | 14 +- simf/u256_test.simf | 64 ++++ simf/u256_test_bits.simf | 34 ++ simf/u256_test_compare.simf | 16 + simf/u256_test_div.simf | 53 ++++ simf/u256_test_split_add.simf | 44 +++ simf/u256_test_sub_mul.simf | 64 ++++ simf/u8_convert_test.simf | 14 +- simf/u8_math_test.simf | 20 +- tests/common/helper.rs | 22 ++ tests/common/mod.rs | 2 + tests/common/u256_wrapper.rs | 149 +++++++++ tests/helper_test.rs | 23 ++ tests/op_return_test.rs | 2 +- tests/u128_test.rs | 2 +- tests/u128_test_arithmetic.rs | 359 ++++++++++++++++----- tests/u128_test_bits.rs | 2 +- tests/u16_math_test.rs | 2 +- tests/u256_test.rs | 48 +++ tests/u256_test_bits.rs | 198 ++++++++++++ tests/u256_test_compare.rs | 166 ++++++++++ tests/u256_test_div.rs | 401 +++++++++++++++++++++++ tests/u256_test_split_add.rs | 146 +++++++++ tests/u256_test_sub_mul.rs | 261 +++++++++++++++ tests/u32_math_test.rs | 2 +- tests/u64_test.rs | 2 +- tests/u8_math_tests.rs | 2 +- 32 files changed, 2777 insertions(+), 217 deletions(-) create mode 100644 simf/lib/u256.simf create mode 100644 simf/u256_test.simf create mode 100644 simf/u256_test_bits.simf create mode 100644 simf/u256_test_compare.simf create mode 100644 simf/u256_test_div.simf create mode 100644 simf/u256_test_split_add.simf create mode 100644 simf/u256_test_sub_mul.simf create mode 100644 tests/common/helper.rs create mode 100644 tests/common/u256_wrapper.rs create mode 100644 tests/helper_test.rs create mode 100644 tests/u256_test.rs create mode 100644 tests/u256_test_bits.rs create mode 100644 tests/u256_test_compare.rs create mode 100644 tests/u256_test_div.rs create mode 100644 tests/u256_test_split_add.rs create mode 100644 tests/u256_test_sub_mul.rs diff --git a/simf/lib/u128.simf b/simf/lib/u128.simf index 5695081..342de61 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 @@ -141,14 +137,27 @@ 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); let (carry_low, res_low): (bool, u64) = jet::add_64(a_low, b); let (carry_high, res_high): (bool, u64) = jet::full_add_64(carry_low, a_high, 0); - (carry_high, <(u64, u64)>::into((res_high, res_low))) + let res: u128 = <(u64, u64)>::into((res_high, res_low)); + (carry_high, res) +} + +/// 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_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_out, res) } /// Returns the sum of two u128 values wrapped in Some, or None if the result overflows u128 @@ -178,6 +187,18 @@ pub fn sub_128(a: u128, b: u128) -> (bool, u128) { (borrow_high, res) } +/// 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 { let (borrow, diff): (bool, u128) = sub_128(a, b); @@ -193,7 +214,7 @@ pub fn safe_sub_128(a: u128, b: u128) -> u128 { unwrap(checked_sub_128(a, b)) } -/// Multiply two integers. The output is a 256-bit integer +/// Multiplies two integers. The output is a 256-bit integer. /// The idea is that u128-bit `a` divides into 64-bit `a_high` and `a_low`, /// so a = a_high * 2^64 + a_low. /// In the same way, b = b_high * 2^64 + b_low. @@ -221,7 +242,7 @@ pub fn mul_128(a: u128, b: u128) -> u256 { // `word_3` is the upper half of a_high * b_high. It is at most `u64::MAX - 1` when // either factor is `u64::MAX`, and even in the extreme case where // a == b == u128::MAX, the total product still fits into u256. - // Therefore, word_3 + carry_3a + carry_3b can not overflow, and `add_64` + // Therefore, word_3 + carry_3a + carry_3b can not overflow, and `full_add_64` // is used instead of `safe_add_64` to avoid the unnecessary overflow check let (_, res_3a): (bool, u64) = jet::full_add_64(carry_3a, word_3, 0); let (_, res_3): (bool, u64) = jet::full_add_64(carry_3b, res_3a, 0); @@ -232,6 +253,27 @@ pub fn mul_128(a: u128, b: u128) -> u256 { <(u128, u128)>::into((res_3_2, res_1_0)) } +/// Multiplies two integers. The output is a 256-bit integer. +/// The idea is that u128-bit `a` divides into 64-bit `a_high` and `a_low`, +/// so a = a_high * 2^64 + a_low. +/// Therefore, a * b = 2^64 * a_high * b + a_low * b. +pub fn mul_128_64(a: u128, b: u64) -> u256 { + let (a_high, a_low): (u64, u64) = ::into(a); + + let highest: u128 = jet::multiply_64(a_high, b); + let lowest: u128 = jet::multiply_64(a_low, b); + + let (word_1, word_0): (u64, u64) = ::into(lowest); + let (word_3, word_2): (u64, u64) = ::into(highest); + + let (carry_2, res_1): (bool, u64) = jet::add_64(word_1, word_2); + // a * b fits into u192, so addition below can not overflow and `full_add_64` + // is used instead of `safe_add_64` to avoid the unnecessary overflow check + let (_, res_2): (bool, u64) = jet::full_add_64(carry_2, word_3, 0); + + <(u64, u64, u64, u64)>::into((0, res_2, res_1, word_0)) +} + /// Returns the product of two u128 values wrapped in Some, or None if the result overflows u128 pub fn checked_mul_128(a: u128, b: u128) -> Option { let result: u256 = mul_128(a, b); @@ -248,26 +290,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,18 +309,84 @@ 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 } + } +} + +/// 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, carry): (u64, u64, bool) = match jet::lt_64(u2, v1) { + true => { + let (q_hat, r_hat) : (u64, u64) =jet::div_mod_128_64(<(u64, u64)>::into((u2, u1)), v1); + (q_hat, r_hat, false) + }, + false => { + // This means u2 == v1, q_hat = 2^64, and r_hat = u1. + // Therefore, we need to decrement q and add v1 to r_hat. + // r_hat = u1 + v1 may overflow u64, which means that the estimate is exact. + let (carry, r_hat): (bool, u64) = jet::add_64(u1, v1); + + (jet::high_64(), r_hat, carry) + } }; - 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), + match carry { + true => q_hat, + false => { + let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); + + // correcting estimation: q_hat is off by at most 2. + 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, + false => { + let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); + + 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); + + q_hat + } + false => q_hat, + } + } + } + }, + false => q_hat, + } + } } } @@ -300,44 +394,14 @@ pub fn normalize_to_threshold(a: u128, b: u128, is_b_u128: bool) -> (u256, u128) /// 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); +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)) = split_256_into_64(norm_dividend); + let (_, u2, u1, u0): (u64, u64, u64, u64) = ::into(norm_dividend); let (v1, v0): (u64, u64) = ::into(norm_divisor); - 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)) { - 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, - false => { - let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); - - 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); - - q_hat - } - false => q_hat, - } - } - - } - }, - false => q_hat, - }; + 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 +419,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 +462,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/lib/u256.simf b/simf/lib/u256.simf new file mode 100644 index 0000000..fcbe0a8 --- /dev/null +++ b/simf/lib/u256.simf @@ -0,0 +1,563 @@ +use crate::lib::binary::{not, 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, + full_sub_128, + mul_128, + mul_128_64, + safe_mul_128, + calculate_normalizer_base_64, + estimate_quotient_digit_base_64, + div_mod_128, + div_mod_128_64 +}; + +/// 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))) +} + +/// 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) { + 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 { + 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) { + true => true, + false => match eq_128(a_high, b_high) { + true => le_128(a_low, b_low), + false => false, + }, + } +} + +/// 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) { + ::into(a) +} + +/// 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 256-bit integer with the 128-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 { + 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 { + 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_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); + + 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 +pub fn checked_sub_256(a: u256, b: u256) -> Option { + let (borrow, diff): (bool, u256) = sub_256(a, b); + + match borrow { + 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 { + unwrap(checked_sub_256(a, b)) +} + +/// Multiplies 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 `full_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) +} + +/// Multiplies two integers. +/// The output consists of a 64-bit integer and a 256-bit integer. +/// The idea is that u256-bit `a` divides into 128-bit `a_high` and `a_low`, +/// so a = a_high * 2^128 + a_low, +/// a * b = 2^128 * a_high * b + a_low * b +pub fn mul_256_64(a: u256, b: u64) -> (u64, u256) { + let (a_high, a_low): (u128, u128) = ::into(a); + + let lowest: u256 = mul_128_64(a_low, b); + let highest: u256 = mul_128_64(a_high, b); + + let (word_1, word_0): (u128, u128) = ::into(lowest); + let (word_3, word_2): (u128, u128) = ::into(highest); + + let (_, word_3): (u64, u64) = ::into(word_3); + + let (carry_2, res_1): (bool, u128) = add_128(word_1, word_2); + // a * b fits into u320, so addition below can not overflow and + // `full_add_64` is used to avoid the unnecessary overflow check + let (_, res_2): (bool, u64) = jet::full_add_64(carry_2, word_3, 0); + + (res_2, <(u128, u128)>::into((res_1, word_0))) +} + +/// Multiplies two integers. +/// The output consists of a 128-bit integer and a 256-bit integer. +/// The idea is that u256-bit `a` divides into 128-bit `a_high` and `a_low`, +/// so a = a_high * 2^128 + a_low, +/// and a * b = 2^128 * a_high * b + a_low * b +pub fn mul_256_128(a: u256, b: u128) -> (u128, u256) { + let (a_high, a_low): (u128, u128) = ::into(a); + + let lowest: u256 = mul_128(a_low, b); + let highest: u256 = mul_128(a_high, b); + + let (word_1, word_0): (u128, u128) = ::into(lowest); + let (word_3, word_2): (u128, u128) = ::into(highest); + + let (carry_2, res_1): (bool, u128) = add_128(word_1, word_2); + // a * b fits into u384, so addition below can not overflow and + // `full_add_128` is used to avoid the unnecessary overflow check + let (_, res_2): (bool, u128) = full_add_128(carry_2, word_3, 0); + + (res_2, <(u128, u128)>::into((res_1, word_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) { + 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 { + unwrap(checked_mul_256(a, b)) +} + +/// Returns the product of u256 and u128 values, panics if the result overflows u256 +fn safe_mul_256_128(a: u256, b: u128) -> u256 { + let (result_high, result_low): (u128, u256) = mul_256_128(a, b); + assert!(is_zero_128(result_high)); + + result_low +} + +/// 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. +/// +/// Returns the normalized `a` as a `(u64, u256)` pair, +/// the normalized `b` and the normalization factor. +fn normalize_to_threshold_256_63(a: u256, b: u128, is_b_u128: bool) -> (u64, 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): (u64, u256) = mul_256_64(a, norm); + let b_norm: u128 = safe_mul_128(b, norm_128); + + (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. +/// +/// The normalized dividend fits into 384 bits and is returned as a +/// `(u128, u256)` pair. The normalized divisor fits into a `u256`. +fn normalize_to_threshold_256_127(a: u256, b: u256) -> (u128, u256, u256) { + // Compile-time constant: 2^127 + let threshold: u128 = 0x80000000000000000000000000000000; + + let (b_high, _): (u128, u128) = ::into(b); + + assert!(not(is_zero_128(b_high))); + + let (norm, remainder): (u128, u128) = div_mod_128(threshold, b_high); + + 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 + } + }; + + match lt_128(b_high, threshold) { + true => { + let (high, low): (u128, u256) = mul_256_128(a, norm); + + (high, low, safe_mul_256_128(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)); + + // normalized dividend fits into 256 bits because remainder < divisor and + // divisor * norm fits into u64, so dividend * norm also fits + let (_, norm_dividend_low, norm_divisor, norm): (u64, u256, u128, u64) = + normalize_to_threshold_256_63(dividend, divisor, false); + + 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_64(v, q); + + 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 (u4, norm_dividend_low, norm_divisor, norm): (u64, u256, u128, u64) = + normalize_to_threshold_256_63(dividend, divisor, true); + + // normalized dividend fits into 320 bits + 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) { + 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), + } +} + +/// 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 (u2, norm_dividend_low, norm_divisor): (u128, u256, u256) = normalize_to_threshold_256_127(dividend, divisor); + + 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_128(divisor, 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) { + 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 { + 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 a95ee18..39dddb3 100644 --- a/simf/u128_test_arithmetic.simf +++ b/simf/u128_test_arithmetic.simf @@ -1,4 +1,21 @@ -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::lib::u128::{ + eq_128, + is_zero_128, + lt_128, + le_128, + add_128, + add_128_64, + full_add_128, + sub_128, + full_sub_128, + mul_128, + mul_128_64, + 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. @@ -7,7 +24,7 @@ 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)); } @@ -30,62 +47,63 @@ fn main() { 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(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) { + match if_test_this_function(8, 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) { + match if_test_this_function(9, 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)); + let (_, b): (u64, u64) = ::into(b); + let result: u256 = mul_128_64(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(8, fn_idx) { + match if_test_this_function(10, 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) { + match if_test_this_function(11, 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, r): (u64, u128) = algorithm_d(a, b); + let q: u64 = estimate_quotient_digit_base_64(u2, u1, u0, v1, v0); + + let (_, expected_q): (u64, u64) = ::into(unwrap(expected)); assert!(jet::eq_64(q, expected_q)); - assert!(eq_128(r, second_expected)); }, false => (), }; - match if_test_this_function(10, fn_idx) { + match if_test_this_function(12, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); let (_, expected_r): (u64, u64) = ::into(second_expected); @@ -98,7 +116,7 @@ fn main() { false => (), }; - match if_test_this_function(11, fn_idx) { + match if_test_this_function(13, fn_idx) { true => { let (q, r): (u128, u128) = div_mod_128(a, b); @@ -108,5 +126,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(14, 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/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.simf b/simf/u256_test.simf new file mode 100644 index 0000000..3cb186d --- /dev/null +++ b/simf/u256_test.simf @@ -0,0 +1,64 @@ +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, + 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 { + 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) { + 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_bits.simf b/simf/u256_test_bits.simf new file mode 100644 index 0000000..4739fcd --- /dev/null +++ b/simf/u256_test_bits.simf @@ -0,0 +1,34 @@ +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; + +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; + + /// 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 => { + 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(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)); + }, false => (), + }; +} diff --git a/simf/u256_test_compare.simf b/simf/u256_test_compare.simf new file mode 100644 index 0000000..7deedb5 --- /dev/null +++ b/simf/u256_test_compare.simf @@ -0,0 +1,16 @@ +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_bool: bool = witness::EXPECTED_BOOL; + + /// 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 => (), }; +} diff --git a/simf/u256_test_div.simf b/simf/u256_test_div.simf new file mode 100644 index 0000000..50576b1 --- /dev/null +++ b/simf/u256_test_div.simf @@ -0,0 +1,53 @@ +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() { + 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): (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); + + 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(2, 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(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 new file mode 100644 index 0000000..da49048 --- /dev/null +++ b/simf/u256_test_split_add.simf @@ -0,0 +1,44 @@ +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}; + +/// 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; + + /// Arithmetic + + match if_test_this_function(0, 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(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_sub_mul.simf b/simf/u256_test_sub_mul.simf new file mode 100644 index 0000000..2ca3442 --- /dev/null +++ b/simf/u256_test_sub_mul.simf @@ -0,0 +1,64 @@ +use crate::lib::u256::{sub_256, mul_256, mul_256_64, mul_256_128, split_256_into_64}; +use crate::lib::asserts::{assert_eq_64, assert_eq_128, assert_eq_256}; +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): (u64, u64, u64, u64) = split_256_into_64(unwrap(expected)); + + let (result_high, result_low): (u64, u256) = mul_256_64(a, b); + + assert_eq_64(result_high, expected); + assert_eq_256(result_low, second_expected); + }, + false => (), + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (_, b): (u128, u128) = ::into(b); + let (_, expected): (u128, u128) = ::into(unwrap(expected)); + + let (result_high, result_low): (u128, u256) = mul_256_128(a, b); + + assert_eq_128(result_high, expected); + assert_eq_256(result_low, second_expected); + }, + 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 => (), }; } diff --git a/tests/common/helper.rs b/tests/common/helper.rs new file mode 100644 index 0000000..6b5a568 --- /dev/null +++ b/tests/common/helper.rs @@ -0,0 +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; + +use crate::common::u256_wrapper::U256Wrapper; + +// Shared constants and helper functions used across integration tests + +pub const DEFAULT_BOOL: bool = false; + +pub fn generate_u256(lower_bound: U256, upper_bound: U256) -> U256 { + assert!( + lower_bound <= upper_bound, + "Error: lower bound is greater than upper bound" + ); + rand::thread_rng() + .gen_range(U256Wrapper(lower_bound)..=U256Wrapper(upper_bound)) + .0 +} 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/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/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/u128_test_arithmetic.rs b/tests/u128_test_arithmetic.rs index 7b72ea9..5fb5f95 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; @@ -16,11 +17,13 @@ enum FunctionToTest { Le128, Add128, Add128_64, + FullAdd128, Sub128, + FullSub128, Mul128, - Split256Into64, - NormalizeToThreshold, - AlgorithmD, + Mul128_64, + CalculateNormalizerBase64, + EstimateQuotientDigitBase64, DivMod128_64, DivMod128, Div128, @@ -31,7 +34,6 @@ fn op(o: FunctionToTest) -> u8 { o as u8 } -const DEFAULT_BOOL: bool = false; const DEFAULT_EXPECTED: u128 = 0; fn program() -> U128TestArithmeticProgram { @@ -322,6 +324,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); @@ -463,6 +569,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_sub(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); @@ -488,20 +696,23 @@ 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); + fn u128_test_mul_128_64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = rand::thread_rng().gen_range(0..u64::MAX); + let result = U256::from(a) * U256::from(b); + + let (result_high, result_low) = split_helper(result); run( &context, program(), build_witness( - op(FunctionToTest::Split256Into64), + op(FunctionToTest::Mul128_64), a, - b, - Some(a), + b as u128, + Some(result_high), DEFAULT_BOOL, - b, + result_low, DEFAULT_EXPECTED, ), Expect::Ok, @@ -509,107 +720,94 @@ mod u128_tests_arithmetic { } #[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 +819,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 +841,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 +863,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 +896,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 + 1..=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, 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/u16_math_test.rs b/tests/u16_math_test.rs index 10cc47f..88d3ab8 100644 --- a/tests/u16_math_test.rs +++ b/tests/u16_math_test.rs @@ -35,6 +35,6 @@ impl TestUint for u16 { mod u16_math_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 new file mode 100644 index 0000000..d15c05d --- /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 22 `#[simplex::test]` entry points for U256Wrapper. Logic lives in common::uint. + uint_tests!(U256Wrapper); +} diff --git a/tests/u256_test_bits.rs b/tests/u256_test_bits.rs new file mode 100644 index 0000000..ae070e1 --- /dev/null +++ b/tests/u256_test_bits.rs @@ -0,0 +1,198 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use crate::common::helper::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, + LeftShift256, + RightShift256, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U256TestBitsProgram { + U256TestBitsProgram::new(U256TestBitsArguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, +) -> U256TestBitsWitness { + U256TestBitsWitness { + function_index: function, + first_arg: a, + second_arg: b, + expected, + } +} + +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), + ), + 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), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_left_shift_256(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = rand::thread_rng().gen_range(1..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), + ), + 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), + ), + 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), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_right_shift_256(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = rand::thread_rng().gen_range(1..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), + ), + 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), + ), + 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), + ), + Expect::Ok, + ) + } +} diff --git a/tests/u256_test_compare.rs b/tests/u256_test_compare.rs new file mode 100644 index 0000000..0ed9e83 --- /dev/null +++ b/tests/u256_test_compare.rs @@ -0,0 +1,166 @@ +mod common; + +use primitive_types::U256; + +use crate::common::helper::generate_u256; +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_test_compare::U256TestCompareProgram; +use simplicityhl_std::artifacts::u256_test_compare::derived_u256_test_compare::{ + U256TestCompareArguments, U256TestCompareWitness, +}; + +enum FunctionToTest { + IsZero256, + Lt256, + Le256, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestCompareProgram { + U256TestCompareProgram::new(U256TestCompareArguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected_bool: bool, +) -> U256TestCompareWitness { + U256TestCompareWitness { + function_index: function, + first_arg: a, + second_arg: b, + expected_bool, + } +} + +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, true), + 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, false), + 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(), + true, + ), + 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, false), + 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(), + false, + ), + 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(), + true, + ), + 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, true), + 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(), + false, + ), + Expect::Ok, + ) + } +} diff --git a/tests/u256_test_div.rs b/tests/u256_test_div.rs new file mode 100644 index 0000000..9e04d00 --- /dev/null +++ b/tests/u256_test_div.rs @@ -0,0 +1,401 @@ +mod common; + +use primitive_types::U256; + +use crate::common::helper::generate_u256; +use common::core::{Expect, run}; + +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, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestDivProgram { + U256TestDivProgram::new(U256TestDivArguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, + second_expected: [u8; 32], +) -> U256TestDivWitness { + U256TestDivWitness { + 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_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); + 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 << 128) | (a_low); + let b = (high << 128) | (b_low); + + 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 << 128).to_big_endian(); + let b = ((high << 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_mod_256_edge_case(context: simplex::TestContext) -> anyhow::Result<()> { + let a: U256 = U256::from(2).pow(U256::from(255)); + let b = U256::from(2).pow(U256::from(127)) + U256::from(2).pow(U256::from(64)) - 1; + + let (q, r) = a.div_mod(b); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q.to_big_endian()), + r.to_big_endian(), + ), + 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, + ) + } +} diff --git a/tests/u256_test_split_add.rs b/tests/u256_test_split_add.rs new file mode 100644 index 0000000..18898c9 --- /dev/null +++ b/tests/u256_test_split_add.rs @@ -0,0 +1,146 @@ +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_split_add::U256TestSplitAddProgram; +use simplicityhl_std::artifacts::u256_test_split_add::derived_u256_test_split_add::{ + U256TestSplitAddArguments, U256TestSplitAddWitness, +}; + +enum FunctionToTest { + Split256Into64, + Add256, + Add256_128, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestSplitAddProgram { + U256TestSplitAddProgram::new(U256TestSplitAddArguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, + expected_bool: bool, +) -> U256TestSplitAddWitness { + U256TestSplitAddWitness { + function_index: function, + first_arg: a, + second_arg: b, + expected, + expected_bool, + } +} + +mod u256_tests_arithmetic { + use super::*; + + #[simplex::test] + fn u256_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, + ), + 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, + ), + 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, + ), + 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)); + 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, + ), + 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)); + 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, + ), + Expect::Ok, + ) + } +} diff --git a/tests/u256_test_sub_mul.rs b/tests/u256_test_sub_mul.rs new file mode 100644 index 0000000..d6b8006 --- /dev/null +++ b/tests/u256_test_sub_mul.rs @@ -0,0 +1,261 @@ +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_sub_mul::U256TestSubMulProgram; +use simplicityhl_std::artifacts::u256_test_sub_mul::derived_u256_test_sub_mul::{ + U256TestSubMulArguments, U256TestSubMulWitness, +}; + +enum FunctionToTest { + Sub256, + Mul256, + Mul256_64, + Mul256_128, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestSubMulProgram { + U256TestSubMulProgram::new(U256TestSubMulArguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, + expected_bool: bool, + second_expected: [u8; 32], +) -> U256TestSubMulWitness { + U256TestSubMulWitness { + 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 u256_test_mul_256_64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u64::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_64), + a.to_big_endian(), + b.to_big_endian(), + Some(result_high), + DEFAULT_BOOL, + result_low, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_256_128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u128::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_128), + a.to_big_endian(), + b.to_big_endian(), + Some(result_high), + DEFAULT_BOOL, + result_low, + ), + Expect::Ok, + ) + } +} diff --git a/tests/u32_math_test.rs b/tests/u32_math_test.rs index 5c6c009..abaedef 100644 --- a/tests/u32_math_test.rs +++ b/tests/u32_math_test.rs @@ -35,6 +35,6 @@ impl TestUint for u32 { mod u32_math_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_math_tests.rs b/tests/u8_math_tests.rs index 2d5c120..cc69367 100644 --- a/tests/u8_math_tests.rs +++ b/tests/u8_math_tests.rs @@ -35,6 +35,6 @@ impl TestUint for u8 { mod u8_math_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 7fe7d6075a4b30a677cdbcfe531c70711e22d224 Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Fri, 14 Aug 2026 17:45:30 +0300 Subject: [PATCH 06/11] =?UTF-8?q?=D0=A1onvert=20functions=20for=20u64=20(#?= =?UTF-8?q?41)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * added convert functions for u64 * typo * returned u32 typo back, it will be fixed in upcoming pr; renamed to -> into for u64 * added missing split_u64_into_u8 --- simf/lib/u64/convert.simf | 72 ++++++ simf/lib/{u64.simf => u64/math.simf} | 5 - simf/u64_convert_test.simf | 154 ++++++++++++ simf/{u64_test.simf => u64_math_test.simf} | 22 +- tests/u64_convert_test.rs | 260 +++++++++++++++++++++ tests/u64_math_test.rs | 40 ++++ tests/u64_test.rs | 75 ------ 7 files changed, 538 insertions(+), 90 deletions(-) create mode 100644 simf/lib/u64/convert.simf rename simf/lib/{u64.simf => u64/math.simf} (94%) create mode 100644 simf/u64_convert_test.simf rename simf/{u64_test.simf => u64_math_test.simf} (85%) create mode 100644 tests/u64_convert_test.rs create mode 100644 tests/u64_math_test.rs delete mode 100644 tests/u64_test.rs diff --git a/simf/lib/u64/convert.simf b/simf/lib/u64/convert.simf new file mode 100644 index 0000000..8a7d985 --- /dev/null +++ b/simf/lib/u64/convert.simf @@ -0,0 +1,72 @@ +/// Widening uint conversions + +/// Converts u64 to u128 +pub fn u64_to_u128(a: u64) -> u128 { + <(u64, u64)>::into((0, a)) +} + +/// Converts u64 to u256 +pub fn u64_to_u256(a: u64) -> u256 { + let a_u128: u128 = u64_to_u128(a); + + <(u128, u128)>::into((0, a_u128)) +} + +/// Splitting uint conversions + +/// Splits u64 into eight u8 +pub fn split_u64_into_u8(a: u64) -> (u8, u8, u8, u8, u8, u8, u8, u8) { + ::into(a) +} + +/// Splits u64 into four u16 +pub fn split_u64_into_u16(a: u64) -> (u16, u16, u16, u16) { + ::into(a) +} + +/// Splits u64 into two u32 +pub fn split_u64_into_u32(a: u64) -> (u32, u32) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u64 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u64_to_u1(a: u64) -> u1 { + let u1_max: u64 = jet::left_pad_low_1_64(jet::high_1()); + + assert!(jet::le_64(a, u1_max)); + + jet::rightmost_64_1(a) +} + +/// Converts u64 into u8. +/// Panics if the value does not fit in u8 +pub fn safe_u64_to_u8(a: u64) -> u8 { + let u8_max: u64 = jet::left_pad_low_8_64(jet::high_8()); + + assert!(jet::le_64(a, u8_max)); + + jet::rightmost_64_8(a) +} + +/// Converts u64 into u16. +/// Panics if the value does not fit in u16 +pub fn safe_u64_to_u16(a: u64) -> u16 { + let u16_max: u64 = jet::left_pad_low_16_64(jet::high_16()); + + assert!(jet::le_64(a, u16_max)); + + jet::rightmost_64_16(a) +} + +/// Converts u64 into u32. +/// Panics if the value does not fit in u32 +pub fn safe_u64_to_u32(a: u64) -> u32 { + let u32_max: u64 = jet::left_pad_low_32_64(jet::high_32()); + + assert!(jet::le_64(a, u32_max)); + + jet::rightmost_64_32(a) +} diff --git a/simf/lib/u64.simf b/simf/lib/u64/math.simf similarity index 94% rename from simf/lib/u64.simf rename to simf/lib/u64/math.simf index f3fd3da..77fc4aa 100644 --- a/simf/lib/u64.simf +++ b/simf/lib/u64/math.simf @@ -67,8 +67,3 @@ pub fn gt_64(a: u64, b: u64) -> bool { pub fn ge_64(a: u64, b: u64) -> bool { jet::le_64(b, a) } - -/// Widen `u64` to a `u256` scalar. -pub fn u64_into_u256(val: u64) -> u256 { - <(u64, u64, u64, u64)>::into((0, 0, 0, val)) -} diff --git a/simf/u64_convert_test.simf b/simf/u64_convert_test.simf new file mode 100644 index 0000000..00d651e --- /dev/null +++ b/simf/u64_convert_test.simf @@ -0,0 +1,154 @@ +use crate::lib::u64::convert::{ + u64_to_u128, + u64_to_u256, + split_u64_into_u8, + split_u64_into_u16, + split_u64_into_u32, + safe_u64_to_u1, + safe_u64_to_u8, + safe_u64_to_u16, + safe_u64_to_u32 +}; +use crate::lib::asserts::{ + assert_eq_1, + assert_eq_8, + assert_eq_16, + assert_eq_32, + assert_eq_128, + assert_eq_256 +}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u64 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert_eq_128(u64_to_u128(a), expected); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + assert_eq_256(u64_to_u256(a), expected); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + let ( + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = split_u64_into_u8(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let ( + expected7, + expected6, + expected5, + expected4, + expected3, + expected2, + expected1, + expected0 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = ::into(expected); + + assert_eq_8(a7, expected7); + assert_eq_8(a6, expected6); + assert_eq_8(a5, expected5); + assert_eq_8(a4, expected4); + assert_eq_8(a3, expected3); + assert_eq_8(a2, expected2); + assert_eq_8(a1, expected1); + assert_eq_8(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (a3, a2, a1, a0): (u16, u16, u16, u16) = split_u64_into_u16(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u16, u16, u16, u16) = ::into(expected); + + assert_eq_16(a3, expected3); + assert_eq_16(a2, expected2); + assert_eq_16(a1, expected1); + assert_eq_16(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (a1, a0): (u32, u32) = split_u64_into_u32(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (expected1, expected0): (u32, u32) = ::into(expected); + + assert_eq_32(a1, expected1); + assert_eq_32(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u64_to_u1(a), expected); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + + assert_eq_8(safe_u64_to_u8(a), expected); + }, + false => {}, + }; + + match if_test_this_function(7, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u16 = jet::rightmost_64_16(expected); + + assert_eq_16(safe_u64_to_u16(a), expected); + }, + false => {}, + }; + + match if_test_this_function(8, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u32 = jet::rightmost_64_32(expected); + + assert_eq_32(safe_u64_to_u32(a), expected); + }, + false => {}, + }; +} diff --git a/simf/u64_test.simf b/simf/u64_math_test.simf similarity index 85% rename from simf/u64_test.simf rename to simf/u64_math_test.simf index 879215f..0866a57 100644 --- a/simf/u64_test.simf +++ b/simf/u64_math_test.simf @@ -1,6 +1,14 @@ -use crate::lib::u64::{ - checked_add_64, safe_add_64, checked_sub_64, safe_sub_64, checked_mul_64, safe_mul_64, checked_div_64, safe_div_64, - gt_64, ge_64, u64_into_u256, +use crate::lib::u64::math::{ + checked_add_64, + safe_add_64, + checked_sub_64, + safe_sub_64, + checked_mul_64, + safe_mul_64, + checked_div_64, + safe_div_64, + gt_64, + ge_64 }; use crate::lib::asserts::{assert_none_64, assert_eq_64}; use crate::lib::binary::not; @@ -11,8 +19,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_64(result), Some(e: u64) => assert_eq_64(unwrap(result), e), + None => assert_none_64(result), } } @@ -32,9 +40,6 @@ fn main() { let b: u64 = witness::SECOND_ARG; let expected: Option = witness::EXPECTED; - let expected_widen: u256 = witness::EXPECTED_WIDEN; - - // add match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_64(a, b), expected); }, false => (), }; match if_test_this_function(1, fn_idx) { true => { assert!(jet::eq_64(safe_add_64(a, b), unwrap(expected))); }, false => (), }; @@ -54,7 +59,4 @@ fn main() { // gt, ge match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_64(a, b), expected); }, false => (), }; match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_64(a, b), expected); }, false => (), }; - - // Custom - match if_test_this_function(192, fn_idx) { true => { assert!(jet::eq_256(u64_into_u256(a), expected_widen)); }, false => (), }; } diff --git a/tests/u64_convert_test.rs b/tests/u64_convert_test.rs new file mode 100644 index 0000000..1a847f8 --- /dev/null +++ b/tests/u64_convert_test.rs @@ -0,0 +1,260 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u64_convert_test::U64ConvertTestProgram; +use simplicityhl_std::artifacts::u64_convert_test::derived_u64_convert_test::{ + U64ConvertTestArguments, U64ConvertTestWitness, +}; + +enum FunctionToTest { + U64ToU128, + U64ToU256, + SplitU64IntoU8, + SplitU64IntoU16, + SplitU64IntoU32, + SafeU64ToU1, + SafeU64ToU8, + SafeU64ToU16, + SafeU64ToU32, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U64ConvertTestProgram { + U64ConvertTestProgram::new(U64ConvertTestArguments {}) +} + +fn build_witness(function: u8, a: u64, expected: [u8; 32]) -> U64ConvertTestWitness { + U64ConvertTestWitness { + function_index: function, + first_arg: a, + expected, + } +} + +mod u64_convert_test { + use super::*; + + #[simplex::test] + fn u64_convert_test_u64_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U64ToU128), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_u64_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U64ToU256), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_split_u64_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU64IntoU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_split_u64_into_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU64IntoU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_split_u64_into_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU64IntoU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u1_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX as u64); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u8_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u8::MAX as u64 + 1..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX as u64); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u16_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u16::MAX as u64 + 1..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX as u64); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u32_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u32::MAX as u64 + 1..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u64_math_test.rs b/tests/u64_math_test.rs new file mode 100644 index 0000000..91214e6 --- /dev/null +++ b/tests/u64_math_test.rs @@ -0,0 +1,40 @@ +mod common; + +use common::uint::TestUint; + +use simplicityhl_std::artifacts::u64_math_test::U64MathTestProgram; +use simplicityhl_std::artifacts::u64_math_test::derived_u64_math_test::{ + U64MathTestArguments, U64MathTestWitness, +}; + +// The only per-width code for the common operations. +impl TestUint for u64 { + type Program = U64MathTestProgram; + type Witness = U64MathTestWitness; + + const ZERO: u64 = 0; + const ONE: u64 = 1; + const MAX: u64 = u64::MAX; + const HALF_MAX: u64 = u64::MAX / 2; + const MUL_BOUND: u64 = 1 << 32; // 2^(64/2) + + fn program() -> U64MathTestProgram { + U64MathTestProgram::new(U64MathTestArguments {}) + } + + fn witness(op: u8, a: u64, b: u64, expected: Option) -> U64MathTestWitness { + U64MathTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + expected, + } + } +} + +mod u64_math_tests { + use super::*; + + // Stamps the 22 `#[simplex::test]` entry points for u64. Logic lives in common::uint. + uint_tests!(u64); +} diff --git a/tests/u64_test.rs b/tests/u64_test.rs deleted file mode 100644 index bb30a03..0000000 --- a/tests/u64_test.rs +++ /dev/null @@ -1,75 +0,0 @@ -mod common; - -use common::uint::TestUint; - -use simplicityhl_std::artifacts::u64_test::U64TestProgram; -use simplicityhl_std::artifacts::u64_test::derived_u64_test::{U64TestArguments, U64TestWitness}; - -use crate::common::uint::CUSTOM_BASE; - -// The only per-width code for the common operations. -impl TestUint for u64 { - type Program = U64TestProgram; - type Witness = U64TestWitness; - - const ZERO: u64 = 0; - const ONE: u64 = 1; - const MAX: u64 = u64::MAX; - const HALF_MAX: u64 = u64::MAX / 2; - const MUL_BOUND: u64 = 1 << 32; // 2^(64/2) - - fn program() -> U64TestProgram { - U64TestProgram::new(U64TestArguments {}) - } - - fn witness(op: u8, a: u64, b: u64, expected: Option) -> U64TestWitness { - U64TestWitness { - function_index: op, - first_arg: a, - second_arg: b, - expected, - ..Default::default() - } - } -} - -enum FunctionToTest { - U64Widen, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 + CUSTOM_BASE -} - -mod u64_tests { - use rand::Rng as _; - - use crate::FunctionToTest::U64Widen; - use crate::common::core::{Expect, run}; - - use super::*; - - // Stamps the 22 `#[simplex::test]` entry points for u64. Logic lives in common::uint. - uint_tests!(u64); - - #[simplex::test] - fn u64_into_u256(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u64::MAX); - - let mut expected_widen: [u8; 32] = [0u8; 32]; - expected_widen[24..32].copy_from_slice(&a.to_be_bytes()); - - run( - &context, - ::program(), - U64TestWitness { - function_index: op(U64Widen), - first_arg: a, - expected_widen, - ..Default::default() - }, - Expect::Ok, - ) - } -} From db7164881fc8feb2e9688577fc221d350e11768a Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Mon, 17 Aug 2026 11:59:25 +0300 Subject: [PATCH 07/11] fixed typo: `split to` -> `split into` (#43) --- simf/lib/u16/convert.simf | 2 +- simf/lib/u32/convert.simf | 4 ++-- simf/lib/u8/convert.simf | 2 +- simf/u16_convert_test.simf | 4 ++-- simf/u32_convert_test.simf | 8 ++++---- simf/u8_convert_test.simf | 4 ++-- tests/u16_convert_test.rs | 6 +++--- tests/u32_convert_test.rs | 12 ++++++------ tests/u8_convert_test.rs | 6 +++--- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/simf/lib/u16/convert.simf b/simf/lib/u16/convert.simf index f1a3b9f..34b4b15 100644 --- a/simf/lib/u16/convert.simf +++ b/simf/lib/u16/convert.simf @@ -27,7 +27,7 @@ pub fn u16_to_u256(a: u16) -> u256 { /// Splitting uint conversions /// Splits u16 into two u8 -pub fn split_u16_to_u8(a: u16) -> (u8, u8) { +pub fn split_u16_into_u8(a: u16) -> (u8, u8) { ::into(a) } diff --git a/simf/lib/u32/convert.simf b/simf/lib/u32/convert.simf index b5028bb..4145570 100644 --- a/simf/lib/u32/convert.simf +++ b/simf/lib/u32/convert.simf @@ -22,12 +22,12 @@ pub fn u32_to_u256(a: u32) -> u256 { /// Splitting uint conversions /// Splits u32 into four u8 -pub fn split_u32_to_u8(a: u32) -> (u8, u8, u8, u8) { +pub fn split_u32_into_u8(a: u32) -> (u8, u8, u8, u8) { ::into(a) } /// Splits u32 into two u16 -pub fn split_u32_to_u16(a: u32) -> (u16, u16) { +pub fn split_u32_into_u16(a: u32) -> (u16, u16) { ::into(a) } diff --git a/simf/lib/u8/convert.simf b/simf/lib/u8/convert.simf index fee231c..bfe9bc4 100644 --- a/simf/lib/u8/convert.simf +++ b/simf/lib/u8/convert.simf @@ -32,7 +32,7 @@ pub fn u8_to_u256(a: u8) -> u256 { /// Splitting uint conversions /// Splits u8 into eight u1 -pub fn split_u8_to_u1(a: u8) -> (u1, u1, u1, u1, u1, u1, u1, u1) { +pub fn split_u8_into_u1(a: u8) -> (u1, u1, u1, u1, u1, u1, u1, u1) { ::into(a) } diff --git a/simf/u16_convert_test.simf b/simf/u16_convert_test.simf index 7a59769..20beff3 100644 --- a/simf/u16_convert_test.simf +++ b/simf/u16_convert_test.simf @@ -3,7 +3,7 @@ use crate::lib::u16::convert::{ u16_to_u64, u16_to_u128, u16_to_u256, - split_u16_to_u8, + split_u16_into_u8, safe_u16_to_u1, safe_u16_to_u8 }; @@ -55,7 +55,7 @@ fn main() { match if_test_this_function(4, fn_idx) { true => { - let (a1, a0): (u8, u8) = split_u16_to_u8(a); + let (a1, a0): (u8, u8) = split_u16_into_u8(a); let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u16 = jet::rightmost_64_16(expected); diff --git a/simf/u32_convert_test.simf b/simf/u32_convert_test.simf index af21275..b8dedee 100644 --- a/simf/u32_convert_test.simf +++ b/simf/u32_convert_test.simf @@ -2,8 +2,8 @@ use crate::lib::u32::convert::{ u32_to_u64, u32_to_u128, u32_to_u256, - split_u32_to_u8, - split_u32_to_u16, + split_u32_into_u8, + split_u32_into_u16, safe_u32_to_u1, safe_u32_to_u8, safe_u32_to_u16 @@ -46,7 +46,7 @@ fn main() { match if_test_this_function(3, fn_idx) { true => { - let (a3, a2, a1, a0): (u8, u8, u8, u8) = split_u32_to_u8(a); + let (a3, a2, a1, a0): (u8, u8, u8, u8) = split_u32_into_u8(a); let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u32 = jet::rightmost_64_32(expected); @@ -67,7 +67,7 @@ fn main() { match if_test_this_function(4, fn_idx) { true => { - let (a1, a0): (u16, u16) = split_u32_to_u16(a); + let (a1, a0): (u16, u16) = split_u32_into_u16(a); let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u32 = jet::rightmost_64_32(expected); diff --git a/simf/u8_convert_test.simf b/simf/u8_convert_test.simf index 928baf8..f272a6c 100644 --- a/simf/u8_convert_test.simf +++ b/simf/u8_convert_test.simf @@ -4,7 +4,7 @@ use crate::lib::u8::convert::{ u8_to_u64, u8_to_u128, u8_to_u256, - split_u8_to_u1, + split_u8_into_u1, safe_u8_to_u1 }; // use crate::lib::u256::{split_256_into_64}; TODO: uncomment when u256 functions are merged @@ -75,7 +75,7 @@ fn main() { a2, a1, a0 - ): (u1, u1, u1, u1, u1, u1, u1, u1) = split_u8_to_u1(a); + ): (u1, u1, u1, u1, u1, u1, u1, u1) = split_u8_into_u1(a); let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u8 = jet::rightmost_64_8(expected); diff --git a/tests/u16_convert_test.rs b/tests/u16_convert_test.rs index f9c222d..56c6a0a 100644 --- a/tests/u16_convert_test.rs +++ b/tests/u16_convert_test.rs @@ -15,7 +15,7 @@ enum FunctionToTest { U16ToU64, U16ToU128, U16ToU256, - SplitU16ToU8, + SplitU16IntoU8, SafeU16ToU1, SafeU16ToU8, } @@ -105,14 +105,14 @@ mod u16_convert_test { } #[simplex::test] - fn u16_convert_test_split_u16_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + fn u16_convert_test_split_u16_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u16::MAX); run( &context, program(), build_witness( - op(FunctionToTest::SplitU16ToU8), + op(FunctionToTest::SplitU16IntoU8), a, U256::from(a).to_big_endian(), ), diff --git a/tests/u32_convert_test.rs b/tests/u32_convert_test.rs index 860cab9..1ed57be 100644 --- a/tests/u32_convert_test.rs +++ b/tests/u32_convert_test.rs @@ -14,8 +14,8 @@ enum FunctionToTest { U32ToU64, U32ToU128, U32ToU256, - SplitU32ToU8, - SplitU32ToU16, + SplitU32IntoU8, + SplitU32IntoU16, SafeU32ToU1, SafeU32ToU8, SafeU32ToU16, @@ -90,14 +90,14 @@ mod u32_convert_test { } #[simplex::test] - fn u32_convert_test_split_u32_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + fn u32_convert_test_split_u32_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u32::MAX); run( &context, program(), build_witness( - op(FunctionToTest::SplitU32ToU8), + op(FunctionToTest::SplitU32IntoU8), a, U256::from(a).to_big_endian(), ), @@ -106,14 +106,14 @@ mod u32_convert_test { } #[simplex::test] - fn u32_convert_test_split_u32_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + fn u32_convert_test_split_u32_into_u16(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u32::MAX); run( &context, program(), build_witness( - op(FunctionToTest::SplitU32ToU16), + op(FunctionToTest::SplitU32IntoU16), a, U256::from(a).to_big_endian(), ), diff --git a/tests/u8_convert_test.rs b/tests/u8_convert_test.rs index 6331249..fac8c88 100644 --- a/tests/u8_convert_test.rs +++ b/tests/u8_convert_test.rs @@ -16,7 +16,7 @@ enum FunctionToTest { U8ToU64, U8ToU128, U8ToU256, - SplitU8ToU1, + SplitU8IntoU1, SafeU8ToU1, } @@ -121,14 +121,14 @@ mod u8_convert_test { } #[simplex::test] - fn u8_convert_test_split_u8_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + fn u8_convert_test_split_u8_into_u1(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u8::MAX); run( &context, program(), build_witness( - op(FunctionToTest::SplitU8ToU1), + op(FunctionToTest::SplitU8IntoU1), a, U256::from(a).to_big_endian(), ), From 60dc1559d00f0a23d4af220a5f25ace33687b84b Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Mon, 17 Aug 2026 12:21:35 +0300 Subject: [PATCH 08/11] Convert functions for u128 (#42) * moved u128 to math file * added convert functions for u128 * typo * fixed typo * added missing split_u128 functions * resolved todo; utilized assert_eq in tests * fixed path * splitted math file into comparison and bit * renamed arithmetic test to basic math test for avoid ambiguity --- simf/lib/asserts.simf | 2 +- simf/lib/u128/bit.simf | 79 +++++ simf/lib/u128/comparison.simf | 48 +++ simf/lib/u128/convert.simf | 94 ++++++ simf/lib/{u128.simf => u128/math.simf} | 124 +------- simf/lib/u256.simf | 12 +- ...thmetic.simf => u128_basic_math_test.simf} | 36 +-- simf/u128_convert_test.simf | 240 ++++++++++++++ simf/{u128_test.simf => u128_math_test.simf} | 16 +- simf/u128_test_bits.simf | 2 +- simf/u128_test_compare.simf | 20 ++ simf/u16_convert_test.simf | 19 +- simf/u1_convert_test.simf | 24 +- simf/u32_convert_test.simf | 17 +- simf/u8_convert_test.simf | 22 +- ..._arithmetic.rs => u128_basic_math_test.rs} | 225 +------------ tests/u128_convert_test.rs | 295 ++++++++++++++++++ tests/{u128_test.rs => u128_math_test.rs} | 20 +- tests/u128_test_compare.rs | 142 +++++++++ 19 files changed, 1017 insertions(+), 420 deletions(-) create mode 100644 simf/lib/u128/bit.simf create mode 100644 simf/lib/u128/comparison.simf create mode 100644 simf/lib/u128/convert.simf rename simf/lib/{u128.simf => u128/math.simf} (78%) rename simf/{u128_test_arithmetic.simf => u128_basic_math_test.simf} (75%) create mode 100644 simf/u128_convert_test.simf rename simf/{u128_test.simf => u128_math_test.simf} (76%) create mode 100644 simf/u128_test_compare.simf rename tests/{u128_test_arithmetic.rs => u128_basic_math_test.rs} (81%) create mode 100644 tests/u128_convert_test.rs rename tests/{u128_test.rs => u128_math_test.rs} (58%) create mode 100644 tests/u128_test_compare.rs diff --git a/simf/lib/asserts.simf b/simf/lib/asserts.simf index 63c4a9a..1c6ad58 100644 --- a/simf/lib/asserts.simf +++ b/simf/lib/asserts.simf @@ -1,4 +1,4 @@ -use crate::lib::u128::eq_128; +use crate::lib::u128::bit::eq_128; /// Asserts that two `u1` are equal pub fn assert_eq_1(a: u1, b: u1) { diff --git a/simf/lib/u128/bit.simf b/simf/lib/u128/bit.simf new file mode 100644 index 0000000..1e4f0e9 --- /dev/null +++ b/simf/lib/u128/bit.simf @@ -0,0 +1,79 @@ +use crate::lib::binary::and; + +/// Bit logic + +/// Bitwise AND of two 128-bit values +pub fn and_128(a: u128, b: u128) -> u128 { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + <(u64, u64)>::into((jet::and_64(a_high, b_high), jet::and_64(a_low, b_low))) +} + +/// Bitwise OR of two 128-bit values +pub fn or_128(a: u128, b: u128) -> u128 { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + <(u64, u64)>::into((jet::or_64(a_high, b_high), jet::or_64(a_low, b_low))) +} + +/// Checks if two 128-bit values are equal +pub fn eq_128(a: u128, b: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + and(jet::eq_64(a_high, b_high), jet::eq_64(a_low, b_low)) +} + +/// Left-shift a 128-bit value by the given amount. Bits are filled with zeroes +pub fn left_shift_128(shift: u8, a: u128) -> u128 { + match jet::is_zero_8(shift) { + true => a, + false => { + let (a_high, a_low): (u64, u64) = ::into(a); + + match jet::lt_8(shift, 64) { + true => { + let (_, low_to_high_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 + let shifted_bits: u64 = jet::right_shift_64(low_to_high_amount, a_low); + + let res_high: u64 = jet::or_64(jet::left_shift_64(shift, a_high), shifted_bits); + + <(u64, u64)>::into((res_high, jet::left_shift_64(shift, a_low))) + }, + false => { + let (_, shift): (bool, u8) = jet::subtract_8(shift, 64); // shift >= 64 + + <(u64, u64)>::into((jet::left_shift_64(shift, a_low), 0)) + } + } + } + } +} + +/// Right-shift a 128-bit value by the given amount. Bits are filled with zeroes +pub fn right_shift_128(shift: u8, a: u128) -> u128 { + match jet::is_zero_8(shift) { + true => a, + false => { + let (a_high, a_low): (u64, u64) = ::into(a); + + match jet::lt_8(shift, 64) { + true => { + let (_, high_to_low_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 + let shifted_bits: u64 = jet::left_shift_64(high_to_low_amount, a_high); + + let res_low: u64 = jet::or_64(jet::right_shift_64(shift, a_low), shifted_bits); + + <(u64, u64)>::into((jet::right_shift_64(shift, a_high), res_low)) + }, + false => { + let (_, shift): (bool, u8) = jet::subtract_8(shift, 64); // shift >= 64 + + <(u64, u64)>::into((0, jet::right_shift_64(shift, a_high))) + } + } + } + } +} diff --git a/simf/lib/u128/comparison.simf b/simf/lib/u128/comparison.simf new file mode 100644 index 0000000..25c0b58 --- /dev/null +++ b/simf/lib/u128/comparison.simf @@ -0,0 +1,48 @@ +use crate::lib::binary::and; + +/// Comparison operations + +/// Checks if an integer is zero +pub fn is_zero_128(a: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + + and(jet::is_zero_64(a_high), jet::is_zero_64(a_low)) +} + +/// Checks if an integer is less than another integer +pub fn lt_128(a: u128, b: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + 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, + }, + } +} + +/// Checks if an integer is less than or equal to another integer +pub fn le_128(a: u128, b: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + 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, + }, + } +} + +/// Check if an integer is greater than another integer +pub fn gt_128(a: u128, b: u128) -> bool { + lt_128(b, a) +} + +/// Check if an integer is greater than or equal to another integer +pub fn ge_128(a: u128, b: u128) -> bool { + le_128(b, a) +} diff --git a/simf/lib/u128/convert.simf b/simf/lib/u128/convert.simf new file mode 100644 index 0000000..4b6aea9 --- /dev/null +++ b/simf/lib/u128/convert.simf @@ -0,0 +1,94 @@ +use crate::lib::u1::convert::u1_to_u128; +use crate::lib::u8::convert::u8_to_u128; +use crate::lib::u16::convert::u16_to_u128; +use crate::lib::u32::convert::u32_to_u128; +use crate::lib::u64::convert::u64_to_u128; +use crate::lib::u128::comparison::le_128; + +/// Widening uint conversions + +/// Converts u128 to u256 +pub fn u128_to_u256(a: u128) -> u256 { + <(u128, u128)>::into((0, a)) +} + +/// Splitting uint conversions + +/// Splits u128 into sixteen u8 +pub fn split_u128_into_u8( + a: u128 +) -> (u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8) { + ::into(a) +} + +/// Splits u128 into eight u16 +pub fn split_u128_into_u16(a: u128) -> (u16, u16, u16, u16, u16, u16, u16, u16) { + ::into(a) +} + +/// Splits u128 into four u32 +pub fn split_u128_into_u32(a: u128) -> (u32, u32, u32, u32) { + ::into(a) +} + +/// Splits u128 into two u64 +pub fn split_u128_into_u64(a: u128) -> (u64, u64) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u128 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u128_to_u1(a: u128) -> u1 { + let u1_max: u128 = u1_to_u128(jet::high_1()); + let (_, a_64): (u64, u64) = split_u128_into_u64(a); + + assert!(le_128(a, u1_max)); + + jet::rightmost_64_1(a_64) +} + +/// Converts u128 into u8. +/// Panics if the value does not fit in u8 +pub fn safe_u128_to_u8(a: u128) -> u8 { + let u8_max: u128 = u8_to_u128(jet::high_8()); + let (_, a_64): (u64, u64) = split_u128_into_u64(a); + + assert!(le_128(a, u8_max)); + + jet::rightmost_64_8(a_64) +} + +/// Converts u128 into u16. +/// Panics if the value does not fit in u16 +pub fn safe_u128_to_u16(a: u128) -> u16 { + let u16_max: u128 = u16_to_u128(jet::high_16()); + let (_, a_64): (u64, u64) = split_u128_into_u64(a); + + assert!(le_128(a, u16_max)); + + jet::rightmost_64_16(a_64) +} + +/// Converts u128 into u32. +/// Panics if the value does not fit in u32 +pub fn safe_u128_to_u32(a: u128) -> u32 { + let u32_max: u128 = u32_to_u128(jet::high_32()); + let (_, a_64): (u64, u64) = split_u128_into_u64(a); + + assert!(le_128(a, u32_max)); + + jet::rightmost_64_32(a_64) +} + +/// Converts u128 into u64. +/// Panics if the value does not fit in u64 +pub fn safe_u128_to_u64(a: u128) -> u64 { + let u64_max: u128 = u64_to_u128(jet::high_64()); + let (_, a_64): (u64, u64) = split_u128_into_u64(a); + + assert!(le_128(a, u64_max)); + + a_64 +} diff --git a/simf/lib/u128.simf b/simf/lib/u128/math.simf similarity index 78% rename from simf/lib/u128.simf rename to simf/lib/u128/math.simf index 342de61..1909850 100644 --- a/simf/lib/u128.simf +++ b/simf/lib/u128/math.simf @@ -1,130 +1,8 @@ use crate::lib::binary::{not, or, and}; - -/// Bit logic - -/// Bitwise AND of two 128-bit values -pub fn and_128(a: u128, b: u128) -> u128 { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - <(u64, u64)>::into((jet::and_64(a_high, b_high), jet::and_64(a_low, b_low))) -} - -/// Bitwise OR of two 128-bit values -pub fn or_128(a: u128, b: u128) -> u128 { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - <(u64, u64)>::into((jet::or_64(a_high, b_high), jet::or_64(a_low, b_low))) -} - -/// Checks if two 128-bit values are equal -pub fn eq_128(a: u128, b: u128) -> bool { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - and(jet::eq_64(a_high, b_high), jet::eq_64(a_low, b_low)) -} - -/// Left-shift a 128-bit value by the given amount. Bits are filled with zeroes -pub fn left_shift_128(shift: u8, a: u128) -> u128 { - match jet::is_zero_8(shift) { - true => a, - false => { - let (a_high, a_low): (u64, u64) = ::into(a); - - match jet::lt_8(shift, 64) { - true => { - let (_, low_to_high_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 - let shifted_bits: u64 = jet::right_shift_64(low_to_high_amount, a_low); - - let res_high: u64 = jet::or_64(jet::left_shift_64(shift, a_high), shifted_bits); - - <(u64, u64)>::into((res_high, jet::left_shift_64(shift, a_low))) - }, - false => { - let (_, shift): (bool, u8) = jet::subtract_8(shift, 64); // shift >= 64 - - <(u64, u64)>::into((jet::left_shift_64(shift, a_low), 0)) - } - } - } - } -} - -/// Right-shift a 128-bit value by the given amount. Bits are filled with zeroes -pub fn right_shift_128(shift: u8, a: u128) -> u128 { - match jet::is_zero_8(shift) { - true => a, - false => { - let (a_high, a_low): (u64, u64) = ::into(a); - - match jet::lt_8(shift, 64) { - true => { - let (_, high_to_low_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 - let shifted_bits: u64 = jet::left_shift_64(high_to_low_amount, a_high); - - let res_low: u64 = jet::or_64(jet::right_shift_64(shift, a_low), shifted_bits); - - <(u64, u64)>::into((jet::right_shift_64(shift, a_high), res_low)) - }, - false => { - let (_, shift): (bool, u8) = jet::subtract_8(shift, 64); // shift >= 64 - - <(u64, u64)>::into((0, jet::right_shift_64(shift, a_high))) - } - } - } - } -} +use crate::lib::u128::comparison::{is_zero_128, lt_128}; /// Arithmetic -/// Checks if an integer is zero -pub fn is_zero_128(a: u128) -> bool { - let (a_high, a_low): (u64, u64) = ::into(a); - - and(jet::is_zero_64(a_high), jet::is_zero_64(a_low)) -} - -/// Checks if an integer is less than another integer -pub fn lt_128(a: u128, b: u128) -> bool { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - 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, - }, - } -} - -/// Checks if an integer is less than or equal to another integer -pub fn le_128(a: u128, b: u128) -> bool { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - 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, - }, - } -} - -/// Check if an integer is greater than another integer -pub fn gt_128(a: u128, b: u128) -> bool { - lt_128(b, a) -} - -/// Check if an integer is greater than or equal to another integer -pub fn ge_128(a: u128, b: u128) -> bool { - le_128(b, a) -} - /// Adds two integers and returns the carry pub fn add_128(a: u128, b: u128) -> (bool, u128) { let (a_high, a_low): (u64, u64) = ::into(a); diff --git a/simf/lib/u256.simf b/simf/lib/u256.simf index fcbe0a8..95c8d79 100644 --- a/simf/lib/u256.simf +++ b/simf/lib/u256.simf @@ -1,13 +1,5 @@ use crate::lib::binary::{not, and}; -use crate::lib::u128::{ - and_128, - or_128, - eq_128, - left_shift_128, - right_shift_128, - is_zero_128, - lt_128, - le_128, +use crate::lib::u128::math::{ add_128, full_add_128, sub_128, @@ -20,6 +12,8 @@ use crate::lib::u128::{ div_mod_128, div_mod_128_64 }; +use crate::lib::u128::bit::{and_128, or_128, eq_128, left_shift_128, right_shift_128}; +use crate::lib::u128::comparison::{is_zero_128, lt_128, le_128}; /// Bit logic diff --git a/simf/u128_test_arithmetic.simf b/simf/u128_basic_math_test.simf similarity index 75% rename from simf/u128_test_arithmetic.simf rename to simf/u128_basic_math_test.simf index 39dddb3..17d5992 100644 --- a/simf/u128_test_arithmetic.simf +++ b/simf/u128_basic_math_test.simf @@ -1,8 +1,4 @@ -use crate::lib::u128::{ - eq_128, - is_zero_128, - lt_128, - le_128, +use crate::lib::u128::math::{ add_128, add_128_64, full_add_128, @@ -16,6 +12,7 @@ use crate::lib::u128::{ div_mod_128, div_128 }; +use crate::lib::u128::bit::eq_128; use crate::helper::{if_test_this_function, assert_bool}; /// Asserts a result equals expected u128 and bool values. @@ -37,22 +34,17 @@ fn main() { let expected_bool: bool = witness::EXPECTED_BOOL; let second_expected: u128 = witness::SECOND_EXPECTED; // for cases where the result is a 256 bit value - let third_expected: u128 = witness::THIRD_EXPECTED; // for cases where the result is 256 bit + 128 bit values /// 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_eq_uint_bool(add_128(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(1, 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(2, 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(3, fn_idx) { true => { assert_eq_uint_bool(sub_128(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(4, 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) { + match if_test_this_function(5, fn_idx) { true => { let result: u256 = mul_128(a, b); @@ -64,7 +56,7 @@ fn main() { false => (), }; - match if_test_this_function(9, fn_idx) { + match if_test_this_function(6, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); let result: u256 = mul_128_64(a, b); @@ -77,7 +69,7 @@ fn main() { false => (), }; - match if_test_this_function(10, fn_idx) { + match if_test_this_function(7, fn_idx) { true => { let norm: u64 = calculate_normalizer_base_64(b, expected_bool); @@ -88,7 +80,7 @@ fn main() { false => (), }; - match if_test_this_function(11, fn_idx) { + match if_test_this_function(8, fn_idx) { true => { let (_, u2): (u64, u64) = ::into(a); let (u1, u0): (u64, u64) = ::into(b); @@ -103,7 +95,7 @@ fn main() { false => (), }; - match if_test_this_function(12, fn_idx) { + match if_test_this_function(9, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); let (_, expected_r): (u64, u64) = ::into(second_expected); @@ -116,7 +108,7 @@ fn main() { false => (), }; - match if_test_this_function(13, fn_idx) { + match if_test_this_function(10, fn_idx) { true => { let (q, r): (u128, u128) = div_mod_128(a, b); @@ -126,5 +118,5 @@ fn main() { false => (), }; - match if_test_this_function(14, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(11, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => (), }; } diff --git a/simf/u128_convert_test.simf b/simf/u128_convert_test.simf new file mode 100644 index 0000000..0d70dcf --- /dev/null +++ b/simf/u128_convert_test.simf @@ -0,0 +1,240 @@ +use crate::lib::u128::convert::{ + u128_to_u256, + split_u128_into_u8, + split_u128_into_u16, + split_u128_into_u32, + split_u128_into_u64, + safe_u128_to_u1, + safe_u128_to_u8, + safe_u128_to_u16, + safe_u128_to_u32, + safe_u128_to_u64 +}; +use crate::lib::asserts::{ + assert_eq_1, + assert_eq_8, + assert_eq_16, + assert_eq_32, + assert_eq_64, + assert_eq_256 +}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u128 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_256(u128_to_u256(a), expected); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + let ( + a15, + a14, + a13, + a12, + a11, + a10, + a9, + a8, + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): ( + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8 + ) = split_u128_into_u8(a); + + let (_, _, expected1, expected0): (u64, u64, u64, u64) = ::into(expected); + let (expected3, expected2): (u32, u32) = ::into(expected1); + let (expected1, expected0): (u32, u32) = ::into(expected0); + + let ( + expected15, + expected14, + expected13, + expected12 + ): (u8, u8, u8, u8) = ::into(expected3); + let ( + expected11, + expected10, + expected9, + expected8 + ): (u8, u8, u8, u8) = ::into(expected2); + let ( + expected7, + expected6, + expected5, + expected4 + ): (u8, u8, u8, u8) = ::into(expected1); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u8, u8, u8, u8) = ::into(expected0); + + assert_eq_8(a15, expected15); + assert_eq_8(a14, expected14); + assert_eq_8(a13, expected13); + assert_eq_8(a12, expected12); + assert_eq_8(a11, expected11); + assert_eq_8(a10, expected10); + assert_eq_8(a9, expected9); + assert_eq_8(a8, expected8); + assert_eq_8(a7, expected7); + assert_eq_8(a6, expected6); + assert_eq_8(a5, expected5); + assert_eq_8(a4, expected4); + assert_eq_8(a3, expected3); + assert_eq_8(a2, expected2); + assert_eq_8(a1, expected1); + assert_eq_8(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + let ( + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): (u16, u16, u16, u16, u16, u16, u16, u16) = split_u128_into_u16(a); + + let (_, _, expected1, expected0): (u64, u64, u64, u64) = ::into(expected); + + let ( + expected7, + expected6, + expected5, + expected4 + ): (u16, u16, u16, u16) = ::into(expected1); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u16, u16, u16, u16) = ::into(expected0); + + assert_eq_16(a7, expected7); + assert_eq_16(a6, expected6); + assert_eq_16(a5, expected5); + assert_eq_16(a4, expected4); + assert_eq_16(a3, expected3); + assert_eq_16(a2, expected2); + assert_eq_16(a1, expected1); + assert_eq_16(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (a3, a2, a1, a0): (u32, u32, u32, u32) = split_u128_into_u32(a); + + let (_, _, expected1, expected0): (u64, u64, u64, u64) = ::into(expected); + let (expected3, expected2): (u32, u32) = ::into(expected1); + let (expected1, expected0): (u32, u32) = ::into(expected0); + + assert_eq_32(a3, expected3); + assert_eq_32(a2, expected2); + assert_eq_32(a1, expected1); + assert_eq_32(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (a1, a0): (u64, u64) = split_u128_into_u64(a); + + let (_, _, expected1, expected0): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(a1, expected1); + assert_eq_64(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u128_to_u1(a), expected); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + + assert_eq_8(safe_u128_to_u8(a), expected); + }, + false => {}, + }; + + match if_test_this_function(7, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u16 = jet::rightmost_64_16(expected); + + assert_eq_16(safe_u128_to_u16(a), expected); + }, + false => {}, + }; + + match if_test_this_function(8, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u32 = jet::rightmost_64_32(expected); + + assert_eq_32(safe_u128_to_u32(a), expected); + }, + false => {}, + }; + + match if_test_this_function(9, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(safe_u128_to_u64(a), expected); + }, + false => {}, + }; +} diff --git a/simf/u128_test.simf b/simf/u128_math_test.simf similarity index 76% rename from simf/u128_test.simf rename to simf/u128_math_test.simf index 15b99fe..1c73009 100644 --- a/simf/u128_test.simf +++ b/simf/u128_math_test.simf @@ -1,4 +1,4 @@ -use crate::lib::u128::{ +use crate::lib::u128::math::{ checked_add_128, safe_add_128, checked_sub_128, @@ -6,11 +6,9 @@ use crate::lib::u128::{ checked_mul_128, safe_mul_128, checked_div_128, - safe_div_128, - eq_128, - gt_128, - ge_128 + safe_div_128 }; +use crate::lib::u128::comparison::{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; @@ -45,19 +43,19 @@ fn main() { // add match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_128(a, b), expected); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { assert!(eq_128(safe_add_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => {assert_eq_128(safe_add_128(a, b), unwrap(expected)); }, false => (), }; // sub match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_128(a, b), expected); }, false => (), }; - match if_test_this_function(3, fn_idx) { true => { assert!(eq_128(safe_sub_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => {assert_eq_128(safe_sub_128(a, b), unwrap(expected)); }, false => (), }; // mul match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_128(a, b), expected); }, false => (), }; - match if_test_this_function(5, fn_idx) { true => { assert!(eq_128(safe_mul_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => {assert_eq_128(safe_mul_128(a, b), unwrap(expected)); }, false => (), }; // div match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_128(a, b), expected); }, false => (), }; - match if_test_this_function(7, fn_idx) { true => { assert!(eq_128(safe_div_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => {assert_eq_128(safe_div_128(a, b), unwrap(expected)); }, false => (), }; // gt, ge match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_128(a, b), expected); }, false => (), }; diff --git a/simf/u128_test_bits.simf b/simf/u128_test_bits.simf index d492b02..b3ea239 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::bit::{and_128, or_128, eq_128, left_shift_128, right_shift_128}; use crate::helper::{if_test_this_function, assert_bool}; fn main() { diff --git a/simf/u128_test_compare.simf b/simf/u128_test_compare.simf new file mode 100644 index 0000000..e9badb1 --- /dev/null +++ b/simf/u128_test_compare.simf @@ -0,0 +1,20 @@ +use crate::lib::u128::comparison::{ + is_zero_128, + lt_128, + le_128, +}; +use crate::helper::{if_test_this_function, assert_bool}; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u128 = witness::FIRST_ARG; + let b: u128 = witness::SECOND_ARG; + let expected_bool: bool = witness::EXPECTED_BOOL; + + /// Comparison operations + + 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 => (), }; +} diff --git a/simf/u16_convert_test.simf b/simf/u16_convert_test.simf index 20beff3..847d4f3 100644 --- a/simf/u16_convert_test.simf +++ b/simf/u16_convert_test.simf @@ -7,8 +7,15 @@ use crate::lib::u16::convert::{ safe_u16_to_u1, safe_u16_to_u8 }; -use crate::lib::u128::eq_128; -use crate::lib::asserts::{assert_eq_1, assert_eq_8}; +use crate::lib::u128::bit::eq_128; +use crate::lib::asserts::{ + assert_eq_1, + assert_eq_8, + assert_eq_32, + assert_eq_64, + assert_eq_128, + assert_eq_256 +}; use crate::helper::if_test_this_function; fn main() { @@ -23,7 +30,7 @@ fn main() { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let (_, expected): (u32, u32) = ::into(expected); - assert!(jet::eq_32(u16_to_u32(a), expected)); + assert_eq_32(u16_to_u32(a), expected); }, false => {}, }; @@ -32,7 +39,7 @@ fn main() { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); - assert!(jet::eq_64(u16_to_u64(a), expected)); + assert_eq_64(u16_to_u64(a), expected); }, false => {}, }; @@ -41,14 +48,14 @@ fn main() { true => { let (_, expected): (u128, u128) = ::into(expected); - assert!(eq_128(u16_to_u128(a), expected)); + assert_eq_128(u16_to_u128(a), expected); }, false => {}, }; match if_test_this_function(3, fn_idx) { true => { - assert!(jet::eq_256(u16_to_u256(a), expected)); + assert_eq_256(u16_to_u256(a), expected); }, false => {}, }; diff --git a/simf/u1_convert_test.simf b/simf/u1_convert_test.simf index fff7a92..8263efc 100644 --- a/simf/u1_convert_test.simf +++ b/simf/u1_convert_test.simf @@ -7,8 +7,16 @@ use crate::lib::u1::convert::{ u1_to_u256, u1_to_bool }; -use crate::lib::u128::eq_128; -use crate::lib::asserts::assert_eq_bool; +use crate::lib::u128::bit::eq_128; +use crate::lib::asserts::{ + assert_eq_bool, + assert_eq_8, + assert_eq_16, + assert_eq_32, + assert_eq_64, + assert_eq_128, + assert_eq_256 +}; use crate::helper::if_test_this_function; fn main() { @@ -24,7 +32,7 @@ fn main() { let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); let (_, expected): (u8, u8) = ::into(expected); - assert!(jet::eq_8(u1_to_u8(a), expected)); + assert_eq_8(u1_to_u8(a), expected); }, false => (), }; @@ -34,7 +42,7 @@ fn main() { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); - assert!(jet::eq_16(u1_to_u16(a), expected)); + assert_eq_16(u1_to_u16(a), expected); }, false => (), }; @@ -44,7 +52,7 @@ fn main() { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let (_, expected): (u32, u32) = ::into(expected); - assert!(jet::eq_32(u1_to_u32(a), expected)); + assert_eq_32(u1_to_u32(a), expected); }, false => (), }; @@ -53,7 +61,7 @@ fn main() { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); - assert!(jet::eq_64(u1_to_u64(a), expected)); + assert_eq_64(u1_to_u64(a), expected); }, false => (), }; @@ -62,14 +70,14 @@ fn main() { true => { let (_, expected): (u128, u128) = ::into(expected); - assert!(eq_128(u1_to_u128(a), expected)); + assert_eq_128(u1_to_u128(a), expected); }, false => (), }; match if_test_this_function(5, fn_idx) { true => { - assert!(jet::eq_256(u1_to_u256(a), expected)); + assert_eq_256(u1_to_u256(a), expected); }, false => (), }; diff --git a/simf/u32_convert_test.simf b/simf/u32_convert_test.simf index b8dedee..658cc58 100644 --- a/simf/u32_convert_test.simf +++ b/simf/u32_convert_test.simf @@ -8,8 +8,15 @@ use crate::lib::u32::convert::{ safe_u32_to_u8, safe_u32_to_u16 }; -use crate::lib::u128::eq_128; -use crate::lib::asserts::{assert_eq_1, assert_eq_8, assert_eq_16}; +use crate::lib::u128::bit::eq_128; +use crate::lib::asserts::{ + assert_eq_1, + assert_eq_8, + assert_eq_16, + assert_eq_64, + assert_eq_128, + assert_eq_256 +}; use crate::helper::if_test_this_function; fn main() { @@ -23,7 +30,7 @@ fn main() { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); - assert!(jet::eq_64(u32_to_u64(a), expected)); + assert_eq_64(u32_to_u64(a), expected); }, false => {}, }; @@ -32,14 +39,14 @@ fn main() { true => { let (_, expected): (u128, u128) = ::into(expected); - assert!(eq_128(u32_to_u128(a), expected)); + assert_eq_128(u32_to_u128(a), expected); }, false => {}, }; match if_test_this_function(2, fn_idx) { true => { - assert!(jet::eq_256(u32_to_u256(a), expected)); + assert_eq_256(u32_to_u256(a), expected); }, false => {}, }; diff --git a/simf/u8_convert_test.simf b/simf/u8_convert_test.simf index f272a6c..2256638 100644 --- a/simf/u8_convert_test.simf +++ b/simf/u8_convert_test.simf @@ -7,9 +7,15 @@ use crate::lib::u8::convert::{ split_u8_into_u1, safe_u8_to_u1 }; -// use crate::lib::u256::{split_256_into_64}; TODO: uncomment when u256 functions are merged -use crate::lib::u128::eq_128; -use crate::lib::asserts::assert_eq_1; +use crate::lib::u128::bit::eq_128; +use crate::lib::asserts::{ + assert_eq_1, + assert_eq_16, + assert_eq_32, + assert_eq_64, + assert_eq_128, + assert_eq_256 +}; use crate::helper::if_test_this_function; fn main() { @@ -24,7 +30,7 @@ fn main() { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); - assert!(jet::eq_16(u8_to_u16(a), expected)); + assert_eq_16(u8_to_u16(a), expected); }, false => (), }; @@ -34,7 +40,7 @@ fn main() { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let (_, expected): (u32, u32) = ::into(expected); - assert!(jet::eq_32(u8_to_u32(a), expected)); + assert_eq_32(u8_to_u32(a), expected); }, false => (), }; @@ -43,7 +49,7 @@ fn main() { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); - assert!(jet::eq_64(u8_to_u64(a), expected)); + assert_eq_64(u8_to_u64(a), expected); }, false => (), }; @@ -52,14 +58,14 @@ fn main() { true => { let (_, expected): (u128, u128) = ::into(expected); - assert!(eq_128(u8_to_u128(a), expected)); + assert_eq_128(u8_to_u128(a), expected); }, false => (), }; match if_test_this_function(4, fn_idx) { true => { - assert!(jet::eq_256(u8_to_u256(a), expected)); + assert_eq_256(u8_to_u256(a), expected); }, false => (), }; diff --git a/tests/u128_test_arithmetic.rs b/tests/u128_basic_math_test.rs similarity index 81% rename from tests/u128_test_arithmetic.rs rename to tests/u128_basic_math_test.rs index 5fb5f95..133f2bc 100644 --- a/tests/u128_test_arithmetic.rs +++ b/tests/u128_basic_math_test.rs @@ -6,15 +6,12 @@ use rand::Rng; use crate::common::helper::DEFAULT_BOOL; use common::core::{Expect, run}; -use simplicityhl_std::artifacts::u128_test_arithmetic::U128TestArithmeticProgram; -use simplicityhl_std::artifacts::u128_test_arithmetic::derived_u128_test_arithmetic::{ - U128TestArithmeticArguments, U128TestArithmeticWitness, +use simplicityhl_std::artifacts::u128_basic_math_test::U128BasicMathTestProgram; +use simplicityhl_std::artifacts::u128_basic_math_test::derived_u128_basic_math_test::{ + U128BasicMathTestArguments, U128BasicMathTestWitness, }; enum FunctionToTest { - IsZero128, - Lt128, - Le128, Add128, Add128_64, FullAdd128, @@ -36,8 +33,8 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: u128 = 0; -fn program() -> U128TestArithmeticProgram { - U128TestArithmeticProgram::new(U128TestArithmeticArguments {}) +fn program() -> U128BasicMathTestProgram { + U128BasicMathTestProgram::new(U128BasicMathTestArguments {}) } fn build_witness( @@ -47,16 +44,14 @@ fn build_witness( expected: Option, expected_bool: bool, second_expected: u128, - third_expected: u128, -) -> U128TestArithmeticWitness { - U128TestArithmeticWitness { +) -> U128BasicMathTestWitness { + U128BasicMathTestWitness { function_index: function, first_arg: a, second_arg: b, expected, expected_bool, second_expected, - third_expected, } } @@ -70,172 +65,6 @@ fn split_helper(a: U256) -> (u128, u128) { mod u128_tests_arithmetic { use super::*; - #[simplex::test] - fn u128_test_is_zero_128_true(context: simplex::TestContext) -> anyhow::Result<()> { - let a = 0; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::IsZero128), - a, - DEFAULT_EXPECTED, - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_is_zero_128_false(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u128::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::IsZero128), - a, - DEFAULT_EXPECTED, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_lt_128_less(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..u128::MAX); - let b = a + 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Lt128), - a, - b, - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_lt_128_eq(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..u128::MAX); - let b = a; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Lt128), - a, - b, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_lt_128_bigger(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u128::MAX); - let b = a - 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Lt128), - a, - b, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_le_128_less(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..u128::MAX); - let b = a + 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Le128), - a, - b, - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_le_128_eq(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..u128::MAX); - let b = a; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Le128), - a, - b, - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_le_128_bigger(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u128::MAX); - let b = a - 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Le128), - a, - b, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - #[simplex::test] fn u128_test_add_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u128::MAX / 2); @@ -252,7 +81,6 @@ mod u128_tests_arithmetic { Some(result), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -274,7 +102,6 @@ mod u128_tests_arithmetic { Some(result), true, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -296,7 +123,6 @@ mod u128_tests_arithmetic { Some(result), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -318,7 +144,6 @@ mod u128_tests_arithmetic { Some(result), true, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -344,7 +169,6 @@ mod u128_tests_arithmetic { Some(result), result_carry, carry_low, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -370,7 +194,6 @@ mod u128_tests_arithmetic { Some(result), result_carry, carry_low, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -396,7 +219,6 @@ mod u128_tests_arithmetic { Some(result), result_carry, carry_low, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -422,7 +244,6 @@ mod u128_tests_arithmetic { Some(result), result_carry, carry_low, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -444,7 +265,6 @@ mod u128_tests_arithmetic { Some(result), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -464,7 +284,6 @@ mod u128_tests_arithmetic { Some(0), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -491,7 +310,6 @@ mod u128_tests_arithmetic { Some(result), carry, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -520,7 +338,6 @@ mod u128_tests_arithmetic { Some(result), carry, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -541,7 +358,6 @@ mod u128_tests_arithmetic { Some(a), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -563,7 +379,6 @@ mod u128_tests_arithmetic { Some(result), true, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -589,7 +404,6 @@ mod u128_tests_arithmetic { Some(result), result_borrow, borrow_low, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -615,7 +429,6 @@ mod u128_tests_arithmetic { Some(result), result_borrow, borrow_low, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -639,7 +452,6 @@ mod u128_tests_arithmetic { Some(result), result_borrow, borrow_low, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -665,7 +477,6 @@ mod u128_tests_arithmetic { Some(result - 1), result_borrow, borrow_low, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -689,7 +500,6 @@ mod u128_tests_arithmetic { Some(result_high), DEFAULT_BOOL, result_low, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -713,7 +523,6 @@ mod u128_tests_arithmetic { Some(result_high), DEFAULT_BOOL, result_low, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -739,7 +548,6 @@ mod u128_tests_arithmetic { Some(norm), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -763,7 +571,6 @@ mod u128_tests_arithmetic { Some(1), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -790,7 +597,6 @@ mod u128_tests_arithmetic { Some(norm), true, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -812,7 +618,6 @@ mod u128_tests_arithmetic { Some(DEFAULT_EXPECTED), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::AssertFailed, ) @@ -834,7 +639,6 @@ mod u128_tests_arithmetic { Some(DEFAULT_EXPECTED), true, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::AssertFailed, ) @@ -856,7 +660,6 @@ mod u128_tests_arithmetic { Some(DEFAULT_EXPECTED), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::AssertFailed, ) @@ -889,7 +692,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, b, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -923,7 +725,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, b, - DEFAULT_EXPECTED, ), Expect::AssertFailed, ) @@ -947,7 +748,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -968,7 +768,6 @@ mod u128_tests_arithmetic { Some(DEFAULT_EXPECTED), DEFAULT_BOOL, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::AssertFailed, ) @@ -992,7 +791,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -1016,7 +814,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -1045,7 +842,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -1069,7 +865,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -1096,7 +891,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -1116,7 +910,6 @@ mod u128_tests_arithmetic { Some(1u128), DEFAULT_BOOL, 0u128, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -1141,7 +934,6 @@ mod u128_tests_arithmetic { Some(1u128), DEFAULT_BOOL, u64::MAX as u128, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -1166,7 +958,6 @@ mod u128_tests_arithmetic { Some(0u128), DEFAULT_BOOL, a, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -1188,7 +979,6 @@ mod u128_tests_arithmetic { Some(result), DEFAULT_BOOL, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -1209,7 +999,6 @@ mod u128_tests_arithmetic { Some(DEFAULT_EXPECTED), DEFAULT_BOOL, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::AssertFailed, ) diff --git a/tests/u128_convert_test.rs b/tests/u128_convert_test.rs new file mode 100644 index 0000000..bf42042 --- /dev/null +++ b/tests/u128_convert_test.rs @@ -0,0 +1,295 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u128_convert_test::U128ConvertTestProgram; +use simplicityhl_std::artifacts::u128_convert_test::derived_u128_convert_test::{ + U128ConvertTestArguments, U128ConvertTestWitness, +}; + +enum FunctionToTest { + U128ToU256, + SplitU128IntoU8, + SplitU128IntoU16, + SplitU128IntoU32, + SplitU128IntoU64, + SafeU128ToU1, + SafeU128ToU8, + SafeU128ToU16, + SafeU128ToU32, + SafeU128ToU64, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U128ConvertTestProgram { + U128ConvertTestProgram::new(U128ConvertTestArguments {}) +} + +fn build_witness(function: u8, a: u128, expected: [u8; 32]) -> U128ConvertTestWitness { + U128ConvertTestWitness { + function_index: function, + first_arg: a, + expected, + } +} + +mod u128_convert_test { + use super::*; + + #[simplex::test] + fn u128_convert_test_u128_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U128ToU256), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_split_u128_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU128IntoU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_split_u128_into_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU128IntoU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_split_u128_into_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU128IntoU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_split_u128_into_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU128IntoU64), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u1_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX as u128); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u8_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u8::MAX as u128 + 1..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX as u128); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u16_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u16::MAX as u128 + 1..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX as u128); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u32_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u32::MAX as u128 + 1..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX as u128); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU64), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u64_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u64::MAX as u128 + 1..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU64), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u128_test.rs b/tests/u128_math_test.rs similarity index 58% rename from tests/u128_test.rs rename to tests/u128_math_test.rs index f2411bb..a7ea30f 100644 --- a/tests/u128_test.rs +++ b/tests/u128_math_test.rs @@ -2,15 +2,15 @@ mod common; use common::uint::TestUint; -use simplicityhl_std::artifacts::u128_test::U128TestProgram; -use simplicityhl_std::artifacts::u128_test::derived_u128_test::{ - U128TestArguments, U128TestWitness, +use simplicityhl_std::artifacts::u128_math_test::U128MathTestProgram; +use simplicityhl_std::artifacts::u128_math_test::derived_u128_math_test::{ + U128MathTestArguments, U128MathTestWitness, }; // The only per-width code for the common operations. impl TestUint for u128 { - type Program = U128TestProgram; - type Witness = U128TestWitness; + type Program = U128MathTestProgram; + type Witness = U128MathTestWitness; const ZERO: u128 = 0; const ONE: u128 = 1; @@ -18,12 +18,12 @@ impl TestUint for u128 { const HALF_MAX: u128 = u128::MAX / 2; const MUL_BOUND: u128 = 1 << 64; // 2^(128/2) - fn program() -> U128TestProgram { - U128TestProgram::new(U128TestArguments {}) + fn program() -> U128MathTestProgram { + U128MathTestProgram::new(U128MathTestArguments {}) } - fn witness(op: u8, a: u128, b: u128, expected: Option) -> U128TestWitness { - U128TestWitness { + fn witness(op: u8, a: u128, b: u128, expected: Option) -> U128MathTestWitness { + U128MathTestWitness { function_index: op, first_arg: a, second_arg: b, @@ -32,7 +32,7 @@ impl TestUint for u128 { } } -mod u128_tests { +mod u128_math_tests { use super::*; // Stamps the 22 `#[simplex::test]` entry points for u128. Logic lives in common::uint. diff --git a/tests/u128_test_compare.rs b/tests/u128_test_compare.rs new file mode 100644 index 0000000..24e1187 --- /dev/null +++ b/tests/u128_test_compare.rs @@ -0,0 +1,142 @@ +mod common; + +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u128_test_compare::U128TestCompareProgram; +use simplicityhl_std::artifacts::u128_test_compare::derived_u128_test_compare::{ + U128TestCompareArguments, U128TestCompareWitness, +}; + +enum FunctionToTest { + IsZero128, + Lt128, + Le128, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: u128 = 0; + +fn program() -> U128TestCompareProgram { + U128TestCompareProgram::new(U128TestCompareArguments {}) +} + +fn build_witness(function: u8, a: u128, b: u128, expected_bool: bool) -> U128TestCompareWitness { + U128TestCompareWitness { + function_index: function, + first_arg: a, + second_arg: b, + expected_bool, + } +} + +mod u128_tests_arithmetic { + use super::*; + + #[simplex::test] + fn u128_test_is_zero_128_true(context: simplex::TestContext) -> anyhow::Result<()> { + let a = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::IsZero128), a, DEFAULT_EXPECTED, true), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_is_zero_128_false(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + + run( + &context, + program(), + build_witness(op(FunctionToTest::IsZero128), a, DEFAULT_EXPECTED, false), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_lt_128_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a + 1; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Lt128), a, b, true), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_lt_128_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Lt128), a, b, false), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_lt_128_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + let b = a - 1; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Lt128), a, b, false), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_le_128_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a + 1; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Le128), a, b, true), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_le_128_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Le128), a, b, true), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_le_128_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + let b = a - 1; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Le128), a, b, false), + Expect::Ok, + ) + } +} From 733b93336f4743c651b0b399db555f87a2dda17d Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Tue, 18 Aug 2026 16:35:13 +0200 Subject: [PATCH 09/11] Convert functions for u256 (#47) * added convert functions for u256; splitted u256_math into bit and comparison * updated readme * bumped simplex version --- Cargo.lock | 249 ++++++----- Cargo.toml | 2 +- README.md | 42 +- simf/lib/u256/bit.simf | 71 +++ simf/lib/u256/comparison.simf | 50 +++ simf/lib/u256/convert.simf | 138 ++++++ simf/lib/{u256.simf => u256/math.simf} | 133 +----- simf/u256_convert_test.simf | 406 ++++++++++++++++++ simf/{u256_test.simf => u256_math_test.simf} | 7 +- ...test_split_add.simf => u256_test_add.simf} | 23 +- simf/u256_test_bits.simf | 7 +- simf/u256_test_compare.simf | 4 +- simf/u256_test_div.simf | 7 +- simf/u256_test_sub_mul.simf | 7 +- tests/u256_convert_test.rs | 331 ++++++++++++++ tests/{u256_test.rs => u256_math_test.rs} | 20 +- ...256_test_split_add.rs => u256_test_add.rs} | 37 +- tests/u256_test_compare.rs | 2 +- 18 files changed, 1208 insertions(+), 328 deletions(-) create mode 100644 simf/lib/u256/bit.simf create mode 100644 simf/lib/u256/comparison.simf create mode 100644 simf/lib/u256/convert.simf rename simf/lib/{u256.simf => u256/math.simf} (79%) create mode 100644 simf/u256_convert_test.simf rename simf/{u256_test.simf => u256_math_test.simf} (96%) rename simf/{u256_test_split_add.simf => u256_test_add.simf} (53%) create mode 100644 tests/u256_convert_test.rs rename tests/{u256_test.rs => u256_math_test.rs} (68%) rename tests/{u256_test_split_add.rs => u256_test_add.rs} (73%) diff --git a/Cargo.lock b/Cargo.lock index e9dcc4c..4ccea75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -69,15 +69,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "ar_archive_writer" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348" +checksum = "73cd58deff2140a0a8eae87e417bd01db68a33e148aa93d1e8cd837e55e312b6" dependencies = [ "object", ] @@ -96,9 +96,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" +checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" dependencies = [ "aws-lc-sys", "zeroize", @@ -106,9 +106,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" +checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" dependencies = [ "cc", "cmake", @@ -177,9 +177,9 @@ dependencies = [ [[package]] name = "bitcoin-consensus-encoding" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" +checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" dependencies = [ "bitcoin-internals", "hex-conservative 1.2.0", @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ "memchr", "serde_core", @@ -328,9 +328,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.67" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" +checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -360,18 +360,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.2" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.6.2" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -496,9 +496,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" dependencies = [ "serde", ] @@ -571,15 +571,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "fixed-hash" @@ -613,21 +613,21 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", "futures-task", @@ -677,20 +677,20 @@ checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7" [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "globset" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.16", + "regex-automata 0.4.18", "regex-syntax 0.8.11", ] @@ -772,15 +772,15 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.30" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b009b6744c1445efd7244084e25e498636412effb6760b55067553baa925cc7" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.16", + "regex-automata 0.4.18", "same-file", "walkdir", "winapi-util", @@ -803,7 +803,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -849,9 +849,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -887,9 +887,9 @@ checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "linux-raw-sys" @@ -983,9 +983,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" dependencies = [ "num-traits", ] @@ -1001,9 +1001,9 @@ dependencies = [ [[package]] name = "object" -version = "0.37.3" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" dependencies = [ "memchr", ] @@ -1045,7 +1045,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1068,9 +1068,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "ppv-lite86" @@ -1088,7 +1088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.119", ] [[package]] @@ -1113,18 +1113,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] [[package]] name = "psm" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +checksum = "4dcd034599e63b970727f70d79e02d62390a4a84f7c6b827c27c46d5ac3fa622" dependencies = [ "ar_archive_writer", "cc", @@ -1132,9 +1132,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -1194,9 +1194,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -1263,9 +1263,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.42" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "aws-lc-rs", "log", @@ -1278,18 +1278,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" dependencies = [ "aws-lc-rs", "ring", @@ -1358,9 +1358,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -1368,29 +1368,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -1491,9 +1491,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smplx-build" -version = "0.0.8" +version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a9a38dc0d803acdeed6217cdf9c1b3ded9220796315a458f47572269abaf2f" +checksum = "f9f3e2235cc1bb20a5dda4bd32c9be665f39c95f1bcc62d7aa2a9adfc8fb670f" dependencies = [ "glob", "globwalk", @@ -1503,27 +1503,28 @@ dependencies = [ "quote", "serde", "simplicityhl", - "syn", + "syn 2.0.119", "thiserror", "toml", + "toml_edit", ] [[package]] name = "smplx-macros" -version = "0.0.8" +version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e1091132a1f5157111920504e403fc8a4eb8a7814a376e4bd4c55a7fb6edff" +checksum = "03e1a764666ba1aada361499286710a0d5d7f1a0251d882a0843bb67ae5b7973" dependencies = [ "smplx-build", "smplx-test", - "syn", + "syn 2.0.119", ] [[package]] name = "smplx-regtest" -version = "0.0.8" +version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ccf7e3eade934e890bcee3558af658de46177dbd6a5dd0302bb1a64b7701bbe" +checksum = "f60a5ca5ccc0b531f4915f1d044c68b7ba3bc344e84ac019f539b2e488c8c8d7" dependencies = [ "electrsd", "hex", @@ -1537,9 +1538,9 @@ dependencies = [ [[package]] name = "smplx-sdk" -version = "0.0.8" +version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d06536b493296672867c1a1ea8d67538c233af72624c18694ecb50691fcb74" +checksum = "940acb2e942bda24f3f27c3a387aabcf866dc16299746e79a58b47d091162f5d" dependencies = [ "bip39", "bitcoin_hashes", @@ -1557,9 +1558,9 @@ dependencies = [ [[package]] name = "smplx-std" -version = "0.0.8" +version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "834fd437c9394610ece857755d5967124a0b97c6837bf1170ae8deed38ba4d32" +checksum = "bcfa462d2e480d1fa6baa382d578501991fd19c3fb15ae5cdd44be362ef5e9ef" dependencies = [ "either", "serde", @@ -1571,9 +1572,9 @@ dependencies = [ [[package]] name = "smplx-test" -version = "0.0.8" +version = "0.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82bf119d89ca2b2ac992377912efe7dd1915e0aa6a193ca5383fb8a3da86594c" +checksum = "d76eab48bbf610aea049644b6aacffebbb194e59c663cd450b1f30a29ead19c1" dependencies = [ "electrsd", "proc-macro2", @@ -1582,16 +1583,16 @@ dependencies = [ "simplicityhl", "smplx-regtest", "smplx-sdk", - "syn", + "syn 2.0.119", "thiserror", "toml", ] [[package]] name = "stacker" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967" dependencies = [ "cc", "cfg-if", @@ -1629,6 +1630,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tap" version = "1.0.1" @@ -1650,22 +1662,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -1725,14 +1737,15 @@ dependencies = [ "indexmap", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", + "toml_writer", "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ "winnow 1.0.4", ] @@ -1751,9 +1764,9 @@ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uint" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +checksum = "6f9227a75a5a540a464c832ad4a4195dbdbecd8787610a56262721fde6f04f90" dependencies = [ "byteorder", "crunchy", @@ -1824,9 +1837,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -1837,9 +1850,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1847,31 +1860,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] [[package]] name = "webpki-roots" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -2020,22 +2033,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d636ad8..25078a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["simplicity", "liquid", "elements", "smart-contracts"] categories = ["cryptography::cryptocurrencies"] [dependencies] -smplx-std = "0.0.8" +smplx-std = "0.0.9" [dev-dependencies] anyhow = { version = "1.0.101" } diff --git a/README.md b/README.md index e607392..9c489e8 100644 --- a/README.md +++ b/README.md @@ -15,20 +15,44 @@ simf/lib │ ├── Subtraction for `Fe`, `Scalar`, `Gej`. │ ├── Equality predicates and their `assert_*` counterparts. │ └── Safe Jacobian-to-affine normalization. +├── u1 +│ └── convert.simf +├── u8 +│ ├── convert.simf +│ └── math.simf +├── u16 +│ ├── convert.simf +│ └── math.simf +├── u32 +│ ├── convert.simf +│ └── math.simf +├── u64 +│ ├── convert.simf +│ └── math.simf +├── u128 +│ ├── bit.simf +│ │ └── Basic bit operations that are available as jets for `u8`–`u64` but are missing for `u128`. +│ ├── comparison.simf +│ │ └── Basic comparison operations that are available as jets for `u8`–`u64` but are missing for `u128`. +│ ├── convert.simf +│ │ └── Conversions between `u128` and other uint types. +│ └── math.simf +│ └── Overflow-checked arithmetic operations. +├── u256 +│ ├── bit.simf +│ │ └── Basic bit operations that are available as jets for `u8`–`u64` but are missing for `u256`. +│ ├── comparison.simf +│ │ └── Basic comparison operations that are available as jets for `u8`–`u64` but are missing for `u256`. +│ ├── convert.simf +│ │ └── Conversions between `u256` and other uint types. +│ └── math.simf +│ └── Overflow-checked arithmetic operations. ├── asserts.simf │ └── Generic assertion helpers with equality checks between uint values. ├── binary.simf │ └── Basic binary logic operations: `and`, `or`, `not`, `xor`. ├── op_return.simf -│ └── Utilities for detecting and enforcing `OP_RETURN` (null data) outputs. -├── u8.simf -├── u16.simf -├── u32.simf -├── u64.simf -└── u128.simf - ├── Overflow-checked arithmetic operations. - ├── Comparison helpers. - └── Basic operations that are available as jets for `u8`-`u64` but are missing for `u128`. + └── Utilities for detecting and enforcing `OP_RETURN` (null data) outputs. ``` ## Installation diff --git a/simf/lib/u256/bit.simf b/simf/lib/u256/bit.simf new file mode 100644 index 0000000..2df63a7 --- /dev/null +++ b/simf/lib/u256/bit.simf @@ -0,0 +1,71 @@ +use crate::lib::u128::bit::{and_128, or_128, left_shift_128, right_shift_128}; + +/// 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))) +} + +/// 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))) + } + } + } + } +} diff --git a/simf/lib/u256/comparison.simf b/simf/lib/u256/comparison.simf new file mode 100644 index 0000000..9a5fbe0 --- /dev/null +++ b/simf/lib/u256/comparison.simf @@ -0,0 +1,50 @@ +use crate::lib::binary::and; +use crate::lib::u128::bit::eq_128; +use crate::lib::u128::comparison::{is_zero_128, lt_128, le_128}; + +/// Comparison operations + +/// 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) { + 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 { + 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) { + true => true, + false => match eq_128(a_high, b_high) { + true => le_128(a_low, b_low), + false => false, + }, + } +} + +/// 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) +} diff --git a/simf/lib/u256/convert.simf b/simf/lib/u256/convert.simf new file mode 100644 index 0000000..0e12ce1 --- /dev/null +++ b/simf/lib/u256/convert.simf @@ -0,0 +1,138 @@ +use crate::lib::u1::convert::u1_to_u256; +use crate::lib::u8::convert::u8_to_u256; +use crate::lib::u16::convert::u16_to_u256; +use crate::lib::u32::convert::u32_to_u256; +use crate::lib::u64::convert::u64_to_u256; +use crate::lib::u256::comparison::le_256; + +/// Splitting uint conversions + +/// Splits u256 into thirty two u8 +pub fn split_u256_into_u8( + a: u256 +) -> ( + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8 +) { + ::into(a) +} + +/// Splits u256 into sixteen u16 +pub fn split_u256_into_u16( + a: u256 +) -> (u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16) { + ::into(a) +} + +/// Splits u256 into eight u32 +pub fn split_u256_into_u32(a: u256) -> (u32, u32, u32, u32, u32, u32, u32, u32) { + ::into(a) +} + +/// Splits u256 into four u64 +pub fn split_u256_into_u64(a: u256) -> (u64, u64, u64, u64) { + ::into(a) +} + +/// Splits u256 into two u128 +pub fn split_u256_into_u128(a: u256) -> (u128, u128) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u256 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u256_to_u1(a: u256) -> u1 { + let u1_max: u256 = u1_to_u256(jet::high_1()); + let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a); + + assert!(le_256(a, u1_max)); + + jet::rightmost_64_1(a_64) +} + +/// Converts u256 into u8. +/// Panics if the value does not fit in u8 +pub fn safe_u256_to_u8(a: u256) -> u8 { + let u8_max: u256 = u8_to_u256(jet::high_8()); + let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a); + + assert!(le_256(a, u8_max)); + + jet::rightmost_64_8(a_64) +} + +/// Converts u256 into u16. +/// Panics if the value does not fit in u16 +pub fn safe_u256_to_u16(a: u256) -> u16 { + let u16_max: u256 = u16_to_u256(jet::high_16()); + let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a); + + assert!(le_256(a, u16_max)); + + jet::rightmost_64_16(a_64) +} + +/// Converts u256 into u32. +/// Panics if the value does not fit in u32 +pub fn safe_u256_to_u32(a: u256) -> u32 { + let u32_max: u256 = u32_to_u256(jet::high_32()); + let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a); + + assert!(le_256(a, u32_max)); + + jet::rightmost_64_32(a_64) +} + +/// Converts u256 into u64. +/// Panics if the value does not fit in u64 +pub fn safe_u256_to_u64(a: u256) -> u64 { + let u64_max: u256 = u64_to_u256(jet::high_64()); + let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a); + + assert!(le_256(a, u64_max)); + + a_64 +} + +/// Converts u256 into u128. +/// Panics if the value does not fit in u128 +pub fn safe_u256_to_u128(a: u256) -> u128 { + let u128_max: u256 = 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + let (_, a_128): (u128, u128) = split_u256_into_u128(a); + + assert!(le_256(a, u128_max)); + + a_128 +} diff --git a/simf/lib/u256.simf b/simf/lib/u256/math.simf similarity index 79% rename from simf/lib/u256.simf rename to simf/lib/u256/math.simf index 95c8d79..12a37f7 100644 --- a/simf/lib/u256.simf +++ b/simf/lib/u256/math.simf @@ -12,131 +12,14 @@ use crate::lib::u128::math::{ div_mod_128, div_mod_128_64 }; -use crate::lib::u128::bit::{and_128, or_128, eq_128, left_shift_128, right_shift_128}; -use crate::lib::u128::comparison::{is_zero_128, lt_128, le_128}; +use crate::lib::u128::bit::eq_128; +use crate::lib::u128::comparison::{is_zero_128, lt_128}; -/// 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))) -} - -/// 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))) - } - } - } - } -} +use crate::lib::u256::convert::split_u256_into_u64; +use crate::lib::u256::comparison::{is_zero_256, lt_256}; /// 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) { - 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 { - 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) { - true => true, - false => match eq_128(a_high, b_high) { - true => le_128(a_low, b_low), - false => false, - }, - } -} - -/// 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) { - ::into(a) -} - /// 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); @@ -364,7 +247,7 @@ fn normalize_to_threshold_256_127(a: u256, b: u256) -> (u128, u256, u256) { /// 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_u256_into_u64(dividend); // calculate the upper part of the quotient let (q3, remainder): (u64, u64) = jet::div_mod_64(u3, divisor); @@ -377,7 +260,7 @@ pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { let (_, norm_dividend_low, norm_divisor, norm): (u64, u256, u128, u64) = normalize_to_threshold_256_63(dividend, divisor, false); - 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_u256_into_u64(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); @@ -399,7 +282,7 @@ fn mul_and_sub(q: u64, u2: u64, u1: u64, u0: u64, v: u128) -> (u64, u64) { let q_v: u256 = mul_128_64(v, q); 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_u256_into_u64(u_updated); (u1, u0) } @@ -413,7 +296,7 @@ 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 (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_u256_into_u64(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_convert_test.simf b/simf/u256_convert_test.simf new file mode 100644 index 0000000..fe659dd --- /dev/null +++ b/simf/u256_convert_test.simf @@ -0,0 +1,406 @@ +use crate::lib::u256::convert::{ + split_u256_into_u8, + split_u256_into_u16, + split_u256_into_u32, + split_u256_into_u64, + split_u256_into_u128, + safe_u256_to_u1, + safe_u256_to_u8, + safe_u256_to_u16, + safe_u256_to_u32, + safe_u256_to_u64, + safe_u256_to_u128 +}; +use crate::lib::asserts::{ + assert_eq_1, + assert_eq_8, + assert_eq_16, + assert_eq_32, + assert_eq_64, + assert_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 expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let ( + a31, + a30, + a29, + a28, + a27, + a26, + a25, + a24, + a23, + a22, + a21, + a20, + a19, + a18, + a17, + a16, + a15, + a14, + a13, + a12, + a11, + a10, + a9, + a8, + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): ( + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8 + ) = split_u256_into_u8(a); + + let ( + expected3, + expected2, + expected1, + expected0 + ): (u64, u64, u64, u64) = ::into(expected); + + let ( + expected31, + expected30, + expected29, + expected28, + expected27, + expected26, + expected25, + expected24 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = ::into(expected3); + + let ( + expected23, + expected22, + expected21, + expected20, + expected19, + expected18, + expected17, + expected16 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = ::into(expected2); + + let ( + expected15, + expected14, + expected13, + expected12, + expected11, + expected10, + expected9, + expected8 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = ::into(expected1); + + let ( + expected7, + expected6, + expected5, + expected4, + expected3, + expected2, + expected1, + expected0 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = ::into(expected0); + + assert_eq_8(a31, expected31); + assert_eq_8(a30, expected30); + assert_eq_8(a29, expected29); + assert_eq_8(a28, expected28); + assert_eq_8(a27, expected27); + assert_eq_8(a26, expected26); + assert_eq_8(a25, expected25); + assert_eq_8(a24, expected24); + assert_eq_8(a23, expected23); + assert_eq_8(a22, expected22); + assert_eq_8(a21, expected21); + assert_eq_8(a20, expected20); + assert_eq_8(a19, expected19); + assert_eq_8(a18, expected18); + assert_eq_8(a17, expected17); + assert_eq_8(a16, expected16); + assert_eq_8(a15, expected15); + assert_eq_8(a14, expected14); + assert_eq_8(a13, expected13); + assert_eq_8(a12, expected12); + assert_eq_8(a11, expected11); + assert_eq_8(a10, expected10); + assert_eq_8(a9, expected9); + assert_eq_8(a8, expected8); + assert_eq_8(a7, expected7); + assert_eq_8(a6, expected6); + assert_eq_8(a5, expected5); + assert_eq_8(a4, expected4); + assert_eq_8(a3, expected3); + assert_eq_8(a2, expected2); + assert_eq_8(a1, expected1); + assert_eq_8(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + let ( + a15, + a14, + a13, + a12, + a11, + a10, + a9, + a8, + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): ( + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16 + ) = split_u256_into_u16(a); + + let ( + expected3, + expected2, + expected1, + expected0 + ): (u64, u64, u64, u64) = ::into(expected); + + let ( + expected15, + expected14, + expected13, + expected12 + ): (u16, u16, u16, u16) = ::into(expected3); + let ( + expected11, + expected10, + expected9, + expected8 + ): (u16, u16, u16, u16) = ::into(expected2); + let ( + expected7, + expected6, + expected5, + expected4 + ): (u16, u16, u16, u16) = ::into(expected1); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u16, u16, u16, u16) = ::into(expected0); + + assert_eq_16(a15, expected15); + assert_eq_16(a14, expected14); + assert_eq_16(a13, expected13); + assert_eq_16(a12, expected12); + assert_eq_16(a11, expected11); + assert_eq_16(a10, expected10); + assert_eq_16(a9, expected9); + assert_eq_16(a8, expected8); + assert_eq_16(a7, expected7); + assert_eq_16(a6, expected6); + assert_eq_16(a5, expected5); + assert_eq_16(a4, expected4); + assert_eq_16(a3, expected3); + assert_eq_16(a2, expected2); + assert_eq_16(a1, expected1); + assert_eq_16(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + let ( + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): (u32, u32, u32, u32, u32, u32, u32, u32) = split_u256_into_u32(a); + + let (expected1, expected0): (u128, u128) = ::into(expected); + + let ( + expected7, + expected6, + expected5, + expected4 + ): (u32, u32, u32, u32) = ::into(expected1); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u32, u32, u32, u32) = ::into(expected0); + + assert_eq_32(a7, expected7); + assert_eq_32(a6, expected6); + assert_eq_32(a5, expected5); + assert_eq_32(a4, expected4); + assert_eq_32(a3, expected3); + assert_eq_32(a2, expected2); + assert_eq_32(a1, expected1); + assert_eq_32(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (a3, a2, a1, a0): (u64, u64, u64, u64) = split_u256_into_u64(a); + + let ( + expected3, + expected2, + expected1, + expected0 + ): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(a3, expected3); + assert_eq_64(a2, expected2); + assert_eq_64(a1, expected1); + assert_eq_64(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (a1, a0): (u128, u128) = split_u256_into_u128(a); + + let (expected1, expected0): (u128, u128) = ::into(expected); + + assert_eq_128(a1, expected1); + assert_eq_128(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u256_to_u1(a), expected); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + + assert_eq_8(safe_u256_to_u8(a), expected); + }, + false => {}, + }; + + match if_test_this_function(7, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u16 = jet::rightmost_64_16(expected); + + assert_eq_16(safe_u256_to_u16(a), expected); + }, + false => {}, + }; + + match if_test_this_function(8, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u32 = jet::rightmost_64_32(expected); + + assert_eq_32(safe_u256_to_u32(a), expected); + }, + false => {}, + }; + + match if_test_this_function(9, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(safe_u256_to_u64(a), expected); + }, + false => {}, + }; + + match if_test_this_function(10, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert_eq_128(safe_u256_to_u128(a), expected); + }, + false => {}, + }; +} diff --git a/simf/u256_test.simf b/simf/u256_math_test.simf similarity index 96% rename from simf/u256_test.simf rename to simf/u256_math_test.simf index 3cb186d..9bc8930 100644 --- a/simf/u256_test.simf +++ b/simf/u256_math_test.simf @@ -1,4 +1,4 @@ -use crate::lib::u256::{ +use crate::lib::u256::math::{ checked_add_256, safe_add_256, checked_sub_256, @@ -6,10 +6,9 @@ use crate::lib::u256::{ checked_mul_256, safe_mul_256, checked_div_256, - safe_div_256, - gt_256, - ge_256 + safe_div_256 }; +use crate::lib::u256::comparison::{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; diff --git a/simf/u256_test_split_add.simf b/simf/u256_test_add.simf similarity index 53% rename from simf/u256_test_split_add.simf rename to simf/u256_test_add.simf index da49048..1c06f46 100644 --- a/simf/u256_test_split_add.simf +++ b/simf/u256_test_add.simf @@ -1,4 +1,4 @@ -use crate::lib::u256::{split_256_into_64, add_256, add_256_128}; +use crate::lib::u256::math::{add_256, add_256_128}; use crate::lib::asserts::assert_eq_256; use crate::helper::{if_test_this_function, assert_bool}; @@ -22,23 +22,6 @@ fn main() { /// Arithmetic - match if_test_this_function(0, 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(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(0, fn_idx) { true => { assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(1, 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_bits.simf b/simf/u256_test_bits.simf index 4739fcd..797c0e8 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, left_shift_256, right_shift_256, split_256_into_64}; +use crate::lib::u256::bit::{and_256, or_256, left_shift_256, right_shift_256}; +use crate::lib::u256::convert::split_u256_into_u64; use crate::lib::asserts::assert_eq_256; use crate::helper::if_test_this_function; @@ -16,7 +17,7 @@ fn main() { match if_test_this_function(2, fn_idx) { true => { - let (_, _, _, a): (u64, u64, u64, u64) = split_256_into_64(a); + let (_, _, _, a): (u64, u64, u64, u64) = split_u256_into_u64(a); let shift: u8 = jet::rightmost_64_8(a); assert_eq_256(left_shift_256(shift, b), unwrap(expected)); @@ -25,7 +26,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_u256_into_u64(a); let shift: u8 = jet::rightmost_64_8(a); assert_eq_256(right_shift_256(shift, b), unwrap(expected)); diff --git a/simf/u256_test_compare.simf b/simf/u256_test_compare.simf index 7deedb5..5192369 100644 --- a/simf/u256_test_compare.simf +++ b/simf/u256_test_compare.simf @@ -1,4 +1,4 @@ -use crate::lib::u256::{is_zero_256, lt_256, le_256}; +use crate::lib::u256::comparison::{is_zero_256, lt_256, le_256}; use crate::helper::{if_test_this_function, assert_bool}; fn main() { @@ -8,7 +8,7 @@ fn main() { let b: u256 = witness::SECOND_ARG; let expected_bool: bool = witness::EXPECTED_BOOL; - /// Arithmetic + /// Comparison operations 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 => (), }; diff --git a/simf/u256_test_div.simf b/simf/u256_test_div.simf index 50576b1..7c895b2 100644 --- a/simf/u256_test_div.simf +++ b/simf/u256_test_div.simf @@ -1,4 +1,5 @@ -use crate::lib::u256::{div_mod_256_64, div_mod_256_128, div_mod_256, div_256, split_256_into_64}; +use crate::lib::u256::math::{div_mod_256_64, div_mod_256_128, div_mod_256, div_256}; +use crate::lib::u256::convert::split_u256_into_u64; use crate::lib::asserts::{assert_eq_128, assert_eq_256}; use crate::helper::if_test_this_function; @@ -15,8 +16,8 @@ fn main() { 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 (_, _, _, b): (u64, u64, u64, u64) = split_u256_into_u64(b); + let (_, _, _, expected_r): (u64, u64, u64, u64) = split_u256_into_u64(second_expected); let (q, r): (u256, u64) = div_mod_256_64(a, b); diff --git a/simf/u256_test_sub_mul.simf b/simf/u256_test_sub_mul.simf index 2ca3442..aa6b673 100644 --- a/simf/u256_test_sub_mul.simf +++ b/simf/u256_test_sub_mul.simf @@ -1,4 +1,5 @@ -use crate::lib::u256::{sub_256, mul_256, mul_256_64, mul_256_128, split_256_into_64}; +use crate::lib::u256::math::{sub_256, mul_256, mul_256_64, mul_256_128}; +use crate::lib::u256::convert::split_u256_into_u64; use crate::lib::asserts::{assert_eq_64, assert_eq_128, assert_eq_256}; use crate::helper::{if_test_this_function, assert_bool}; @@ -38,8 +39,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): (u64, u64, u64, u64) = split_256_into_64(unwrap(expected)); + let (_, _, _, b): (u64, u64, u64, u64) = split_u256_into_u64(b); + let (_, _, _, expected): (u64, u64, u64, u64) = split_u256_into_u64(unwrap(expected)); let (result_high, result_low): (u64, u256) = mul_256_64(a, b); diff --git a/tests/u256_convert_test.rs b/tests/u256_convert_test.rs new file mode 100644 index 0000000..2f31be6 --- /dev/null +++ b/tests/u256_convert_test.rs @@ -0,0 +1,331 @@ +mod common; + +use primitive_types::U256; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_convert_test::U256ConvertTestProgram; +use simplicityhl_std::artifacts::u256_convert_test::derived_u256_convert_test::{ + U256ConvertTestArguments, U256ConvertTestWitness, +}; + +enum FunctionToTest { + SplitU256IntoU8, + SplitU256IntoU16, + SplitU256IntoU32, + SplitU256IntoU64, + SplitU256IntoU128, + SafeU256ToU1, + SafeU256ToU8, + SafeU256ToU16, + SafeU256ToU32, + SafeU256ToU64, + SafeU256ToU128, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U256ConvertTestProgram { + U256ConvertTestProgram::new(U256ConvertTestArguments {}) +} + +fn build_witness(function: u8, a: [u8; 32], expected: [u8; 32]) -> U256ConvertTestWitness { + U256ConvertTestWitness { + function_index: function, + first_arg: a, + expected, + } +} + +mod u256_convert_test { + use crate::common::helper::generate_u256; + + use super::*; + + #[simplex::test] + fn u256_convert_test_u256_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU256IntoU8), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_u256_into_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU256IntoU16), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_u256_into_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU256IntoU32), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_u256_into_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU256IntoU64), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_u256_into_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU256IntoU128), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::one()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU1), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u1_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(2), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU1), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u8::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU8), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u8_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u8::MAX) + 1, U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU8), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u16::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU16), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u16_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u16::MAX) + 1, U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU16), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u32::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU32), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u32_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u32::MAX) + 1, U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU32), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u64::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU64), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u64_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u64::MAX) + 1, U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU64), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u128::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU128), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u128_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU128), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u256_test.rs b/tests/u256_math_test.rs similarity index 68% rename from tests/u256_test.rs rename to tests/u256_math_test.rs index d15c05d..2d407be 100644 --- a/tests/u256_test.rs +++ b/tests/u256_math_test.rs @@ -5,15 +5,15 @@ 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, +use simplicityhl_std::artifacts::u256_math_test::U256MathTestProgram; +use simplicityhl_std::artifacts::u256_math_test::derived_u256_math_test::{ + U256MathTestArguments, U256MathTestWitness, }; // The only per-width code for the common operations. impl TestUint for U256Wrapper { - type Program = U256TestProgram; - type Witness = U256TestWitness; + type Program = U256MathTestProgram; + type Witness = U256MathTestWitness; const ZERO: U256Wrapper = U256Wrapper(U256::zero()); const ONE: U256Wrapper = U256Wrapper(U256::one()); @@ -21,8 +21,8 @@ impl TestUint for U256Wrapper { 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 program() -> U256MathTestProgram { + U256MathTestProgram::new(U256MathTestArguments {}) } fn witness( @@ -30,8 +30,8 @@ impl TestUint for U256Wrapper { a: U256Wrapper, b: U256Wrapper, expected: Option, - ) -> U256TestWitness { - U256TestWitness { + ) -> U256MathTestWitness { + U256MathTestWitness { function_index: op, first_arg: a.to_big_endian(), second_arg: b.to_big_endian(), @@ -40,7 +40,7 @@ impl TestUint for U256Wrapper { } } -mod u256_tests { +mod u256_math_tests { use super::*; // Stamps the 22 `#[simplex::test]` entry points for U256Wrapper. Logic lives in common::uint. diff --git a/tests/u256_test_split_add.rs b/tests/u256_test_add.rs similarity index 73% rename from tests/u256_test_split_add.rs rename to tests/u256_test_add.rs index 18898c9..f7792f7 100644 --- a/tests/u256_test_split_add.rs +++ b/tests/u256_test_add.rs @@ -2,16 +2,15 @@ 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_split_add::U256TestSplitAddProgram; -use simplicityhl_std::artifacts::u256_test_split_add::derived_u256_test_split_add::{ - U256TestSplitAddArguments, U256TestSplitAddWitness, +use simplicityhl_std::artifacts::u256_test_add::U256TestAddProgram; +use simplicityhl_std::artifacts::u256_test_add::derived_u256_test_add::{ + U256TestAddArguments, U256TestAddWitness, }; enum FunctionToTest { - Split256Into64, Add256, Add256_128, } @@ -21,10 +20,8 @@ fn op(o: FunctionToTest) -> u8 { o as u8 } -const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; - -fn program() -> U256TestSplitAddProgram { - U256TestSplitAddProgram::new(U256TestSplitAddArguments {}) +fn program() -> U256TestAddProgram { + U256TestAddProgram::new(U256TestAddArguments {}) } fn build_witness( @@ -33,8 +30,8 @@ fn build_witness( b: [u8; 32], expected: Option<[u8; 32]>, expected_bool: bool, -) -> U256TestSplitAddWitness { - U256TestSplitAddWitness { +) -> U256TestAddWitness { + U256TestAddWitness { function_index: function, first_arg: a, second_arg: b, @@ -46,24 +43,6 @@ fn build_witness( mod u256_tests_arithmetic { use super::*; - #[simplex::test] - fn u256_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, - ), - 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); diff --git a/tests/u256_test_compare.rs b/tests/u256_test_compare.rs index 0ed9e83..eea807d 100644 --- a/tests/u256_test_compare.rs +++ b/tests/u256_test_compare.rs @@ -41,7 +41,7 @@ fn build_witness( } } -mod u256_tests_arithmetic { +mod u256_tests_compare { use super::*; #[simplex::test] From 72d4827508d371c7e555f9b3d61be2b165db2fb8 Mon Sep 17 00:00:00 2001 From: Oleh Komendant Date: Wed, 19 Aug 2026 12:46:20 +0300 Subject: [PATCH 10/11] Update README --- README.md | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9c489e8..1f1bfe7 100644 --- a/README.md +++ b/README.md @@ -10,25 +10,34 @@ The standard library implementation for [SimplicityHL](https://github.com/Blocks ```md simf/lib ├── secp256k1 -│ └── operations.simf +│ └── operations.simf │ ├── Conversions between `Ge`, `Gej`, and compressed `Point`. │ ├── Subtraction for `Fe`, `Scalar`, `Gej`. │ ├── Equality predicates and their `assert_*` counterparts. │ └── Safe Jacobian-to-affine normalization. ├── u1 │ └── convert.simf +│ └── Conversions from `u1` to other uint types and `bool`. ├── u8 │ ├── convert.simf +│ │ └── Conversions between `u8` and other uint types. │ └── math.simf +│ └── Overflow-checked arithmetic and `gt` / `ge`. ├── u16 │ ├── convert.simf +│ │ └── Conversions between `u16` and other uint types. │ └── math.simf +│ └── Overflow-checked arithmetic and `gt` / `ge`. ├── u32 │ ├── convert.simf +│ │ └── Conversions between `u32` and other uint types. │ └── math.simf +│ └── Overflow-checked arithmetic and `gt` / `ge`. ├── u64 │ ├── convert.simf +│ │ └── Conversions between `u64` and other uint types. │ └── math.simf +│ └── Overflow-checked arithmetic and `gt` / `ge`. ├── u128 │ ├── bit.simf │ │ └── Basic bit operations that are available as jets for `u8`–`u64` but are missing for `u128`. @@ -37,7 +46,7 @@ simf/lib │ ├── convert.simf │ │ └── Conversions between `u128` and other uint types. │ └── math.simf -│ └── Overflow-checked arithmetic operations. +│ └── Carry/borrow arithmetic, multiplication, division, and overflow-checked wrappers. ├── u256 │ ├── bit.simf │ │ └── Basic bit operations that are available as jets for `u8`–`u64` but are missing for `u256`. @@ -46,9 +55,9 @@ simf/lib │ ├── convert.simf │ │ └── Conversions between `u256` and other uint types. │ └── math.simf -│ └── Overflow-checked arithmetic operations. +│ └── Carry/borrow arithmetic, multiplication, division, and overflow-checked wrappers. ├── asserts.simf -│ └── Generic assertion helpers with equality checks between uint values. +│ └── Assertion helpers: `assert_eq_*` for uints and `bool`, plus `assert_none_*` for `Option`. ├── binary.simf │ └── Basic binary logic operations: `and`, `or`, `not`, `xor`. ├── op_return.simf @@ -73,6 +82,19 @@ simplex install https://github.com/BlockstreamResearch/simplicityhl-std > [!NOTE] > The library works with Simplex version 0.0.7 or higher. +## Usage + +After installing the library, import modules from `std::lib`: + +```simf +use std::lib::u32::math::checked_add_32; +use std::lib::asserts::assert_eq_32; + +fn main() { + assert_eq_32(unwrap(checked_add_32(1, 2)), 3); +} +``` + ## Contributing We are open to any mind-blowing ideas! Please take a look at our [contributing guidelines](CONTRIBUTING.md) to get involved. From 5722270f4c5c510e4b901a1b8dd1cfbdcac6c071 Mon Sep 17 00:00:00 2001 From: Oleh Komendant <44612825+Hrom131@users.noreply.github.com> Date: Wed, 19 Aug 2026 22:43:28 +0300 Subject: [PATCH 11/11] Bump Simplex version (#49) --- .github/workflows/tests.yml | 2 ++ Cargo.lock | 19 +++++++------------ Cargo.toml | 2 +- tests/asserts_test.rs | 2 +- tests/binary_test.rs | 2 +- tests/op_return_test.rs | 2 +- tests/secp256k1_operations_test.rs | 2 +- tests/u128_basic_math_test.rs | 2 +- tests/u128_convert_test.rs | 2 +- tests/u128_math_test.rs | 2 +- tests/u128_test_bits.rs | 2 +- tests/u128_test_compare.rs | 2 +- tests/u16_convert_test.rs | 2 +- tests/u16_math_test.rs | 2 +- tests/u1_convert_test.rs | 2 +- tests/u256_convert_test.rs | 2 +- tests/u256_math_test.rs | 2 +- tests/u256_test_add.rs | 2 +- tests/u256_test_bits.rs | 2 +- tests/u256_test_compare.rs | 2 +- tests/u256_test_div.rs | 2 +- tests/u256_test_sub_mul.rs | 2 +- tests/u32_convert_test.rs | 2 +- tests/u32_math_test.rs | 2 +- tests/u64_convert_test.rs | 2 +- tests/u64_math_test.rs | 2 +- tests/u8_convert_test.rs | 2 +- tests/u8_math_tests.rs | 2 +- 28 files changed, 35 insertions(+), 38 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 209790a..87b55d5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -46,6 +46,8 @@ jobs: - name: Install simplex-cli uses: ./.github/actions/setup-smplx + with: + commit-sha: "c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" - name: Generate contract artifacts shell: bash diff --git a/Cargo.lock b/Cargo.lock index 4ccea75..e8bf726 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1492,8 +1492,7 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smplx-build" version = "0.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9f3e2235cc1bb20a5dda4bd32c9be665f39c95f1bcc62d7aa2a9adfc8fb670f" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "glob", "globwalk", @@ -1502,6 +1501,7 @@ dependencies = [ "proc-macro2", "quote", "serde", + "serde_json", "simplicityhl", "syn 2.0.119", "thiserror", @@ -1512,8 +1512,7 @@ dependencies = [ [[package]] name = "smplx-macros" version = "0.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e1a764666ba1aada361499286710a0d5d7f1a0251d882a0843bb67ae5b7973" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "smplx-build", "smplx-test", @@ -1523,8 +1522,7 @@ dependencies = [ [[package]] name = "smplx-regtest" version = "0.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60a5ca5ccc0b531f4915f1d044c68b7ba3bc344e84ac019f539b2e488c8c8d7" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "electrsd", "hex", @@ -1539,8 +1537,7 @@ dependencies = [ [[package]] name = "smplx-sdk" version = "0.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "940acb2e942bda24f3f27c3a387aabcf866dc16299746e79a58b47d091162f5d" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "bip39", "bitcoin_hashes", @@ -1559,8 +1556,7 @@ dependencies = [ [[package]] name = "smplx-std" version = "0.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcfa462d2e480d1fa6baa382d578501991fd19c3fb15ae5cdd44be362ef5e9ef" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "either", "serde", @@ -1573,8 +1569,7 @@ dependencies = [ [[package]] name = "smplx-test" version = "0.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76eab48bbf610aea049644b6aacffebbb194e59c663cd450b1f30a29ead19c1" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "electrsd", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 25078a7..bb8698d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["simplicity", "liquid", "elements", "smart-contracts"] categories = ["cryptography::cryptocurrencies"] [dependencies] -smplx-std = "0.0.9" +smplx-std = { git = "https://github.com/BlockstreamResearch/smplx.git", rev = "c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" } [dev-dependencies] anyhow = { version = "1.0.101" } diff --git a/tests/asserts_test.rs b/tests/asserts_test.rs index e128ce9..94fcee0 100644 --- a/tests/asserts_test.rs +++ b/tests/asserts_test.rs @@ -38,7 +38,7 @@ const DEFAULT_SOME_U128: Option = Some(0); const DEFAULT_SOME_U256: Option<[u8; 32]> = Some([0; 32]); fn program() -> AssertsTestProgram { - AssertsTestProgram::new(AssertsTestArguments {}) + AssertsTestProgram::new(&AssertsTestArguments {}) } /// Returns two values in `[min, max]` that are equal when `same`, distinct otherwise. diff --git a/tests/binary_test.rs b/tests/binary_test.rs index 7bc6f50..2da001e 100644 --- a/tests/binary_test.rs +++ b/tests/binary_test.rs @@ -12,7 +12,7 @@ mod binary_tests { #[simplex::test] fn binary_test(context: simplex::TestContext) -> anyhow::Result<()> { - let program = BinaryTestProgram::new(BinaryTestArguments {}); + let program = BinaryTestProgram::new(&BinaryTestArguments {}); run(&context, program, BinaryTestWitness {}, Expect::Ok) } } diff --git a/tests/op_return_test.rs b/tests/op_return_test.rs index e61268e..4eb2fe1 100644 --- a/tests/op_return_test.rs +++ b/tests/op_return_test.rs @@ -24,7 +24,7 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_DATA: &[u8; 1] = &[1]; fn program() -> OpReturnTestProgram { - OpReturnTestProgram::new(OpReturnTestArguments {}) + OpReturnTestProgram::new(&OpReturnTestArguments {}) } fn build_witness(function: u8, index: u32, expected: bool) -> OpReturnTestWitness { diff --git a/tests/secp256k1_operations_test.rs b/tests/secp256k1_operations_test.rs index 08b5b87..a834f63 100644 --- a/tests/secp256k1_operations_test.rs +++ b/tests/secp256k1_operations_test.rs @@ -48,7 +48,7 @@ const SECP_N: [u8; 32] = [ ]; fn program() -> Secp256k1OperationsTestProgram { - Secp256k1OperationsTestProgram::new(Secp256k1OperationsTestArguments {}) + Secp256k1OperationsTestProgram::new(&Secp256k1OperationsTestArguments {}) } #[allow(clippy::too_many_arguments)] diff --git a/tests/u128_basic_math_test.rs b/tests/u128_basic_math_test.rs index 133f2bc..77dd7b3 100644 --- a/tests/u128_basic_math_test.rs +++ b/tests/u128_basic_math_test.rs @@ -34,7 +34,7 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: u128 = 0; fn program() -> U128BasicMathTestProgram { - U128BasicMathTestProgram::new(U128BasicMathTestArguments {}) + U128BasicMathTestProgram::new(&U128BasicMathTestArguments {}) } fn build_witness( diff --git a/tests/u128_convert_test.rs b/tests/u128_convert_test.rs index bf42042..e2d0a22 100644 --- a/tests/u128_convert_test.rs +++ b/tests/u128_convert_test.rs @@ -29,7 +29,7 @@ fn op(o: FunctionToTest) -> u8 { } fn program() -> U128ConvertTestProgram { - U128ConvertTestProgram::new(U128ConvertTestArguments {}) + U128ConvertTestProgram::new(&U128ConvertTestArguments {}) } fn build_witness(function: u8, a: u128, expected: [u8; 32]) -> U128ConvertTestWitness { diff --git a/tests/u128_math_test.rs b/tests/u128_math_test.rs index a7ea30f..06e2d98 100644 --- a/tests/u128_math_test.rs +++ b/tests/u128_math_test.rs @@ -19,7 +19,7 @@ impl TestUint for u128 { const MUL_BOUND: u128 = 1 << 64; // 2^(128/2) fn program() -> U128MathTestProgram { - U128MathTestProgram::new(U128MathTestArguments {}) + U128MathTestProgram::new(&U128MathTestArguments {}) } fn witness(op: u8, a: u128, b: u128, expected: Option) -> U128MathTestWitness { diff --git a/tests/u128_test_bits.rs b/tests/u128_test_bits.rs index 5d04898..508d5f7 100644 --- a/tests/u128_test_bits.rs +++ b/tests/u128_test_bits.rs @@ -26,7 +26,7 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: u128 = 0; fn program() -> U128TestBitsProgram { - U128TestBitsProgram::new(U128TestBitsArguments {}) + U128TestBitsProgram::new(&U128TestBitsArguments {}) } fn build_witness( diff --git a/tests/u128_test_compare.rs b/tests/u128_test_compare.rs index 24e1187..1c7a01e 100644 --- a/tests/u128_test_compare.rs +++ b/tests/u128_test_compare.rs @@ -23,7 +23,7 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: u128 = 0; fn program() -> U128TestCompareProgram { - U128TestCompareProgram::new(U128TestCompareArguments {}) + U128TestCompareProgram::new(&U128TestCompareArguments {}) } fn build_witness(function: u8, a: u128, b: u128, expected_bool: bool) -> U128TestCompareWitness { diff --git a/tests/u16_convert_test.rs b/tests/u16_convert_test.rs index 56c6a0a..65df71a 100644 --- a/tests/u16_convert_test.rs +++ b/tests/u16_convert_test.rs @@ -26,7 +26,7 @@ fn op(o: FunctionToTest) -> u8 { } fn program() -> U16ConvertTestProgram { - U16ConvertTestProgram::new(U16ConvertTestArguments {}) + U16ConvertTestProgram::new(&U16ConvertTestArguments {}) } fn build_witness(function: u8, a: u16, expected: [u8; 32]) -> U16ConvertTestWitness { diff --git a/tests/u16_math_test.rs b/tests/u16_math_test.rs index 88d3ab8..07b4c00 100644 --- a/tests/u16_math_test.rs +++ b/tests/u16_math_test.rs @@ -19,7 +19,7 @@ impl TestUint for u16 { const MUL_BOUND: u16 = 1 << 8; // 2^(16/2) fn program() -> U16MathTestProgram { - U16MathTestProgram::new(U16MathTestArguments {}) + U16MathTestProgram::new(&U16MathTestArguments {}) } fn witness(op: u8, a: u16, b: u16, expected: Option) -> U16MathTestWitness { diff --git a/tests/u1_convert_test.rs b/tests/u1_convert_test.rs index 230acbf..26c743c 100644 --- a/tests/u1_convert_test.rs +++ b/tests/u1_convert_test.rs @@ -26,7 +26,7 @@ fn op(o: FunctionToTest) -> u8 { } fn program() -> U1ConvertTestProgram { - U1ConvertTestProgram::new(U1ConvertTestArguments {}) + U1ConvertTestProgram::new(&U1ConvertTestArguments {}) } fn build_witness(function: u8, a: u8, expected: [u8; 32]) -> U1ConvertTestWitness { diff --git a/tests/u256_convert_test.rs b/tests/u256_convert_test.rs index 2f31be6..923c40a 100644 --- a/tests/u256_convert_test.rs +++ b/tests/u256_convert_test.rs @@ -29,7 +29,7 @@ fn op(o: FunctionToTest) -> u8 { } fn program() -> U256ConvertTestProgram { - U256ConvertTestProgram::new(U256ConvertTestArguments {}) + U256ConvertTestProgram::new(&U256ConvertTestArguments {}) } fn build_witness(function: u8, a: [u8; 32], expected: [u8; 32]) -> U256ConvertTestWitness { diff --git a/tests/u256_math_test.rs b/tests/u256_math_test.rs index 2d407be..67ebc4a 100644 --- a/tests/u256_math_test.rs +++ b/tests/u256_math_test.rs @@ -22,7 +22,7 @@ impl TestUint for U256Wrapper { const MUL_BOUND: U256Wrapper = U256Wrapper(U256([0, 0, 1, 0])); // 2^(256/2) fn program() -> U256MathTestProgram { - U256MathTestProgram::new(U256MathTestArguments {}) + U256MathTestProgram::new(&U256MathTestArguments {}) } fn witness( diff --git a/tests/u256_test_add.rs b/tests/u256_test_add.rs index f7792f7..2e5bc44 100644 --- a/tests/u256_test_add.rs +++ b/tests/u256_test_add.rs @@ -21,7 +21,7 @@ fn op(o: FunctionToTest) -> u8 { } fn program() -> U256TestAddProgram { - U256TestAddProgram::new(U256TestAddArguments {}) + U256TestAddProgram::new(&U256TestAddArguments {}) } fn build_witness( diff --git a/tests/u256_test_bits.rs b/tests/u256_test_bits.rs index ae070e1..c0c78e6 100644 --- a/tests/u256_test_bits.rs +++ b/tests/u256_test_bits.rs @@ -24,7 +24,7 @@ fn op(o: FunctionToTest) -> u8 { } fn program() -> U256TestBitsProgram { - U256TestBitsProgram::new(U256TestBitsArguments {}) + U256TestBitsProgram::new(&U256TestBitsArguments {}) } fn build_witness( diff --git a/tests/u256_test_compare.rs b/tests/u256_test_compare.rs index eea807d..560e113 100644 --- a/tests/u256_test_compare.rs +++ b/tests/u256_test_compare.rs @@ -24,7 +24,7 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; fn program() -> U256TestCompareProgram { - U256TestCompareProgram::new(U256TestCompareArguments {}) + U256TestCompareProgram::new(&U256TestCompareArguments {}) } fn build_witness( diff --git a/tests/u256_test_div.rs b/tests/u256_test_div.rs index 9e04d00..9c36ca9 100644 --- a/tests/u256_test_div.rs +++ b/tests/u256_test_div.rs @@ -25,7 +25,7 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; fn program() -> U256TestDivProgram { - U256TestDivProgram::new(U256TestDivArguments {}) + U256TestDivProgram::new(&U256TestDivArguments {}) } fn build_witness( diff --git a/tests/u256_test_sub_mul.rs b/tests/u256_test_sub_mul.rs index d6b8006..7c164a5 100644 --- a/tests/u256_test_sub_mul.rs +++ b/tests/u256_test_sub_mul.rs @@ -26,7 +26,7 @@ fn op(o: FunctionToTest) -> u8 { const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; fn program() -> U256TestSubMulProgram { - U256TestSubMulProgram::new(U256TestSubMulArguments {}) + U256TestSubMulProgram::new(&U256TestSubMulArguments {}) } fn build_witness( diff --git a/tests/u32_convert_test.rs b/tests/u32_convert_test.rs index 1ed57be..e7874f0 100644 --- a/tests/u32_convert_test.rs +++ b/tests/u32_convert_test.rs @@ -27,7 +27,7 @@ fn op(o: FunctionToTest) -> u8 { } fn program() -> U32ConvertTestProgram { - U32ConvertTestProgram::new(U32ConvertTestArguments {}) + U32ConvertTestProgram::new(&U32ConvertTestArguments {}) } fn build_witness(function: u8, a: u32, expected: [u8; 32]) -> U32ConvertTestWitness { diff --git a/tests/u32_math_test.rs b/tests/u32_math_test.rs index abaedef..2db6eda 100644 --- a/tests/u32_math_test.rs +++ b/tests/u32_math_test.rs @@ -19,7 +19,7 @@ impl TestUint for u32 { const MUL_BOUND: u32 = 1 << 16; // 2^(32/2) fn program() -> U32MathTestProgram { - U32MathTestProgram::new(U32MathTestArguments {}) + U32MathTestProgram::new(&U32MathTestArguments {}) } fn witness(op: u8, a: u32, b: u32, expected: Option) -> U32MathTestWitness { diff --git a/tests/u64_convert_test.rs b/tests/u64_convert_test.rs index 1a847f8..050faaa 100644 --- a/tests/u64_convert_test.rs +++ b/tests/u64_convert_test.rs @@ -28,7 +28,7 @@ fn op(o: FunctionToTest) -> u8 { } fn program() -> U64ConvertTestProgram { - U64ConvertTestProgram::new(U64ConvertTestArguments {}) + U64ConvertTestProgram::new(&U64ConvertTestArguments {}) } fn build_witness(function: u8, a: u64, expected: [u8; 32]) -> U64ConvertTestWitness { diff --git a/tests/u64_math_test.rs b/tests/u64_math_test.rs index 91214e6..216b4bf 100644 --- a/tests/u64_math_test.rs +++ b/tests/u64_math_test.rs @@ -19,7 +19,7 @@ impl TestUint for u64 { const MUL_BOUND: u64 = 1 << 32; // 2^(64/2) fn program() -> U64MathTestProgram { - U64MathTestProgram::new(U64MathTestArguments {}) + U64MathTestProgram::new(&U64MathTestArguments {}) } fn witness(op: u8, a: u64, b: u64, expected: Option) -> U64MathTestWitness { diff --git a/tests/u8_convert_test.rs b/tests/u8_convert_test.rs index fac8c88..c44ecd0 100644 --- a/tests/u8_convert_test.rs +++ b/tests/u8_convert_test.rs @@ -26,7 +26,7 @@ fn op(o: FunctionToTest) -> u8 { } fn program() -> U8ConvertTestProgram { - U8ConvertTestProgram::new(U8ConvertTestArguments {}) + U8ConvertTestProgram::new(&U8ConvertTestArguments {}) } fn build_witness(function: u8, a: u8, expected: [u8; 32]) -> U8ConvertTestWitness { diff --git a/tests/u8_math_tests.rs b/tests/u8_math_tests.rs index cc69367..3ff3d7b 100644 --- a/tests/u8_math_tests.rs +++ b/tests/u8_math_tests.rs @@ -19,7 +19,7 @@ impl TestUint for u8 { const MUL_BOUND: u8 = 1 << 4; // 2^(8/2) fn program() -> U8MathTestProgram { - U8MathTestProgram::new(U8MathTestArguments {}) + U8MathTestProgram::new(&U8MathTestArguments {}) } fn witness(op: u8, a: u8, b: u8, expected: Option) -> U8MathTestWitness {