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.