From d7e8755ca80504d1ac5a507f6d974bcae4e3e01b Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Fri, 11 Sep 2026 14:35:24 +0200 Subject: [PATCH 01/11] Feat/mul div (#54) * added mul_div functions with tests * added zero checks for div_128 and div_256 to be consistent with jet's `divide` * added tests for new helper functions * typo in a test name * utilized convert and split functions * added mul_div docs; moved arithmetic section for u128 and u256 to be consistent with other sections * linting * added docs for new functions * fixed typo * fixed bug in normalize_to_threshold funcs * added more tests to cover new u512 division functionality * bumped simplex version * optimization for calculate_normalizer_base_128 * typo fixes * a couple of new tests for calculate_normalizer_base_128 --- Cargo.lock | 178 ++++--- Cargo.toml | 2 +- docs/stdlib.json | 525 +++++++++++--------- simf/lib/u128/math.simf | 143 +++--- simf/lib/u128/mul_div.simf | 15 + simf/lib/u16/mul_div.simf | 12 + simf/lib/u256/math.simf | 228 +++++---- simf/lib/u256/mul_div.simf | 179 +++++++ simf/lib/u32/mul_div.simf | 12 + simf/lib/u64/mul_div.simf | 13 + simf/lib/u8/mul_div.simf | 12 + simf/u128_mul_div_test.simf | 21 + simf/u16_mul_div_test.simf | 21 + simf/u256_mul_div_test.simf | 21 + simf/u256_test_add.simf | 26 +- simf/u256_test_div.simf | 51 +- simf/u256_test_sub_mul.simf | 41 +- simf/u32_mul_div_test.simf | 21 + simf/u64_mul_div_test.simf | 21 + simf/u8_mul_div_test.simf | 21 + tests/u128_basic_math_test.rs | 6 +- tests/u128_mul_div_test.rs | 108 ++++ tests/u16_mul_div_test.rs | 100 ++++ tests/u256_mul_div_test.rs | 346 +++++++++++++ tests/u256_test_add.rs | 113 ++++- tests/u256_test_div.rs | 214 +++++++- tests/u256_test_sub_mul.rs | 105 ++++ tests/u32_mul_div_test.rs | 100 ++++ tests/u64_mul_div_test.rs | 100 ++++ tests/{u8_math_tests.rs => u8_math_test.rs} | 0 tests/u8_mul_div_test.rs | 100 ++++ 31 files changed, 2385 insertions(+), 470 deletions(-) create mode 100644 simf/lib/u128/mul_div.simf create mode 100644 simf/lib/u16/mul_div.simf create mode 100644 simf/lib/u256/mul_div.simf create mode 100644 simf/lib/u32/mul_div.simf create mode 100644 simf/lib/u64/mul_div.simf create mode 100644 simf/lib/u8/mul_div.simf create mode 100644 simf/u128_mul_div_test.simf create mode 100644 simf/u16_mul_div_test.simf create mode 100644 simf/u256_mul_div_test.simf create mode 100644 simf/u32_mul_div_test.simf create mode 100644 simf/u64_mul_div_test.simf create mode 100644 simf/u8_mul_div_test.simf create mode 100644 tests/u128_mul_div_test.rs create mode 100644 tests/u16_mul_div_test.rs create mode 100644 tests/u256_mul_div_test.rs create mode 100644 tests/u32_mul_div_test.rs create mode 100644 tests/u64_mul_div_test.rs rename tests/{u8_math_tests.rs => u8_math_test.rs} (100%) create mode 100644 tests/u8_mul_div_test.rs diff --git a/Cargo.lock b/Cargo.lock index 7001e42..7eb322e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,6 +82,16 @@ dependencies = [ "object", ] +[[package]] +name = "ariadne" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31beedec3ce83ae6da3a79592b3d8d7afd146a5b15bb9bb940279aced60faa89" +dependencies = [ + "unicode-width", + "yansi", +] + [[package]] name = "arrayvec" version = "0.7.8" @@ -96,9 +106,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.18.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" dependencies = [ "aws-lc-sys", "zeroize", @@ -106,9 +116,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.44.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" dependencies = [ "cc", "cmake", @@ -169,7 +179,7 @@ dependencies = [ "bitcoin-io", "bitcoin-units", "bitcoin_hashes", - "hex-conservative 0.2.2", + "hex-conservative 0.2.3", "hex_lit", "secp256k1", "serde", @@ -182,7 +192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" dependencies = [ "bitcoin-internals", - "hex-conservative 1.2.0", + "hex-conservative 1.3.0", "serde", ] @@ -224,7 +234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ "bitcoin-io", - "hex-conservative 0.2.2", + "hex-conservative 0.2.3", "serde", ] @@ -273,9 +283,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.1" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" [[package]] name = "bitvec" @@ -328,9 +338,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.4.3" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" +checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80" dependencies = [ "find-msvc-tools", "jobserver", @@ -432,9 +442,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" +checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -442,18 +452,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.20" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" [[package]] name = "crunchy" @@ -496,9 +506,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" dependencies = [ "serde", ] @@ -577,9 +587,9 @@ checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "find-msvc-tools" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" [[package]] name = "fixed-hash" @@ -700,7 +710,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.13.2", "ignore", "walkdir", ] @@ -730,18 +740,18 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +checksum = "db3fef046dca3ca91ee1408a8c1b80ab777e80a4d308d1bf4e7adb3fcb047e08" dependencies = [ "arrayvec", ] [[package]] name = "hex-conservative" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +checksum = "271e0d19bcb473b6675739a2b536076b24a082316cb5199ad918edce10c599e8" dependencies = [ "arrayvec", ] @@ -808,9 +818,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.0" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", "hashbrown 0.17.1", @@ -849,9 +859,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.104" +version = "0.3.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" dependencies = [ "cfg-if", "futures-util", @@ -905,9 +915,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "memchr" @@ -1153,9 +1163,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", "rand_chacha", @@ -1241,7 +1251,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.4.15", @@ -1254,7 +1264,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.12.1", @@ -1263,9 +1273,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.43" +version = "0.23.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" +checksum = "6725596c3f2c3a0aef021139e145d4eafe314a6623e4680ca83852b2c67ab2ba" dependencies = [ "aws-lc-rs", "log", @@ -1287,9 +1297,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.14" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", @@ -1356,6 +1366,12 @@ dependencies = [ "secp256k1-sys", ] +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.229" @@ -1383,7 +1399,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.5", ] [[package]] @@ -1437,7 +1453,7 @@ dependencies = [ "elements", "getrandom 0.2.17", "ghost-cell", - "hex-conservative 0.2.2", + "hex-conservative 0.2.3", "miniscript", "simplicity-sys", ] @@ -1454,10 +1470,11 @@ dependencies = [ [[package]] name = "simplicityhl" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361316795ec753230c421d964ab60940d20d5252c2d39272452528832c9e0ec5" +checksum = "ae77168fa3367d234b11bb3999cbd6e04d7180db0b9e11f83de259528e3b23d1" dependencies = [ + "ariadne", "base64 0.21.7", "chumsky", "clap", @@ -1465,6 +1482,7 @@ dependencies = [ "getrandom 0.2.17", "itertools", "miniscript", + "semver", "serde", "serde_json", "simplicity-lang", @@ -1491,9 +1509,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smplx-build" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5a566aed019a661983dc6f1e420c6431f1cd47c0e3b2d3fabdeb9de75f0c07" +checksum = "ef1538c2de418c94bfec9824b140a0f8bc2f2542cde0f18ef1b2e3a23e5c9eb1" dependencies = [ "glob", "globwalk", @@ -1512,9 +1530,9 @@ dependencies = [ [[package]] name = "smplx-macros" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d68dfea03cfb3282d1c28c9cb80ccb683dc015d1f6ab2f1488806f7e9502fe2" +checksum = "8eb08baf344083ccad07886156cbd92feb2d66b1886b5b73e61a9e03db2ef8f7" dependencies = [ "smplx-build", "smplx-test", @@ -1523,9 +1541,9 @@ dependencies = [ [[package]] name = "smplx-regtest" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a0c9536049b55eebe96cbbd46c6b51026ae964f7c2e664cb4297af9b36178" +checksum = "11d419c9a387fcd2944ac487dff8ced762fc1d52e21186366ab70e7c899931a6" dependencies = [ "electrsd", "hex", @@ -1539,9 +1557,9 @@ dependencies = [ [[package]] name = "smplx-sdk" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "700f68a14dbbe1e6b214965470197e12505693f7e0fd82360efb53d5430cb823" +checksum = "3ac052541ca5611bcdd23d46317970b2065c73ebc7864e53ff5607b30c96dfa9" dependencies = [ "bip39", "bitcoin_hashes", @@ -1559,9 +1577,9 @@ dependencies = [ [[package]] name = "smplx-std" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b11221eb218a73f1d9e20c57364905e62351c43feb601dbc64d406137ebd63c" +checksum = "c6a9e1bdf1d8139fbdf2be662fdcd11761ae61604aeb78628ced8506f7c6a7b9" dependencies = [ "either", "serde", @@ -1573,9 +1591,9 @@ dependencies = [ [[package]] name = "smplx-test" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae375c32c2f220320459710b54bbd7d7289bde0a45daa20cc0193a1999cd98" +checksum = "a3bd10a1047d731372fc1589ebf2a76444ad8c65d352f84115718e6345f3aa0c" dependencies = [ "electrsd", "proc-macro2", @@ -1633,9 +1651,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" dependencies = [ "proc-macro2", "quote", @@ -1678,14 +1696,14 @@ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.5", ] [[package]] name = "tinyvec" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +checksum = "4cf0ded5c4e56918d8f8a339e1bb67d038d3bc6d144ac407904015ba2e4cde9b" dependencies = [ "tinyvec_macros", ] @@ -1796,6 +1814,12 @@ version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -1838,9 +1862,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" dependencies = [ "cfg-if", "once_cell", @@ -1851,9 +1875,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1861,22 +1885,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.5", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" dependencies = [ "unicode-ident", ] @@ -2032,20 +2056,26 @@ dependencies = [ "tap", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "zerocopy" -version = "0.8.56" +version = "0.8.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" +checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.56" +version = "0.8.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" +checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index b918f7b..d54cc7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["simplicity", "liquid", "elements", "smart-contracts"] categories = ["cryptography::cryptocurrencies"] [dependencies] -smplx-std = { version = "0.0.10" } +smplx-std = { version = "0.0.11" } [dev-dependencies] anyhow = { version = "1.0.101" } diff --git a/docs/stdlib.json b/docs/stdlib.json index 8fe6358..e51a52a 100644 --- a/docs/stdlib.json +++ b/docs/stdlib.json @@ -391,6 +391,13 @@ "output_type": "bool", "description": "Check if the first `u8` value is greater than or equal to the second." }, + { + "simplicityhl_name": "mul_div_8", + "section": "`u8` arithmetic", + "input_type": "u8, u8, u8", + "output_type": "u8", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u8." + }, { "simplicityhl_name": "u8_to_u16", "section": "`u8` conversions", @@ -510,6 +517,13 @@ "output_type": "bool", "description": "Check if the first `u16` value is greater than or equal to the second." }, + { + "simplicityhl_name": "mul_div_16", + "section": "`u16` arithmetic", + "input_type": "u16, u16, u16", + "output_type": "u16", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u16." + }, { "simplicityhl_name": "u16_to_u32", "section": "`u16` conversions", @@ -629,6 +643,13 @@ "output_type": "bool", "description": "Check if the first `u32` value is greater than or equal to the second." }, + { + "simplicityhl_name": "mul_div_32", + "section": "`u32` arithmetic", + "input_type": "u32, u32, u32", + "output_type": "u32", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u32." + }, { "simplicityhl_name": "u32_to_u64", "section": "`u32` conversions", @@ -755,6 +776,13 @@ "output_type": "bool", "description": "Check if the first `u64` value is greater than or equal to the second." }, + { + "simplicityhl_name": "mul_div_64", + "section": "`u64` arithmetic", + "input_type": "u64, u64, u64", + "output_type": "u64", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u64." + }, { "simplicityhl_name": "u64_to_u128", "section": "`u64` conversions", @@ -818,6 +846,153 @@ "output_type": "u32", "description": "Narrow a `u64` value to `u32`.\n\n## Panics\nPanics if the value does not fit in `u32`." }, + { + "simplicityhl_name": "add_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "(bool, u128)", + "description": "Add two `u128` values. Return the carry bit and the sum." + }, + { + "simplicityhl_name": "add_128_64", + "section": "`u128` arithmetic", + "input_type": "u128, u64", + "output_type": "(bool, u128)", + "description": "Add a `u64` value to a `u128` value. Return the carry bit and the sum." + }, + { + "simplicityhl_name": "checked_add_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "Option", + "description": "Add two `u128` values. Return `Some` of the sum, or `None` if the result overflows `u128`." + }, + { + "simplicityhl_name": "safe_add_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u128", + "description": "Add two `u128` values.\n\n## Panics\nPanics if the result overflows `u128`." + }, + { + "simplicityhl_name": "sub_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "(bool, u128)", + "description": "Subtract the second `u128` value from the first. Return the borrow bit and the difference." + }, + { + "simplicityhl_name": "checked_sub_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "Option", + "description": "Subtract the second `u128` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u128`." + }, + { + "simplicityhl_name": "safe_sub_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u128", + "description": "Subtract the second `u128` value from the first.\n\n## Panics\nPanics if the result would underflow `u128`." + }, + { + "simplicityhl_name": "mul_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u256", + "description": "Multiply two `u128` values. The full, non-truncated product is returned as a `u256`, so this operation can never overflow." + }, + { + "simplicityhl_name": "checked_mul_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "Option", + "description": "Multiply two `u128` values. Return `Some` of the product, or `None` if the result overflows `u128`." + }, + { + "simplicityhl_name": "safe_mul_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u128", + "description": "Multiply two `u128` values.\n\n## Panics\nPanics if the result overflows `u128`." + }, + { + "simplicityhl_name": "div_mod_128_64", + "section": "`u128` arithmetic", + "input_type": "u128, u64", + "output_type": "(u128, u64)", + "description": "Divide a `u128` value by a `u64` value, returning the `u128` quotient and the `u64` remainder.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "div_mod_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "(u128, u128)", + "description": "Divide the first `u128` value by the second, returning the quotient and the remainder.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "div_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u128", + "description": "Divide the first `u128` value by the second, returning the quotient.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "checked_div_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "Option", + "description": "Divide the first `u128` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." + }, + { + "simplicityhl_name": "safe_div_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u128", + "description": "Divide the first `u128` value by the second.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "full_add_128", + "section": "`u128` arithmetic", + "input_type": "bool, u128, u128", + "output_type": "(bool, u128)", + "description": "Add two `u128` values, taking an incoming carry bit. Return the outgoing carry bit and the sum." + }, + { + "simplicityhl_name": "full_sub_128", + "section": "`u128` arithmetic", + "input_type": "bool, u128, u128", + "output_type": "(bool, u128)", + "description": "Subtract the second `u128` value from the first, taking an incoming borrow bit. Return the outgoing borrow bit and the difference." + }, + { + "simplicityhl_name": "mul_128_64", + "section": "`u128` arithmetic", + "input_type": "u128, u64", + "output_type": "u256", + "description": "Multiply a `u128` value by a `u64` value. The full, non-truncated product is returned as a `u256`, so this operation can never overflow." + }, + { + "simplicityhl_name": "calculate_normalizer_base_64", + "section": "`u128` arithmetic", + "input_type": "u128, bool", + "output_type": "u64", + "description": "Helper for `jet::div_mod_128_64`-based division algorithms. Returns the factor by which `b` should be multiplied so that its most-significant non-zero word is at least `2^63`, as required by those algorithms (which operate in base `2^64`). Set `is_b_u128` to `true` if `b`'s upper 64 bits may be non-zero, or `false` if `b` is known to fit in `u64` (in which case its upper 64 bits must already be zero).\n\n## Panics\nThe assertion fails if `is_b_u128` is `false` but `b`'s upper 64 bits are non-zero, or if `b` is zero." + }, + { + "simplicityhl_name": "estimate_quotient_digit_base_64", + "section": "`u128` arithmetic", + "input_type": "u64, u64, u64, u64, u64", + "output_type": "u64", + "description": "Helper for Algorithm D division. Estimates and corrects the next base-`2^64` quotient digit from the three most-significant dividend words (`u2`, `u1`, `u0`) and the two most-significant divisor words (`v1`, `v0`)." + }, + { + "simplicityhl_name": "mul_div_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128, u128", + "output_type": "u128", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u128." + }, { "simplicityhl_name": "and_128", "section": "`u128` bit logic", @@ -959,144 +1134,172 @@ "description": "Narrow a `u128` value to `u64`.\n\n## Panics\nPanics if the value does not fit in `u64`." }, { - "simplicityhl_name": "add_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "(bool, u128)", - "description": "Add two `u128` values. Return the carry bit and the sum." + "simplicityhl_name": "add_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "(bool, u256)", + "description": "Add two `u256` values. Return the carry bit and the sum." }, { - "simplicityhl_name": "add_128_64", - "section": "`u128` arithmetic", - "input_type": "u128, u64", - "output_type": "(bool, u128)", - "description": "Add a `u64` value to a `u128` value. Return the carry bit and the sum." + "simplicityhl_name": "add_256_128", + "section": "`u256` arithmetic", + "input_type": "u256, u128", + "output_type": "(bool, u256)", + "description": "Add a `u128` value to a `u256` value. Return the carry bit and the sum." }, { - "simplicityhl_name": "checked_add_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "Option", - "description": "Add two `u128` values. Return `Some` of the sum, or `None` if the result overflows `u128`." + "simplicityhl_name": "full_add_256", + "section": "`u256` arithmetic", + "input_type": "bool, u256, u256", + "output_type": "(bool, u256)", + "description": "Add two u256 values, taking an incoming carry bit. Return the outgoing carry bit and the sum." }, { - "simplicityhl_name": "safe_add_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "u128", - "description": "Add two `u128` values.\n\n## Panics\nPanics if the result overflows `u128`." + "simplicityhl_name": "checked_add_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "Option", + "description": "Add two `u256` values. Return `Some` of the sum, or `None` if the result overflows `u256`." }, { - "simplicityhl_name": "sub_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "(bool, u128)", - "description": "Subtract the second `u128` value from the first. Return the borrow bit and the difference." + "simplicityhl_name": "safe_add_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "u256", + "description": "Add two `u256` values.\n\n## Panics\nPanics if the result overflows `u256`." }, { - "simplicityhl_name": "checked_sub_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "Option", - "description": "Subtract the second `u128` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u128`." + "simplicityhl_name": "sub_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "(bool, u256)", + "description": "Subtract the second `u256` value from the first. Return the borrow bit and the difference." }, { - "simplicityhl_name": "safe_sub_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "u128", - "description": "Subtract the second `u128` value from the first.\n\n## Panics\nPanics if the result would underflow `u128`." + "simplicityhl_name": "checked_sub_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "Option", + "description": "Subtract the second `u256` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u256`." }, { - "simplicityhl_name": "mul_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", + "simplicityhl_name": "safe_sub_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", "output_type": "u256", - "description": "Multiply two `u128` values. The full, non-truncated product is returned as a `u256`, so this operation can never overflow." + "description": "Subtract the second `u256` value from the first.\n\n## Panics\nPanics if the result would underflow `u256`." }, { - "simplicityhl_name": "checked_mul_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "Option", - "description": "Multiply two `u128` values. Return `Some` of the product, or `None` if the result overflows `u128`." + "simplicityhl_name": "mul_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "(u256, u256)", + "description": "Multiply two `u256` values. The full, non-truncated product is returned as a pair of `u256` values, most-significant first, so this operation can never overflow." }, { - "simplicityhl_name": "safe_mul_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "u128", - "description": "Multiply two `u128` values.\n\n## Panics\nPanics if the result overflows `u128`." + "simplicityhl_name": "mul_256_64", + "section": "`u256` arithmetic", + "input_type": "u256, u64", + "output_type": "(u64, u256)", + "description": "Multiply a `u256` value by a `u64` value. The full, non-truncated product is returned as a `u64`/`u256` pair, most-significant first, so this operation can never overflow." }, { - "simplicityhl_name": "div_mod_128_64", - "section": "`u128` arithmetic", - "input_type": "u128, u64", - "output_type": "(u128, u64)", - "description": "Divide a `u128` value by a `u64` value, returning the `u128` quotient and the `u64` remainder.\n\n## Panics\nPanics if the divisor is zero." + "simplicityhl_name": "mul_256_128", + "section": "`u256` arithmetic", + "input_type": "u256, u128", + "output_type": "(u128, u256)", + "description": "Multiply a `u256` value by a `u128` value. The full, non-truncated product is returned as a `u128`/`u256` pair, most-significant first, so this operation can never overflow." }, { - "simplicityhl_name": "div_mod_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "(u128, u128)", - "description": "Divide the first `u128` value by the second, returning the quotient and the remainder.\n\n## Panics\nPanics if the divisor is zero." + "simplicityhl_name": "mul_512_128", + "section": "`u256` arithmetic", + "input_type": "u256, u256, u128", + "output_type": "(u128, u256, u256)", + "description": "Multiply a `u512` value by a `u128` value. The full, non-truncated product is returned as a (`u128`, `u256`, `u256`) tuple, most-significant first, so this operation can never overflow." }, { - "simplicityhl_name": "div_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "u128", - "description": "Divide the first `u128` value by the second, returning the quotient.\n\n## Panics\nPanics if the divisor is zero." + "simplicityhl_name": "checked_mul_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "Option", + "description": "Multiply two `u256` values. Return `Some` of the product, or `None` if the result overflows `u256`." }, { - "simplicityhl_name": "checked_div_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "Option", - "description": "Divide the first `u128` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." + "simplicityhl_name": "safe_mul_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "u256", + "description": "Multiply two `u256` values.\n\n## Panics\nPanics if the result overflows `u256`." }, { - "simplicityhl_name": "safe_div_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", + "simplicityhl_name": "safe_mul_256_128", + "section": "`u256` arithmetic", + "input_type": "u256, u128", + "output_type": "u256", + "description": "Multiply a `u256` value by a `u128` value.\n\n## Panics\nPanics if the result overflows `u256`." + }, + { + "simplicityhl_name": "calculate_normalizer_base_128", + "section": "`u256` arithmetic", + "input_type": "u256", "output_type": "u128", - "description": "Divide the first `u128` value by the second.\n\n## Panics\nPanics if the divisor is zero." + "description": "Helper for algorithm D division algorithms. Returns the factor by which `b` should be multiplied so that its upper 128 bits are at least `2^127`, as required by those algorithms (which operate in base `2^128`).\n\n## Panics\nThe assertion fails if `b`'s upper 128 bits are zero" }, { - "simplicityhl_name": "full_add_128", - "section": "`u128` arithmetic", - "input_type": "bool, u128, u128", - "output_type": "(bool, u128)", - "description": "Add two `u128` values, taking an incoming carry bit. Return the outgoing carry bit and the sum." + "simplicityhl_name": "div_mod_256_64", + "section": "`u256` arithmetic", + "input_type": "u256, u64", + "output_type": "(u256, u64)", + "description": "Divide a `u256` value by a `u64` value, returning the `u256` quotient and the `u64` remainder.\n\n## Panics\nPanics if the divisor is zero." }, { - "simplicityhl_name": "full_sub_128", - "section": "`u128` arithmetic", - "input_type": "bool, u128, u128", - "output_type": "(bool, u128)", - "description": "Subtract the second `u128` value from the first, taking an incoming borrow bit. Return the outgoing borrow bit and the difference." + "simplicityhl_name": "algorithm_d_256_128", + "section": "`u256` arithmetic", + "input_type": "u256, u128", + "output_type": "(u256, u128)", + "description": "Divide a `u256` value by a `u128` value, returning the `u256` quotient and the `u128` remainder.\n\n## Panics\nPanics if the divisor is zero or if divisor's upper 64 bits are zero." }, { - "simplicityhl_name": "mul_128_64", - "section": "`u128` arithmetic", - "input_type": "u128, u64", + "simplicityhl_name": "div_mod_256_128", + "section": "`u256` arithmetic", + "input_type": "u256, u128", + "output_type": "(u256, u128)", + "description": "Divide a `u256` value by a `u128` value, returning the `u256` quotient and the `u128` remainder.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "div_mod_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "(u256, u256)", + "description": "Divide the first `u256` value by the second, returning the quotient and the remainder.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "div_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", "output_type": "u256", - "description": "Multiply a `u128` value by a `u64` value. The full, non-truncated product is returned as a `u256`, so this operation can never overflow." + "description": "Divide the first `u256` value by the second, returning the quotient.\n\n## Panics\nPanics if the divisor is zero." }, { - "simplicityhl_name": "calculate_normalizer_base_64", - "section": "`u128` arithmetic", - "input_type": "u128, bool", - "output_type": "u64", - "description": "Helper for `jet::div_mod_128_64`-based division algorithms. Returns the factor by which `b` should be multiplied so that its most-significant non-zero word is at least `2^63`, as required by those algorithms (which operate in base `2^64`). Set `is_b_u128` to `true` if `b`'s upper 64 bits may be non-zero, or `false` if `b` is known to fit in `u64` (in which case its upper 64 bits must already be zero).\n\n## Panics\nThe assertion fails if `is_b_u128` is `false` but `b`'s upper 64 bits are non-zero, or if `b` is zero." + "simplicityhl_name": "checked_div_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "Option", + "description": "Divide the first `u256` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." }, { - "simplicityhl_name": "estimate_quotient_digit_base_64", - "section": "`u128` arithmetic", - "input_type": "u64, u64, u64, u64, u64", - "output_type": "u64", - "description": "Helper for Algorithm D division. Estimates and corrects the next base-`2^64` quotient digit from the three most-significant dividend words (`u2`, `u1`, `u0`) and the two most-significant divisor words (`v1`, `v0`)." + "simplicityhl_name": "safe_div_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "u256", + "description": "Divide the first `u256` value by the second.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "mul_div_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256, u256", + "output_type": "u256", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u256." }, { "simplicityhl_name": "and_256", @@ -1237,131 +1440,5 @@ "input_type": "u256", "output_type": "u128", "description": "Narrow a `u256` value to `u128`.\n\n## Panics\nPanics if the value does not fit in `u128`." - }, - { - "simplicityhl_name": "add_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "(bool, u256)", - "description": "Add two `u256` values. Return the carry bit and the sum." - }, - { - "simplicityhl_name": "add_256_128", - "section": "`u256` arithmetic", - "input_type": "u256, u128", - "output_type": "(bool, u256)", - "description": "Add a `u128` value to a `u256` value. Return the carry bit and the sum." - }, - { - "simplicityhl_name": "checked_add_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "Option", - "description": "Add two `u256` values. Return `Some` of the sum, or `None` if the result overflows `u256`." - }, - { - "simplicityhl_name": "safe_add_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "u256", - "description": "Add two `u256` values.\n\n## Panics\nPanics if the result overflows `u256`." - }, - { - "simplicityhl_name": "sub_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "(bool, u256)", - "description": "Subtract the second `u256` value from the first. Return the borrow bit and the difference." - }, - { - "simplicityhl_name": "checked_sub_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "Option", - "description": "Subtract the second `u256` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u256`." - }, - { - "simplicityhl_name": "safe_sub_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "u256", - "description": "Subtract the second `u256` value from the first.\n\n## Panics\nPanics if the result would underflow `u256`." - }, - { - "simplicityhl_name": "mul_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "(u256, u256)", - "description": "Multiply two `u256` values. The full, non-truncated product is returned as a pair of `u256` values, most-significant first, so this operation can never overflow." - }, - { - "simplicityhl_name": "mul_256_64", - "section": "`u256` arithmetic", - "input_type": "u256, u64", - "output_type": "(u64, u256)", - "description": "Multiply a `u256` value by a `u64` value. The full, non-truncated product is returned as a `u64`/`u256` pair, most-significant first, so this operation can never overflow." - }, - { - "simplicityhl_name": "mul_256_128", - "section": "`u256` arithmetic", - "input_type": "u256, u128", - "output_type": "(u128, u256)", - "description": "Multiply a `u256` value by a `u128` value. The full, non-truncated product is returned as a `u128`/`u256` pair, most-significant first, so this operation can never overflow." - }, - { - "simplicityhl_name": "checked_mul_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "Option", - "description": "Multiply two `u256` values. Return `Some` of the product, or `None` if the result overflows `u256`." - }, - { - "simplicityhl_name": "safe_mul_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "u256", - "description": "Multiply two `u256` values.\n\n## Panics\nPanics if the result overflows `u256`." - }, - { - "simplicityhl_name": "div_mod_256_64", - "section": "`u256` arithmetic", - "input_type": "u256, u64", - "output_type": "(u256, u64)", - "description": "Divide a `u256` value by a `u64` value, returning the `u256` quotient and the `u64` remainder.\n\n## Panics\nPanics if the divisor is zero." - }, - { - "simplicityhl_name": "div_mod_256_128", - "section": "`u256` arithmetic", - "input_type": "u256, u128", - "output_type": "(u256, u128)", - "description": "Divide a `u256` value by a `u128` value, returning the `u256` quotient and the `u128` remainder.\n\n## Panics\nPanics if the divisor is zero." - }, - { - "simplicityhl_name": "div_mod_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "(u256, u256)", - "description": "Divide the first `u256` value by the second, returning the quotient and the remainder.\n\n## Panics\nPanics if the divisor is zero." - }, - { - "simplicityhl_name": "div_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "u256", - "description": "Divide the first `u256` value by the second, returning the quotient.\n\n## Panics\nPanics if the divisor is zero." - }, - { - "simplicityhl_name": "checked_div_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "Option", - "description": "Divide the first `u256` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." - }, - { - "simplicityhl_name": "safe_div_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "u256", - "description": "Divide the first `u256` value by the second.\n\n## Panics\nPanics if the divisor is zero." } -] +] \ No newline at end of file diff --git a/simf/lib/u128/math.simf b/simf/lib/u128/math.simf index 1909850..38a679d 100644 --- a/simf/lib/u128/math.simf +++ b/simf/lib/u128/math.simf @@ -1,12 +1,15 @@ use crate::lib::binary::{not, or, and}; +use crate::lib::u64::convert::u64_to_u128; + use crate::lib::u128::comparison::{is_zero_128, lt_128}; +use crate::lib::u128::convert::{u128_to_u256, split_u128_into_u64}; /// Arithmetic /// 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); - let (b_high, b_low): (u64, u64) = ::into(b); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); let (carry_low, sum_low): (bool, u64) = jet::add_64(a_low, b_low); let (carry_high, sum_high): (bool, u64) = jet::full_add_64(carry_low, a_high, b_high); @@ -17,7 +20,7 @@ pub fn add_128(a: u128, b: u128) -> (bool, u128) { /// 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 (a_high, a_low): (u64, u64) = split_u128_into_u64(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); @@ -28,8 +31,8 @@ pub fn add_128_64(a: u128, b: u64) -> (bool, u128) { /// Adds two integers. Takes a carry-in and returns a carry-out pub fn full_add_128(carry_in: bool, a: u128, b: u128) -> (bool, u128) { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(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); @@ -55,8 +58,8 @@ pub fn safe_add_128(a: u128, b: u128) -> u128 { /// Subtracts the second integer from the first integer, and returns the borrow bit pub fn sub_128(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 (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); let (borrow_low, diff_low): (bool, u64) = jet::subtract_64(a_low, b_low); let (borrow_high, diff_high): (bool, u64) = jet::full_subtract_64(borrow_low, a_high, b_high); @@ -67,8 +70,8 @@ pub fn sub_128(a: u128, b: u128) -> (bool, u128) { /// 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 (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(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); @@ -98,19 +101,19 @@ pub fn safe_sub_128(a: u128, b: u128) -> u128 { /// In the same way, b = b_high * 2^64 + b_low. /// Therefore, a * b = 2^128 * a_high * b_high + 2^64(a_high * b_low + a_low * b_high) + a_low * b_low. pub fn mul_128(a: u128, b: u128) -> u256 { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); let highest: u128 = jet::multiply_64(a_high, b_high); let lowest: u128 = jet::multiply_64(a_low, b_low); - let (word_1, word_0): (u64, u64) = ::into(lowest); - let (word_3, word_2): (u64, u64) = ::into(highest); + let (word_1, word_0): (u64, u64) = split_u128_into_u64(lowest); + let (word_3, word_2): (u64, u64) = split_u128_into_u64(highest); let product_1: u128 = jet::multiply_64(a_high, b_low); let product_2: u128 = jet::multiply_64(b_high, a_low); let (carry_3a, middle): (bool, u128) = add_128(product_1, product_2); - let (middle_2, middle_1): (u64, u64) = ::into(middle); + let (middle_2, middle_1): (u64, u64) = split_u128_into_u64(middle); // fold the low-side carry directly into the word_2 addition via full_add_64, // then propagate any resulting carry into word_3 @@ -136,13 +139,13 @@ pub fn mul_128(a: u128, b: u128) -> u256 { /// 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 (a_high, a_low): (u64, u64) = split_u128_into_u64(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 (word_1, word_0): (u64, u64) = split_u128_into_u64(lowest); + let (word_3, word_2): (u64, u64) = split_u128_into_u64(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` @@ -171,17 +174,17 @@ pub fn safe_mul_128(a: u128, b: u128) -> u128 { /// 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 { +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_high, b_low): (u64, u64) = split_u128_into_u64(b); let b_highest_word: u64 = match is_b_u128 { true => b_high, false => { assert!(jet::is_zero_64(b_high)); b_low - } + }, }; assert!(not(jet::is_zero_64(b_highest_word))); @@ -206,10 +209,10 @@ pub fn calculate_normalizer_base_64(b: u128, is_b_u128: bool) -> 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)); + let norm_128: u128 = u64_to_u128(norm); match jet::eq_64(norm, 1) { - true => (<(u128, u128)>::into((0, a)), b, norm), + true => (u128_to_u256(a), b, norm), false => (mul_128(a, norm_128), safe_mul_128(b, norm_128), norm), } } @@ -220,7 +223,7 @@ fn normalize_to_threshold_128_63(a: u128, b: u128, is_b_u128: bool) -> (u256, u1 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); + let (q_hat, r_hat): (u64, u64) = jet::div_mod_128_64(<(u64, u64)>::into((u2, u1)), v1); (q_hat, r_hat, false) }, false => { @@ -230,7 +233,7 @@ pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u let (carry, r_hat): (bool, u64) = jet::add_64(u1, v1); (jet::high_64(), r_hat, carry) - } + }, }; match carry { @@ -242,7 +245,7 @@ pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u 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 (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); let (carry, r_hat): (bool, u64) = jet::add_64(r_hat, v1); match carry { @@ -253,18 +256,18 @@ pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u 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 (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); q_hat - } + }, false => q_hat, } - } + }, } }, false => q_hat, } - } + }, } } @@ -273,22 +276,27 @@ pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u /// Implements Algorithm D by Donald Knuth. /// Requires the upper half of the divisor to be non-zero. fn algorithm_d_128_128(dividend: u128, divisor: u128) -> (u64, u128) { - let (norm_dividend, norm_divisor, _): (u256, u128, u64) = normalize_to_threshold_128_63(dividend, divisor, true); + let ( + norm_dividend, + norm_divisor, + _ + ): (u256, u128, u64) = normalize_to_threshold_128_63(dividend, divisor, true); // normalized dividend fits into 192 bits let (_, u2, u1, u0): (u64, u64, u64, u64) = ::into(norm_dividend); - let (v1, v0): (u64, u64) = ::into(norm_divisor); + let (v1, v0): (u64, u64) = split_u128_into_u64(norm_divisor); let q: u64 = estimate_quotient_digit_base_64(u2, u1, u0, v1, v0); - let remainder: u128 = safe_sub_128(dividend, safe_mul_128(divisor, <(u64, u64)>::into((0, q)))); + let remainder: u128 = safe_sub_128(dividend, safe_mul_128(divisor, u64_to_u128(q))); (q, remainder) } /// Divides the 128-bit integer by the 64-bit integer, -/// returns a tuple of the u128 quotient and the u64 remainder +/// returns a tuple of the u128 quotient and the u64 remainder. +/// Panics if divisor is equal to zero pub fn div_mod_128_64(a: u128, b: u64) -> (u128, u64) { - let (a_high, a_low): (u64, u64) = ::into(a); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); // calculate the upper half of the quotient let (q_high, remainder): (u64, u64) = jet::div_mod_64(a_high, b); @@ -297,11 +305,19 @@ 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, norm): (u256, u128, u64) = normalize_to_threshold_128_63(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_to_u128(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); + let (_, b_normalized): (u64, u64) = split_u128_into_u64(b_normalized); // remainder < b, so (remainder * 2^64 + a_low) / b fits into u64 let (q_low, r_normalized): (u64, u64) = jet::div_mod_128_64(a_normalized, b_normalized); @@ -313,42 +329,45 @@ pub fn div_mod_128_64(a: u128, b: u64) -> (u128, u64) { /// Divides the first integer by the second integer, /// returns the quotient and the remainder pub fn div_mod_128(a: u128, b: u128) -> (u128, u128) { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - match lt_128(a, b) { + match is_zero_128(b) { true => (0, a), false => { - match and(jet::is_zero_64(a_high), jet::is_zero_64(b_high)) { - true => { - // if both a_high and b_high are zero, this narrows down to 64-bit division - let (q, r): (u64, u64) = jet::div_mod_64(a_low, b_low); - (<(u64, u64)>::into((0, q)), <(u64, u64)>::into((0, r))) - }, + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); + + match lt_128(a, b) { + true => (0, a), false => { - match jet::eq_64(a_high, b_high) { + match and(jet::is_zero_64(a_high), jet::is_zero_64(b_high)) { true => { - // safe: !lt_128(a, b) and a_high == b_high, so a_low >= b_low, - // and the subtraction can not underflow - let (_, diff): (bool, u64) = jet::subtract_64(a_low, b_low); - (1, <(u64, u64)>::into((0, diff))) + // if both a_high and b_high are zero, this narrows down to 64-bit division + let (q, r): (u64, u64) = jet::div_mod_64(a_low, b_low); + (u64_to_u128(q), u64_to_u128(r)) }, false => { - match jet::is_zero_64(b_high) { + match jet::eq_64(a_high, b_high) { true => { - let (q, r): (u128, u64) = div_mod_128_64(a, b_low); - (q, <(u64, u64)>::into((0, r))) + // safe: !lt_128(a, b) and a_high == b_high, so a_low >= b_low, + // and the subtraction can not underflow + let (_, diff): (bool, u64) = jet::subtract_64(a_low, b_low); + (1, u64_to_u128(diff)) }, - false => { - let (q, r): (u64, u128) = algorithm_d_128_128(a, b); - (<(u64, u64)>::into((0, q)), r) - } - } - } + false => match jet::is_zero_64(b_high) { + true => { + let (q, r): (u128, u64) = div_mod_128_64(a, b_low); + (q, u64_to_u128(r)) + }, + false => { + let (q, r): (u64, u128) = algorithm_d_128_128(a, b); + (u64_to_u128(q), r) + }, + }, + } + }, } - } + }, } - } + }, } } diff --git a/simf/lib/u128/mul_div.simf b/simf/lib/u128/mul_div.simf new file mode 100644 index 0000000..6b4c0e2 --- /dev/null +++ b/simf/lib/u128/mul_div.simf @@ -0,0 +1,15 @@ +use crate::lib::u128::math::mul_128; +use crate::lib::u128::convert::u128_to_u256; + +use crate::lib::u256::math::div_256; +use crate::lib::u256::convert::safe_u256_to_u128; + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u128 +pub fn mul_div_128(a: u128, b: u128, denominator: u128) -> u128 { + let denominator_256: u256 = u128_to_u256(denominator); + + let result_256: u256 = div_256(mul_128(a, b), denominator_256); + + safe_u256_to_u128(result_256) +} diff --git a/simf/lib/u16/mul_div.simf b/simf/lib/u16/mul_div.simf new file mode 100644 index 0000000..92e7b04 --- /dev/null +++ b/simf/lib/u16/mul_div.simf @@ -0,0 +1,12 @@ +use crate::lib::u16::convert::u16_to_u32; +use crate::lib::u32::convert::safe_u32_to_u16; + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u16 +pub fn mul_div_16(a: u16, b: u16, denominator: u16) -> u16 { + let denominator_32: u32 = u16_to_u32(denominator); + + let result_32: u32 = jet::divide_32(jet::multiply_16(a, b), denominator_32); + + safe_u32_to_u16(result_32) +} diff --git a/simf/lib/u256/math.simf b/simf/lib/u256/math.simf index 12a37f7..f06ea7c 100644 --- a/simf/lib/u256/math.simf +++ b/simf/lib/u256/math.simf @@ -1,4 +1,6 @@ -use crate::lib::binary::{not, and}; +use crate::lib::binary::{not, and, or}; +use crate::lib::u64::convert::u64_to_u128; + use crate::lib::u128::math::{ add_128, full_add_128, @@ -14,16 +16,17 @@ use crate::lib::u128::math::{ }; use crate::lib::u128::bit::eq_128; use crate::lib::u128::comparison::{is_zero_128, lt_128}; +use crate::lib::u128::convert::{u128_to_u256, split_u128_into_u64}; -use crate::lib::u256::convert::split_u256_into_u64; use crate::lib::u256::comparison::{is_zero_256, lt_256}; +use crate::lib::u256::convert::{split_u256_into_u64, split_u256_into_u128}; /// Arithmetic /// 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 (a_high, a_low): (u128, u128) = split_u256_into_u128(a); + let (b_high, b_low): (u128, u128) = split_u256_into_u128(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); @@ -34,11 +37,23 @@ pub fn add_256(a: u256, b: u256) -> (bool, u256) { /// 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)); + let b: u256 = u128_to_u256(b); add_256(a, b) } +/// Adds two integers. Takes a carry-in and returns a carry-out +pub fn full_add_256(carry_in: bool, a: u256, b: u256) -> (bool, u256) { + let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); + let (b_high, b_low): (u128, u128) = split_u256_into_u128(b); + + let (carry_low, sum_low): (bool, u128) = full_add_128(carry_in, a_low, b_low); + let (carry_out, sum_high): (bool, u128) = full_add_128(carry_low, a_high, b_high); + + let res: u256 = <(u128, u128)>::into((sum_high, sum_low)); + (carry_out, res) +} + /// 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); @@ -56,8 +71,8 @@ pub fn safe_add_256(a: u256, b: u256) -> u256 { /// 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 (a_high, a_low): (u128, u128) = split_u256_into_u128(a); + let (b_high, b_low): (u128, u128) = split_u256_into_u128(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); @@ -87,19 +102,19 @@ pub fn safe_sub_256(a: u256, b: u256) -> u256 { /// 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 (a_high, a_low): (u128, u128) = split_u256_into_u128(a); + let (b_high, b_low): (u128, u128) = split_u256_into_u128(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 (word_1, word_0): (u128, u128) = split_u256_into_u128(lowest); + let (word_3, word_2): (u128, u128) = split_u256_into_u128(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); + let (middle_2, middle_1): (u128, u128) = split_u256_into_u128(middle); // fold the low-side carry directly into the word_2 addition via full_add_128, // then propagate any resulting carry into word_3 @@ -126,15 +141,15 @@ pub fn mul_256(a: u256, b: u256) -> (u256, u256) { /// 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 (a_high, a_low): (u128, u128) = split_u256_into_u128(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_1, word_0): (u128, u128) = split_u256_into_u128(lowest); + let (word_3, word_2): (u128, u128) = split_u256_into_u128(highest); - let (_, word_3): (u64, u64) = ::into(word_3); + let (_, word_3): (u64, u64) = split_u128_into_u64(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 @@ -150,13 +165,13 @@ pub fn mul_256_64(a: u256, b: u64) -> (u64, u256) { /// 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 (a_high, a_low): (u128, u128) = split_u256_into_u128(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 (word_1, word_0): (u128, u128) = split_u256_into_u128(lowest); + let (word_3, word_2): (u128, u128) = split_u256_into_u128(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 @@ -166,6 +181,24 @@ pub fn mul_256_128(a: u256, b: u128) -> (u128, u256) { (res_2, <(u128, u128)>::into((res_1, word_0))) } +/// Multiplies two integers. +/// The output consists of a 128-bit integer and two 256-bit integers. +/// The idea is that 512-bit `a` divides into 256-bit `a_high` and `a_low`, +/// so a = a_high * 2^256 + a_low, +/// and a * b = 2^256 * a_high * b + a_low * b +pub fn mul_512_128(a_high: u256, a_low: u256, b: u128) -> (u128, u256, u256) { + let (word_1, word_0): (u128, u256) = mul_256_128(a_low, b); + let (word_3, word_2): (u128, u256) = mul_256_128(a_high, b); + + let (carry, res_1): (bool, u256) = add_256_128(word_2, word_1); + + // a * b fits into u640, 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, word_3, 0); + + (res_2, 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); @@ -182,13 +215,39 @@ pub fn safe_mul_256(a: u256, b: u256) -> u256 { } /// Returns the product of u256 and u128 values, panics if the result overflows u256 -fn safe_mul_256_128(a: u256, b: u128) -> u256 { +pub 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 } +/// Helper function that can be used with 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^127 +pub fn calculate_normalizer_base_128(b: u256) -> u128 { + // Compile-time constant: 2^127 + let threshold: u128 = 0x80000000000000000000000000000000; + let (b_high, _): (u128, u128) = split_u256_into_u128(b); + + assert!(not(is_zero_128(b_high))); + + match lt_128(b_high, threshold) { + true => { + let (norm, remainder): (u128, u128) = div_mod_128(threshold, b_high); + + 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 + }, + } + }, + false => 1, + } +} + /// 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. @@ -197,7 +256,7 @@ fn safe_mul_256_128(a: u256, b: u128) -> u256 { /// 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)); + let norm_128: u128 = u64_to_u128(norm); match jet::eq_64(norm, 1) { true => (0, a, b, norm), @@ -217,30 +276,15 @@ fn normalize_to_threshold_256_63(a: u256, b: u128, is_b_u128: bool) -> (u64, u25 /// 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: u128 = calculate_normalizer_base_128(b); - let (norm, remainder): (u128, u128) = div_mod_128(threshold, b_high); - - let norm: u128 = match is_zero_128(remainder) { - true => norm, + match eq_128(norm, 1) { + true => (0, a, b), 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), } } @@ -252,23 +296,27 @@ pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { // 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 divisor: u128 = u64_to_u128(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 ( + _, + 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_u256_into_u64(norm_dividend_low); - let (_, v0): (u64, u64) = ::into(norm_divisor); + let (_, v0): (u64, u64) = split_u128_into_u64(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 (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)); + let q: u256 = <(u64, u64, u64, u64)>::into((q3, q2, q1, q0)); (q, remainder) } @@ -276,7 +324,7 @@ pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { /// Multiplies the divisor by the quotient digit and subtracts the result from /// the corresponding dividend limbs. Returns the updated dividend segment, /// which is used by Algorithm D -fn mul_and_sub(q: u64, u2: u64, u1: u64, u0: u64, v: u128) -> (u64, u64) { +fn mul_and_sub_64(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); @@ -291,22 +339,26 @@ fn mul_and_sub(q: u64, u2: u64, u1: u64, u0: u64, v: u128) -> (u64, u64) { /// 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); +pub 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_u256_into_u64(norm_dividend_low); - let (v1, v0): (u64, u64) = ::into(norm_divisor); + let (v1, v0): (u64, u64) = split_u128_into_u64(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 (u3, u2): (u64, u64) = mul_and_sub_64(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 (u2, u1): (u64, u64) = mul_and_sub_64(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 (u1, u0): (u64, u64) = mul_and_sub_64(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); @@ -317,13 +369,13 @@ fn algorithm_d_256_128(dividend: u256, divisor: u128) -> (u256, u128) { /// Divides the 256-bit integer by the 128-bit integer, /// returns a tuple of the u256 quotient and the u128 remainder pub fn div_mod_256_128(dividend: u256, divisor: u128) -> (u256, u128) { - let (divisor_high, divisor_low): (u64, u64) = ::into(divisor); + let (divisor_high, divisor_low): (u64, u64) = split_u128_into_u64(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))) + (q, u64_to_u128(r)) }, false => algorithm_d_256_128(dividend, divisor), } @@ -334,13 +386,17 @@ pub fn div_mod_256_128(dividend: u256, divisor: u128) -> (u256, u128) { /// 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 ( + 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 (u1, u0): (u128, u128) = split_u256_into_u128(norm_dividend_low); + let (v1, v0): (u128, u128) = split_u256_into_u128(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 (_, q_hat): (u128, u128) = split_u256_into_u128(q_hat); let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); let u_hat: u256 = mul_128(q_hat, v0); @@ -349,25 +405,25 @@ fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { 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 (_, 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))); + let (_, u_hat): (bool, u256) = sub_256(u_hat, u128_to_u256(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); + let (_, q_hat): (bool, u128) = sub_128(q_hat, 1); q_hat - } + }, false => q_hat, } - } + }, } }, false => q_hat, @@ -380,17 +436,17 @@ fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { /// 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); + let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); + let (b_high, b_low): (u128, u128) = split_u256_into_u128(b); - match lt_256(a, b) { + match or(is_zero_256(b), lt_256(a, b)) { true => (0, a), false => { - match and(is_zero_128(a_high), is_zero_128(b_high)) { - true => { + 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))) + (u128_to_u256(q), u128_to_u256(r)) }, false => { match eq_128(a_high, b_high) { @@ -398,24 +454,22 @@ pub fn div_mod_256(a: u256, b: u256) -> (u256, u256) { // 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))) + (1, u128_to_u256(diff)) + }, + false => match is_zero_128(b_high) { + true => { + let (q, r): (u256, u128) = div_mod_256_128(a, b_low); + (q, u128_to_u256(r)) + }, + false => { + let (q, r): (u128, u256) = algorithm_d_256_256(a, b); + (u128_to_u256(q), r) + }, }, - 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) - } - } - } } - } + }, } - } + }, } } diff --git a/simf/lib/u256/mul_div.simf b/simf/lib/u256/mul_div.simf new file mode 100644 index 0000000..52ad6ef --- /dev/null +++ b/simf/lib/u256/mul_div.simf @@ -0,0 +1,179 @@ +use crate::lib::binary::not; + +use crate::lib::u128::math::{add_128, sub_128, full_sub_128, mul_128}; +use crate::lib::u128::bit::eq_128; +use crate::lib::u128::comparison::{is_zero_128, lt_128}; + +use crate::lib::u256::math::{ + algorithm_d_256_128, + mul_256_128, + sub_256, + safe_sub_256, + calculate_normalizer_base_128, + mul_512_128, + safe_mul_256_128, + mul_256, + div_mod_256_128, + div_256 +}; +use crate::lib::u256::convert::split_u256_into_u128; +use crate::lib::u256::comparison::{is_zero_256, lt_256}; + +/// 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 u128 +fn estimate_quotient_digit_base_128(u2: u128, u1: u128, u0: u128, v1: u128, v0: u128) -> u128 { + let (q_hat, r_hat, carry): (u128, u128, bool) = match lt_128(u2, v1) { + true => { + let (q_hat, r_hat) : (u256, u128) = algorithm_d_256_128(<(u128, u128)>::into((u2, u1)), v1); + let (_, q_hat): (u128, u128) = split_u256_into_u128(q_hat); // skip the check for optimization. u2 < v1, so q_hat fits into u128 + + (q_hat, r_hat, false) + }, + false => { + // This means u2 == v1, q_hat = 2^128, and r_hat = u1. + // Therefore, we need to decrement q and add v1 to r_hat. + // r_hat = u1 + v1 may overflow u128, which means that the estimate is exact + let (carry, r_hat): (bool, u128) = add_128(u1, v1); + + let u128_max: u128 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + (u128_max, r_hat, carry) + }, + }; + + match carry { + true => q_hat, + false => { + let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); + + // correcting estimation: q_hat is off by at most 2. + match lt_256(r_hat_u0, mul_128(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, 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)); + + match lt_256(r_hat_u0, mul_128(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, u128) = sub_128(q_hat, 1); + + q_hat + }, + false => q_hat, + } + }, + } + }, + false => q_hat, + } + }, + } +} + +/// Normalizes the dividend and divisor for Algorithm D by multiplying +/// all 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 640 bits and is returned as a +/// `(u128, u256, u256)` tuple. The normalized divisor fits into a `u256` +fn normalize_to_threshold_512_127(a_high: u256, a_low: u256, b: u256) -> (u128, u256, u256, u256) { + let norm: u128 = calculate_normalizer_base_128(b); + + match eq_128(norm, 1) { + true => (0, a_high, a_low, b), + false => { + let (a_2, a_1, a_0): (u128, u256, u256) = mul_512_128(a_high, a_low, norm); + + (a_2, a_1, a_0, safe_mul_256_128(b, norm)) + }, + } +} + +/// 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_128(q: u128, u2: u128, u1: u128, u0: u128, v: u256) -> (u128, u128) { + let u1_0: u256 = <(u128, u128)>::into((u1, u0)); + + let (q_v_1, q_v_0): (u128, u256) = mul_256_128(v, q); + + let (borrow_low, u_updated_1_0): (bool, u256) = sub_256(u1_0, q_v_0); + let (borrow, u_updated_2): (bool, u128) = full_sub_128(borrow_low, u2, q_v_1); + + assert!(not(borrow)); + assert!(is_zero_128(u_updated_2)); + + split_u256_into_u128(u_updated_1_0) +} + +/// Divides a 512-bit integer by a 256-bit integer and returns a `u256` quotient. +/// Implements Algorithm D by Donald Knuth. +/// Requires the upper half of the divisor to be non-zero (`divisor >= 2^128`) +/// and the quotient to fit into `u256`. +fn algorithm_d_512_256(dividend_high: u256, dividend_low: u256, divisor: u256) -> u256 { + let ( + _, + norm_dividend_high, + norm_dividend_low, + norm_divisor + ): (u128, u256, u256, u256) = (normalize_to_threshold_512_127(dividend_high, dividend_low, divisor)); + + let (u3, u2): (u128, u128) = split_u256_into_u128(norm_dividend_high); + let (u1, u0): (u128, u128) = split_u256_into_u128(norm_dividend_low); + let (v1, v0): (u128, u128) = split_u256_into_u128(norm_divisor); + + let q1: u128 = estimate_quotient_digit_base_128(u3, u2, u1, v1, v0); + let (u2, u1): (u128, u128) = mul_and_sub_128(q1, u3, u2, u1, norm_divisor); + + let q0: u128 = estimate_quotient_digit_base_128(u2, u1, u0, v1, v0); + + <(u128, u128)>::into((q1, q0)) +} + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u256 +pub fn mul_div_256(a: u256, b: u256, denominator: u256) -> u256 { + match is_zero_256(denominator) { + true => 0, + false => { + let (result_high, result_low): (u256, u256) = mul_256(a, b); + + match is_zero_256(result_high) { + true => div_256(result_low, denominator), + false => { + // if result_high >= denominator, result will overflow u256 + assert!(lt_256(result_high, denominator)); + + let (denom_high, denom_low): (u128, u128) = split_u256_into_u128(denominator); + + match is_zero_128(denom_high) { + true => { + // result_high < denominator, denominator fits into u128, so result_high fits too. + // split_u256_into_u128 is used instead of safe_u256_to_u128 to avoid the unnecessary check + let (_, result_2): (u128, u128) = split_u256_into_u128(result_high); + let (result_1, result_0): (u128, u128) = split_u256_into_u128(result_low); + + // result_high < denominator, so result_2 < denom_low and q1 fits into u128 + let (q1, r): (u256, u128) = div_mod_256_128(<(u128, u128)>::into((result_2, result_1)), denom_low); + let (_, q1): (u128, u128) = split_u256_into_u128(q1); + + // r < denom_low, so q0 fits into u128 + let (q0, _): (u256, u128) = div_mod_256_128(<(u128, u128)>::into((r, result_0)), denom_low); + let (_, q0): (u128, u128) = split_u256_into_u128(q0); + + <(u128, u128)>::into((q1, q0)) + }, + false => algorithm_d_512_256(result_high, result_low, denominator), + } + }, + } + }, + } +} diff --git a/simf/lib/u32/mul_div.simf b/simf/lib/u32/mul_div.simf new file mode 100644 index 0000000..8e4bc43 --- /dev/null +++ b/simf/lib/u32/mul_div.simf @@ -0,0 +1,12 @@ +use crate::lib::u32::convert::u32_to_u64; +use crate::lib::u64::convert::safe_u64_to_u32; + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u32 +pub fn mul_div_32(a: u32, b: u32, denominator: u32) -> u32 { + let denominator_64: u64 = u32_to_u64(denominator); + + let result_64: u64 = jet::divide_64(jet::multiply_32(a, b), denominator_64); + + safe_u64_to_u32(result_64) +} diff --git a/simf/lib/u64/mul_div.simf b/simf/lib/u64/mul_div.simf new file mode 100644 index 0000000..367630a --- /dev/null +++ b/simf/lib/u64/mul_div.simf @@ -0,0 +1,13 @@ +use crate::lib::u64::convert::u64_to_u128; +use crate::lib::u128::convert::safe_u128_to_u64; +use crate::lib::u128::math::div_128; + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u64 +pub fn mul_div_64(a: u64, b: u64, denominator: u64) -> u64 { + let denominator_128: u128 = u64_to_u128(denominator); + + let result_128: u128 = div_128(jet::multiply_64(a, b), denominator_128); + + safe_u128_to_u64(result_128) +} diff --git a/simf/lib/u8/mul_div.simf b/simf/lib/u8/mul_div.simf new file mode 100644 index 0000000..222f53b --- /dev/null +++ b/simf/lib/u8/mul_div.simf @@ -0,0 +1,12 @@ +use crate::lib::u8::convert::u8_to_u16; +use crate::lib::u16::convert::safe_u16_to_u8; + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u8 +pub fn mul_div_8(a: u8, b: u8, denominator: u8) -> u8 { + let denominator_16: u16 = u8_to_u16(denominator); + + let result_16: u16 = jet::divide_16(jet::multiply_8(a, b), denominator_16); + + safe_u16_to_u8(result_16) +} diff --git a/simf/u128_mul_div_test.simf b/simf/u128_mul_div_test.simf new file mode 100644 index 0000000..614349a --- /dev/null +++ b/simf/u128_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u128::mul_div::mul_div_128; + +use crate::lib::asserts::assert_eq_128; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u128 = witness::FIRST_ARG; + let b: u128 = witness::SECOND_ARG; + let c: u128 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_128(mul_div_128(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/simf/u16_mul_div_test.simf b/simf/u16_mul_div_test.simf new file mode 100644 index 0000000..646fde3 --- /dev/null +++ b/simf/u16_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u16::mul_div::mul_div_16; + +use crate::lib::asserts::assert_eq_16; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u16 = witness::FIRST_ARG; + let b: u16 = witness::SECOND_ARG; + let c: u16 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_16(mul_div_16(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/simf/u256_mul_div_test.simf b/simf/u256_mul_div_test.simf new file mode 100644 index 0000000..aeddd10 --- /dev/null +++ b/simf/u256_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u256::mul_div::mul_div_256; + +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 c: u256 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_256(mul_div_256(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/simf/u256_test_add.simf b/simf/u256_test_add.simf index 1c06f46..5f210af 100644 --- a/simf/u256_test_add.simf +++ b/simf/u256_test_add.simf @@ -1,4 +1,4 @@ -use crate::lib::u256::math::{add_256, add_256_128}; +use crate::lib::u256::math::{add_256, add_256_128, full_add_256}; use crate::lib::asserts::assert_eq_256; use crate::helper::{if_test_this_function, assert_bool}; @@ -17,11 +17,31 @@ fn main() { let a: u256 = witness::FIRST_ARG; let b: u256 = witness::SECOND_ARG; + let carry_in: bool = witness::THIRD_ARG; let expected: Option = witness::EXPECTED; let expected_bool: bool = witness::EXPECTED_BOOL; /// Arithmetic - 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 => (), }; + 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 => (), + }; + + match if_test_this_function(2, fn_idx) { + true => { + assert_eq_uint_bool(full_add_256(carry_in, a, b), unwrap(expected), expected_bool); + }, + false => (), + }; } diff --git a/simf/u256_test_div.simf b/simf/u256_test_div.simf index 7c895b2..854a857 100644 --- a/simf/u256_test_div.simf +++ b/simf/u256_test_div.simf @@ -1,4 +1,11 @@ -use crate::lib::u256::math::{div_mod_256_64, div_mod_256_128, div_mod_256, div_256}; +use crate::lib::u256::math::{ + calculate_normalizer_base_128, + div_mod_256_64, + algorithm_d_256_128, + 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,40 +22,64 @@ fn main() { /// Arithmetic match if_test_this_function(0, fn_idx) { + true => { + let result: u128 = calculate_normalizer_base_128(a); + + let (_, expected_r): (u128, u128) = ::into(unwrap(expected)); + + assert_eq_128(result, expected_r); + }, + false => (), + }; + + match if_test_this_function(1, fn_idx) { true => { 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); - assert_eq_256(q, unwrap(expected)); - assert!(jet::eq_64(r, expected_r)); + assert_eq_256(q, unwrap(expected)); + assert!(jet::eq_64(r, expected_r)); }, false => (), }; - match if_test_this_function(1, fn_idx) { + match if_test_this_function(2, fn_idx) { + true => { + let (_, b): (u128, u128) = ::into(b); + let (_, expected_r): (u128, u128) = ::into(second_expected); + + let (q, r): (u256, u128) = algorithm_d_256_128(a, b); + + assert_eq_256(q, unwrap(expected)); + assert_eq_128(r, expected_r); + }, + false => (), + }; + + match if_test_this_function(3, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); let (_, expected_r): (u128, u128) = ::into(second_expected); let (q, r): (u256, u128) = div_mod_256_128(a, b); - assert_eq_256(q, unwrap(expected)); - assert_eq_128(r, expected_r); + assert_eq_256(q, unwrap(expected)); + assert_eq_128(r, expected_r); }, false => (), }; - match if_test_this_function(2, fn_idx) { + match if_test_this_function(4, 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); + 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 => (), }; + match if_test_this_function(5, fn_idx) { true => { assert_eq_256(div_256(a, b), unwrap(expected)); }, false => (), }; } diff --git a/simf/u256_test_sub_mul.simf b/simf/u256_test_sub_mul.simf index aa6b673..bbadd85 100644 --- a/simf/u256_test_sub_mul.simf +++ b/simf/u256_test_sub_mul.simf @@ -1,4 +1,11 @@ -use crate::lib::u256::math::{sub_256, mul_256, mul_256_64, mul_256_128}; +use crate::lib::u256::math::{ + sub_256, + mul_256, + mul_256_64, + mul_256_128, + mul_512_128, + safe_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}; @@ -18,14 +25,21 @@ fn main() { let a: u256 = witness::FIRST_ARG; let b: u256 = witness::SECOND_ARG; + let c: u128 = witness::THIRD_ARG; let expected: Option = witness::EXPECTED; let expected_bool: bool = witness::EXPECTED_BOOL; let second_expected: u256 = witness::SECOND_EXPECTED; + let third_expected: u256 = witness::THIRD_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(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 => { @@ -62,4 +76,27 @@ fn main() { }, false => (), }; + + match if_test_this_function(4, fn_idx) { + true => { + let (result_2, result_1, result_0): (u128, u256, u256) = mul_512_128(a, b, c); + + let (_, expected): (u128, u128) = ::into(unwrap(expected)); + + assert_eq_128(result_2, expected); + assert_eq_256(result_1, second_expected); + assert_eq_256(result_0, third_expected); + }, + false => (), + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, b): (u128, u128) = ::into(b); + let result: u256 = safe_mul_256_128(a, b); + + assert_eq_256(result, unwrap(expected)); + }, + false => (), + }; } diff --git a/simf/u32_mul_div_test.simf b/simf/u32_mul_div_test.simf new file mode 100644 index 0000000..b41fb21 --- /dev/null +++ b/simf/u32_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u32::mul_div::mul_div_32; + +use crate::lib::asserts::assert_eq_32; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u32 = witness::FIRST_ARG; + let b: u32 = witness::SECOND_ARG; + let c: u32 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_32(mul_div_32(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/simf/u64_mul_div_test.simf b/simf/u64_mul_div_test.simf new file mode 100644 index 0000000..fe99df2 --- /dev/null +++ b/simf/u64_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u64::mul_div::mul_div_64; + +use crate::lib::asserts::assert_eq_64; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u64 = witness::FIRST_ARG; + let b: u64 = witness::SECOND_ARG; + let c: u64 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_64(mul_div_64(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/simf/u8_mul_div_test.simf b/simf/u8_mul_div_test.simf new file mode 100644 index 0000000..0cb508d --- /dev/null +++ b/simf/u8_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u8::mul_div::mul_div_8; + +use crate::lib::asserts::assert_eq_8; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u8 = witness::FIRST_ARG; + let b: u8 = witness::SECOND_ARG; + let c: u8 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_8(mul_div_8(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/tests/u128_basic_math_test.rs b/tests/u128_basic_math_test.rs index 77dd7b3..5d0b4ab 100644 --- a/tests/u128_basic_math_test.rs +++ b/tests/u128_basic_math_test.rs @@ -985,7 +985,7 @@ mod u128_tests_arithmetic { } #[simplex::test] - fn u128_test_div_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + fn u128_test_div_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = 0; @@ -996,11 +996,11 @@ mod u128_tests_arithmetic { op(FunctionToTest::Div128), a, b, - Some(DEFAULT_EXPECTED), + Some(0), DEFAULT_BOOL, DEFAULT_EXPECTED, ), - Expect::AssertFailed, + Expect::Ok, ) } } diff --git a/tests/u128_mul_div_test.rs b/tests/u128_mul_div_test.rs new file mode 100644 index 0000000..5abd937 --- /dev/null +++ b/tests/u128_mul_div_test.rs @@ -0,0 +1,108 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u128_mul_div_test::U128MulDivTestProgram; +use simplicityhl_std::artifacts::u128_mul_div_test::derived_u128_mul_div_test::{ + U128MulDivTestArguments, U128MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: u128 = 0; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U128MulDivTestProgram { + U128MulDivTestProgram::new(&U128MulDivTestArguments {}) +} + +fn build_witness( + op: u8, + a: u128, + b: u128, + c: u128, + expected: Option, +) -> U128MulDivTestWitness { + U128MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +mod u128_mul_div_test { + use super::*; + + #[simplex::test] + fn u128_test_mul_div_128_product_is_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX) as u128; + let b = rand::thread_rng().gen_range(0..=u64::MAX) as u128; + let c = rand::thread_rng().gen_range(1..=u128::MAX); + + let res = a * b / c; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_mul_div_128_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u128::MAX); + let b = u128::MAX; + let c = rand::thread_rng().gen_range(a..=u128::MAX); + + let res = U256::from(a) * U256::from(b) / U256::from(c); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res.low_u128())), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_mul_div_128_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u128::MAX); + let b = u128::MAX; + let c = rand::thread_rng().gen_range(1..a); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u128_test_mul_div_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + let b = rand::thread_rng().gen_range(1..=u128::MAX); + let c = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), + Expect::Ok, + ) + } +} diff --git a/tests/u16_mul_div_test.rs b/tests/u16_mul_div_test.rs new file mode 100644 index 0000000..eb18073 --- /dev/null +++ b/tests/u16_mul_div_test.rs @@ -0,0 +1,100 @@ +mod common; + +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::u16_mul_div_test::U16MulDivTestProgram; +use simplicityhl_std::artifacts::u16_mul_div_test::derived_u16_mul_div_test::{ + U16MulDivTestArguments, U16MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: u16 = 0; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U16MulDivTestProgram { + U16MulDivTestProgram::new(&U16MulDivTestArguments {}) +} + +fn build_witness(op: u8, a: u16, b: u16, c: u16, expected: Option) -> U16MulDivTestWitness { + U16MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +mod u16_mul_div_test { + use super::*; + + #[simplex::test] + fn u16_test_mul_div_16_product_is_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX) as u16; + let b = rand::thread_rng().gen_range(0..=u8::MAX) as u16; + let c = rand::thread_rng().gen_range(1..=u16::MAX); + + let res = a * b / c; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u16_test_mul_div_16_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u16::MAX); + let b = u16::MAX; + let c = rand::thread_rng().gen_range(a..=u16::MAX); + + let res = (a as u32) * (b as u32) / (c as u32); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u16)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u16_test_mul_div_16_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u16::MAX); + let b = u16::MAX; + let c = rand::thread_rng().gen_range(1..a); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u16_test_mul_div_16_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u16::MAX); + let b = rand::thread_rng().gen_range(1..=u16::MAX); + let c = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), + Expect::Ok, + ) + } +} diff --git a/tests/u256_mul_div_test.rs b/tests/u256_mul_div_test.rs new file mode 100644 index 0000000..5723c8a --- /dev/null +++ b/tests/u256_mul_div_test.rs @@ -0,0 +1,346 @@ +mod common; + +use primitive_types::U256; +use std::cmp::max; +use std::ops::Div; + +use crate::common::core::{Expect, run}; +use crate::common::helper::generate_u256; + +use simplicityhl_std::artifacts::u256_mul_div_test::U256MulDivTestProgram; +use simplicityhl_std::artifacts::u256_mul_div_test::derived_u256_mul_div_test::{ + U256MulDivTestArguments, U256MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U256MulDivTestProgram { + U256MulDivTestProgram::new(&U256MulDivTestArguments {}) +} + +fn build_witness( + op: u8, + a: [u8; 32], + b: [u8; 32], + c: [u8; 32], + expected: Option<[u8; 32]>, +) -> U256MulDivTestWitness { + U256MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +fn safe_u512_to_u256(a: [u8; 64]) -> [u8; 32] { + let high = U256::from_big_endian(&a[0..32]); + let low = U256::from_big_endian(&a[32..64]); + + assert!(high == U256::zero()); + + low.to_big_endian() +} + +mod u256_mul_div_test { + use super::*; + + #[simplex::test] + fn u256_test_mul_div_256_product_fits_into_u256( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u128::MAX)); + let b = generate_u256(U256::zero(), U256::from(u128::MAX)); + let c = generate_u256(U256::one(), U256::MAX); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(2), U256::MAX); + let b = U256::MAX; + let c = generate_u256(a, U256::MAX); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(2), U256::MAX); + let b = U256::MAX; + let c = generate_u256(U256::one(), a); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(DEFAULT_EXPECTED), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_remainder_is_zero( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let b = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let c = a; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(b.to_big_endian()), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_denominator_is_u128( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let c = generate_u256(b, U256::from(u128::MAX)); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_min_denom_high(context: simplex::TestContext) -> anyhow::Result<()> { + let pow129: U256 = U256::from(2).pow(U256::from(129)); + + let a = generate_u256(U256::from(u128::MAX) + 1, pow129); + let b = generate_u256(U256::from(u128::MAX) + 1, pow129); + let c = generate_u256(U256::from(u128::MAX) + 1, pow129 - 1); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::MAX); + let c = U256::zero(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some([0; 32]), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_algorithm_d_512_256_check( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let pow2_128: U256 = U256::from(u128::MAX) + 1; + + let a = generate_u256(pow2_128, U256::MAX); + let b = generate_u256(pow2_128, U256::MAX - pow2_128); + + let product = a.full_mul(b); + let result_high = + U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); + + let c = generate_u256(max(U256::from(u128::MAX) + 1, result_high + 1), U256::MAX); + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_algorithm_d_512_256_c_is_res_high( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let pow2_128: U256 = U256::from(u128::MAX) + 1; + + let a: U256 = generate_u256(pow2_128, U256::MAX); + let b = generate_u256(pow2_128, U256::MAX - pow2_128); + + let product = a.full_mul(b); + let result_high = + U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); + + let c = result_high + 1; + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_normalize_to_threshold_512_127_norm_is_1( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let pow2_128: U256 = U256::from(u128::MAX) + 1; + + let a: U256 = generate_u256(pow2_128, U256::MAX); + let b = generate_u256(pow2_128, U256::MAX - pow2_128); + + let product = a.full_mul(b); + let result_high = + U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); + + let c = generate_u256( + max(U256::from(2).pow(U256::from(255)), result_high + 1), + U256::MAX, + ); + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_normalize_to_threshold_512_127_norm_greater_than_1( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let b = generate_u256( + U256::from(u128::MAX) + 1, + U256::from(2).pow(U256::from(192)), + ); + + let product = a.full_mul(b); + let result_high = + U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))) + 1; + + let c = generate_u256( + max(U256::from(u128::MAX) + 1, result_high), + U256::from(2).pow(U256::from(255)) - 1, + ); + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } +} diff --git a/tests/u256_test_add.rs b/tests/u256_test_add.rs index 2e5bc44..bdda2ef 100644 --- a/tests/u256_test_add.rs +++ b/tests/u256_test_add.rs @@ -2,7 +2,7 @@ mod common; use primitive_types::U256; -use crate::common::helper::generate_u256; +use crate::common::helper::{DEFAULT_BOOL, generate_u256}; use common::core::{Expect, run}; use simplicityhl_std::artifacts::u256_test_add::U256TestAddProgram; @@ -13,6 +13,7 @@ use simplicityhl_std::artifacts::u256_test_add::derived_u256_test_add::{ enum FunctionToTest { Add256, Add256_128, + FullAdd256, } #[inline] @@ -28,6 +29,7 @@ fn build_witness( function: u8, a: [u8; 32], b: [u8; 32], + c: bool, expected: Option<[u8; 32]>, expected_bool: bool, ) -> U256TestAddWitness { @@ -35,6 +37,7 @@ fn build_witness( function_index: function, first_arg: a, second_arg: b, + third_arg: c, expected, expected_bool, } @@ -56,6 +59,7 @@ mod u256_tests_arithmetic { op(FunctionToTest::Add256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_BOOL, Some(result), false, ), @@ -76,6 +80,7 @@ mod u256_tests_arithmetic { op(FunctionToTest::Add256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_BOOL, Some(result), true, ), @@ -96,6 +101,7 @@ mod u256_tests_arithmetic { op(FunctionToTest::Add256_128), a.to_big_endian(), b.to_big_endian(), + DEFAULT_BOOL, Some(result), false, ), @@ -116,10 +122,115 @@ mod u256_tests_arithmetic { op(FunctionToTest::Add256_128), a.to_big_endian(), b.to_big_endian(), + DEFAULT_BOOL, Some(result), true, ), Expect::Ok, ) } + + #[simplex::test] + fn u256_test_full_add_256_not_overflow_carry_low_false( + 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(); + let result_carry = false; + let carry_low = false; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd256), + a.to_big_endian(), + b.to_big_endian(), + carry_low, + Some(result), + result_carry, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_full_add_256_overflow_carry_low_false( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::MAX); + + let result = (b - 1).to_big_endian(); + let result_carry = true; + let carry_low = false; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd256), + a.to_big_endian(), + b.to_big_endian(), + carry_low, + Some(result), + result_carry, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_full_add_256_not_overflow_carry_low_true( + 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 + 1).to_big_endian(); + let result_carry = false; + let carry_low = true; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd256), + a.to_big_endian(), + b.to_big_endian(), + carry_low, + Some(result), + result_carry, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_full_add_256_overflow_carry_low_true( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + let result = b; + let result_carry = true; + let carry_low = true; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd256), + a.to_big_endian(), + b, + carry_low, + Some(result), + result_carry, + ), + Expect::Ok, + ) + } } diff --git a/tests/u256_test_div.rs b/tests/u256_test_div.rs index 9c36ca9..d74a0ba 100644 --- a/tests/u256_test_div.rs +++ b/tests/u256_test_div.rs @@ -11,7 +11,9 @@ use simplicityhl_std::artifacts::u256_test_div::derived_u256_test_div::{ }; enum FunctionToTest { + CalculateNormalizerBase128, DivMod256_64, + AlgorithmD256_128, DivMod256_128, DivMod256, Div256, @@ -47,6 +49,132 @@ fn build_witness( mod u256_tests_arithmetic { use super::*; + #[simplex::test] + fn u256_test_calculate_normalizer_base_128( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let a_high = (a >> 128).as_u128(); + + let norm = threshold.div_ceil(a_high); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::CalculateNormalizerBase128), + a.to_big_endian(), + DEFAULT_EXPECTED, + Some(U256::from(norm).to_big_endian()), + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_calculate_normalizer_base_128_norm_is_1( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + // a >= 2^255 keeps a_high >= 2^127, so the divisor is already normalized + let a = generate_u256(U256::from(2).pow(U256::from(255)), U256::MAX); + let a_high = (a >> 128).as_u128(); + + let norm = threshold.div_ceil(a_high); + assert_eq!(norm, 1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::CalculateNormalizerBase128), + a.to_big_endian(), + DEFAULT_EXPECTED, + Some(U256::from(norm).to_big_endian()), + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_calculate_normalizer_base_128_norm_greater_than_1( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + // a < 2^255 keeps a_high < 2^127, so the divisor has to be scaled up + let a = generate_u256( + U256::from(u128::MAX) + 1, + U256::from(2).pow(U256::from(255)) - 1, + ); + let a_high = (a >> 128).as_u128(); + + let norm = threshold.div_ceil(a_high); + assert!(norm > 1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::CalculateNormalizerBase128), + a.to_big_endian(), + DEFAULT_EXPECTED, + Some(U256::from(norm).to_big_endian()), + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_calculate_normalizer_base_128_a_is_u128_fail( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + let a = generate_u256(U256::one(), U256::from(threshold) - 1); + + let norm: u128 = threshold.div_ceil(a.low_u128()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::CalculateNormalizerBase128), + a.to_big_endian(), + DEFAULT_EXPECTED, + Some(U256::from(norm).to_big_endian()), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_test_calculate_normalizer_base_128_b_is_zero_fail( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::CalculateNormalizerBase128), + a, + DEFAULT_EXPECTED, + Some(DEFAULT_EXPECTED), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + #[simplex::test] fn test_div_mod_256_64(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); @@ -88,6 +216,86 @@ mod u256_tests_arithmetic { ) } + #[simplex::test] + fn test_algorithm_d_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::AlgorithmD256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_algorithm_d_256_128_fail_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::AlgorithmD256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn test_algorithm_d_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::AlgorithmD256_128), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn test_algorithm_d_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::AlgorithmD256_128), a, a, Some(q), r), + Expect::Ok, + ) + } + #[simplex::test] fn test_div_mod_256_128(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); @@ -381,7 +589,7 @@ mod u256_tests_arithmetic { } #[simplex::test] - fn u256_test_div_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + fn u256_test_div_256_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); let b = [0; 32]; @@ -392,10 +600,10 @@ mod u256_tests_arithmetic { op(FunctionToTest::Div256), a.to_big_endian(), b, - Some(DEFAULT_EXPECTED), + Some([0; 32]), DEFAULT_EXPECTED, ), - Expect::AssertFailed, + Expect::Ok, ) } } diff --git a/tests/u256_test_sub_mul.rs b/tests/u256_test_sub_mul.rs index 7c164a5..ccb3963 100644 --- a/tests/u256_test_sub_mul.rs +++ b/tests/u256_test_sub_mul.rs @@ -1,6 +1,7 @@ mod common; use primitive_types::U256; +use primitive_types::U512; use rand::Rng; use crate::common::helper::{DEFAULT_BOOL, generate_u256}; @@ -16,6 +17,8 @@ enum FunctionToTest { Mul256, Mul256_64, Mul256_128, + Mul512_128, + SafeMul256_128, } #[inline] @@ -24,6 +27,7 @@ fn op(o: FunctionToTest) -> u8 { } const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; +const DEFAULT_U128: u128 = 0; fn program() -> U256TestSubMulProgram { U256TestSubMulProgram::new(&U256TestSubMulArguments {}) @@ -33,17 +37,21 @@ fn build_witness( function: u8, a: [u8; 32], b: [u8; 32], + c: u128, expected: Option<[u8; 32]>, expected_bool: bool, second_expected: [u8; 32], + third_expected: [u8; 32], ) -> U256TestSubMulWitness { U256TestSubMulWitness { function_index: function, first_arg: a, second_arg: b, + third_arg: c, expected, expected_bool, second_expected, + third_expected, } } @@ -70,9 +78,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result), false, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -89,9 +99,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a, a, + DEFAULT_U128, Some([0; 32]), false, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -114,9 +126,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result.to_big_endian()), carry, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -141,9 +155,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result.to_big_endian()), carry, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -161,9 +177,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a, b.to_big_endian(), + DEFAULT_U128, Some(a), false, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -182,9 +200,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result.to_big_endian()), true, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -205,9 +225,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Mul256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result_high), DEFAULT_BOOL, result_low, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -228,9 +250,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Mul256_64), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result_high), DEFAULT_BOOL, result_low, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -251,11 +275,92 @@ mod u256_tests_arithmetic { op(FunctionToTest::Mul256_128), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result_high), DEFAULT_BOOL, result_low, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_512_128(context: simplex::TestContext) -> anyhow::Result<()> { + let a_1 = generate_u256(U256::one(), U256::MAX); + let a_0 = generate_u256(U256::one(), U256::MAX); + let b = rand::thread_rng().gen_range(1..=u128::MAX); + + let result_low = U512::from(a_0) * U512::from(b); + let result_high = U512::from(a_1) * U512::from(b); + + let (res_1, res_0) = split_u512(result_low.to_big_endian()); + let (res_3, res_2) = split_u512(result_high.to_big_endian()); + + let res_2_1 = U512::from_big_endian(&res_1) + U512::from_big_endian(&res_2); + let (res_3_1, res_2_1) = split_u512(res_2_1.to_big_endian()); + + let res_3_final = + (U256::from_big_endian(&res_3_1) + U256::from_big_endian(&res_3)).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Mul512_128), + a_1.to_big_endian(), + a_0.to_big_endian(), + b, + Some(res_3_final), + DEFAULT_BOOL, + res_2_1, + res_0, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_safe_mul_256_128_fitting(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u128::MAX)); + let b = generate_u256(U256::zero(), U256::from(u128::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeMul256_128), + a.to_big_endian(), + b.to_big_endian(), + DEFAULT_U128, + Some((a * b).to_big_endian()), + DEFAULT_BOOL, + DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) } + + #[simplex::test] + fn u256_test_safe_mul_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let b = U256::from(u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeMul256_128), + a.to_big_endian(), + b.to_big_endian(), + DEFAULT_U128, + Some(DEFAULT_EXPECTED), + DEFAULT_BOOL, + DEFAULT_EXPECTED, + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } } diff --git a/tests/u32_mul_div_test.rs b/tests/u32_mul_div_test.rs new file mode 100644 index 0000000..2487f08 --- /dev/null +++ b/tests/u32_mul_div_test.rs @@ -0,0 +1,100 @@ +mod common; + +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::u32_mul_div_test::U32MulDivTestProgram; +use simplicityhl_std::artifacts::u32_mul_div_test::derived_u32_mul_div_test::{ + U32MulDivTestArguments, U32MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: u32 = 0; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U32MulDivTestProgram { + U32MulDivTestProgram::new(&U32MulDivTestArguments {}) +} + +fn build_witness(op: u8, a: u32, b: u32, c: u32, expected: Option) -> U32MulDivTestWitness { + U32MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +mod u32_mul_div_test { + use super::*; + + #[simplex::test] + fn u32_test_mul_div_32_product_is_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX) as u32; + let b = rand::thread_rng().gen_range(0..=u16::MAX) as u32; + let c = rand::thread_rng().gen_range(1..=u32::MAX); + + let res = a * b / c; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_test_mul_div_32_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u32::MAX); + let b = u32::MAX; + let c = rand::thread_rng().gen_range(a..=u32::MAX); + + let res = (a as u64) * (b as u64) / (c as u64); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u32)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_test_mul_div_32_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u32::MAX); + let b = u32::MAX; + let c = rand::thread_rng().gen_range(1..a); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u32_test_mul_div_32_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u32::MAX); + let b = rand::thread_rng().gen_range(1..=u32::MAX); + let c = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), + Expect::Ok, + ) + } +} diff --git a/tests/u64_mul_div_test.rs b/tests/u64_mul_div_test.rs new file mode 100644 index 0000000..bbd17d3 --- /dev/null +++ b/tests/u64_mul_div_test.rs @@ -0,0 +1,100 @@ +mod common; + +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::u64_mul_div_test::U64MulDivTestProgram; +use simplicityhl_std::artifacts::u64_mul_div_test::derived_u64_mul_div_test::{ + U64MulDivTestArguments, U64MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: u64 = 0; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U64MulDivTestProgram { + U64MulDivTestProgram::new(&U64MulDivTestArguments {}) +} + +fn build_witness(op: u8, a: u64, b: u64, c: u64, expected: Option) -> U64MulDivTestWitness { + U64MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +mod u64_mul_div_test { + use super::*; + + #[simplex::test] + fn u64_test_mul_div_64_product_is_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX) as u64; + let b = rand::thread_rng().gen_range(0..=u32::MAX) as u64; + let c = rand::thread_rng().gen_range(1..=u64::MAX); + + let res = a * b / c; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_test_mul_div_64_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u64::MAX); + let b = u64::MAX; + let c = rand::thread_rng().gen_range(a..=u64::MAX); + + let res = (a as u128) * (b as u128) / (c as u128); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u64)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_test_mul_div_64_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u64::MAX); + let b = u64::MAX; + let c = rand::thread_rng().gen_range(1..a); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u64_test_mul_div_64_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u64::MAX); + let b = rand::thread_rng().gen_range(1..=u64::MAX); + let c = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), + Expect::Ok, + ) + } +} diff --git a/tests/u8_math_tests.rs b/tests/u8_math_test.rs similarity index 100% rename from tests/u8_math_tests.rs rename to tests/u8_math_test.rs diff --git a/tests/u8_mul_div_test.rs b/tests/u8_mul_div_test.rs new file mode 100644 index 0000000..f18e46a --- /dev/null +++ b/tests/u8_mul_div_test.rs @@ -0,0 +1,100 @@ +mod common; + +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::u8_mul_div_test::U8MulDivTestProgram; +use simplicityhl_std::artifacts::u8_mul_div_test::derived_u8_mul_div_test::{ + U8MulDivTestArguments, U8MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: u8 = 0; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U8MulDivTestProgram { + U8MulDivTestProgram::new(&U8MulDivTestArguments {}) +} + +fn build_witness(op: u8, a: u8, b: u8, c: u8, expected: Option) -> U8MulDivTestWitness { + U8MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +mod u8_mul_div_test { + use super::*; + + #[simplex::test] + fn u8_test_mul_div_8_product_is_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u8::MAX); + let b = u8::MAX / a; + let c = rand::thread_rng().gen_range(1..=u8::MAX); + + let res = a * b / c; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u8_test_mul_div_8_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u8::MAX); + let b = u8::MAX; + let c = rand::thread_rng().gen_range(a..=u8::MAX); + + let res = (a as u16) * (b as u16) / (c as u16); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u8)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u8_test_mul_div_8_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u8::MAX); + let b = u8::MAX; + let c = rand::thread_rng().gen_range(1..a); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u8_test_mul_div_8_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u8::MAX); + let b = rand::thread_rng().gen_range(1..=u8::MAX); + let c = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), + Expect::Ok, + ) + } +} From 99f24499d3f6830d6f472a368b9715435b4e21b6 Mon Sep 17 00:00:00 2001 From: Yevhenii Sekhin Date: Wed, 16 Sep 2026 13:46:48 +0300 Subject: [PATCH 02/11] Refactor tests structure in SimplicityHL and Rust code (#55) * fix: pin specific Simplex version for tests.yml * refactor: change simf tests structure * fmt: apply format and add chech into tests.yml file * fmt: apply simfmt to all files * refactor: dedup some of simf/tests functions * refactor: rust part and organize everything by modules * refactor(rename): tests/stdlib functions * refactor: better organization for stdlib tests * refactor: separate lint from tests in workflows * refactor: separate crate::lib from crate::tests --- .github/workflows/lint.yml | 61 + .github/workflows/tests.yml | 3 +- CONTRIBUTING.md | 2 +- simf/asserts_test.simf | 62 - simf/binary_test.simf | 21 - simf/helper.simf | 13 - simf/lib/binary.simf | 8 +- simf/lib/op_return.simf | 2 +- simf/lib/secp256k1/operations.simf | 2 +- simf/lib/u128/bit.simf | 18 +- simf/lib/u128/math.simf | 19 +- simf/lib/u16/math.simf | 2 +- simf/lib/u256/bit.simf | 18 +- simf/lib/u256/math.simf | 8 +- simf/lib/u256/mul_div.simf | 43 +- simf/lib/u64/math.simf | 2 +- simf/op_return_test.simf | 12 - simf/secp256k1_operations_test.simf | 35 - simf/tests/asserts.simf | 138 +++ simf/tests/binary.simf | 21 + simf/tests/op_return.simf | 24 + simf/tests/secp256k1/operations.simf | 98 ++ simf/tests/support/dispatch.simf | 6 + simf/tests/support/expect.simf | 136 +++ .../u1/convert.simf} | 18 +- .../u128/bit.simf} | 39 +- simf/tests/u128/comparison.simf | 33 + .../u128/convert.simf} | 23 +- simf/tests/u128/math/api.simf | 95 ++ .../u128/math/primitives.simf} | 87 +- .../u128/mul_div.simf} | 6 +- .../u16/convert.simf} | 18 +- simf/tests/u16/math.simf | 93 ++ .../u16/mul_div.simf} | 6 +- .../u256/bit.simf} | 31 +- simf/tests/u256/comparison.simf | 33 + .../u256/convert.simf} | 25 +- simf/tests/u256/math/add.simf | 38 + simf/tests/u256/math/api.simf | 95 ++ .../u256/math/div.simf} | 20 +- .../u256/math/sub_mul.simf} | 26 +- .../u256/mul_div.simf} | 6 +- .../u32/convert.simf} | 20 +- simf/tests/u32/math.simf | 93 ++ .../u32/mul_div.simf} | 6 +- .../u64/convert.simf} | 21 +- simf/tests/u64/math.simf | 93 ++ .../u64/mul_div.simf} | 6 +- .../u8/convert.simf} | 18 +- simf/tests/u8/math.simf | 93 ++ .../u8/mul_div.simf} | 6 +- simf/u128_math_test.simf | 63 -- simf/u128_test_compare.simf | 20 - simf/u16_math_test.simf | 51 - simf/u256_math_test.simf | 63 -- simf/u256_test_add.simf | 47 - simf/u256_test_compare.simf | 16 - simf/u32_math_test.simf | 51 - simf/u64_math_test.simf | 62 - simf/u8_math_test.simf | 62 - tests/asserts_test.rs | 499 -------- tests/binary_test.rs | 18 - tests/helper_test.rs | 23 - tests/op_return_test.rs | 130 --- tests/secp256k1_operations_test.rs | 569 ---------- tests/stdlib/asserts.rs | 352 ++++++ tests/stdlib/binary.rs | 12 + tests/{ => stdlib}/common/core.rs | 0 tests/{ => stdlib}/common/helper.rs | 23 +- tests/{ => stdlib}/common/mod.rs | 0 tests/{ => stdlib}/common/u256_wrapper.rs | 2 +- tests/{ => stdlib}/common/uint.rs | 4 +- tests/stdlib/main.rs | 13 + tests/stdlib/op_return.rs | 131 +++ tests/stdlib/secp256k1/mod.rs | 1 + tests/stdlib/secp256k1/operations.rs | 411 +++++++ tests/stdlib/u1/convert.rs | 134 +++ tests/stdlib/u1/mod.rs | 1 + tests/stdlib/u128/bit.rs | 168 +++ tests/stdlib/u128/comparison.rs | 129 +++ tests/stdlib/u128/convert.rs | 217 ++++ .../u128/math/api.rs} | 18 +- tests/stdlib/u128/math/mod.rs | 2 + tests/stdlib/u128/math/primitives.rs | 645 +++++++++++ tests/stdlib/u128/mod.rs | 5 + tests/stdlib/u128/mul_div.rs | 109 ++ tests/stdlib/u16/convert.rs | 154 +++ .../{u16_math_test.rs => stdlib/u16/math.rs} | 18 +- tests/stdlib/u16/mod.rs | 3 + tests/stdlib/u16/mul_div.rs | 104 ++ tests/stdlib/u256/bit.rs | 161 +++ tests/stdlib/u256/comparison.rs | 138 +++ tests/stdlib/u256/convert.rs | 239 ++++ tests/stdlib/u256/math/add.rs | 195 ++++ .../u256/math/api.rs} | 20 +- tests/stdlib/u256/math/div.rs | 453 ++++++++ tests/stdlib/u256/math/mod.rs | 4 + tests/stdlib/u256/math/sub_mul.rs | 276 +++++ tests/stdlib/u256/mod.rs | 5 + tests/stdlib/u256/mul_div.rs | 262 +++++ tests/stdlib/u32/convert.rs | 175 +++ .../{u32_math_test.rs => stdlib/u32/math.rs} | 18 +- tests/stdlib/u32/mod.rs | 3 + tests/stdlib/u32/mul_div.rs | 107 ++ tests/stdlib/u64/convert.rs | 196 ++++ .../{u64_math_test.rs => stdlib/u64/math.rs} | 18 +- tests/stdlib/u64/mod.rs | 3 + tests/stdlib/u64/mul_div.rs | 104 ++ tests/stdlib/u8/convert.rs | 144 +++ tests/{u8_math_test.rs => stdlib/u8/math.rs} | 18 +- tests/stdlib/u8/mod.rs | 3 + tests/stdlib/u8/mul_div.rs | 104 ++ tests/u128_basic_math_test.rs | 1006 ----------------- tests/u128_convert_test.rs | 295 ----- tests/u128_mul_div_test.rs | 108 -- tests/u128_test_bits.rs | 233 ---- tests/u128_test_compare.rs | 142 --- tests/u16_convert_test.rs | 190 ---- tests/u16_mul_div_test.rs | 100 -- tests/u1_convert_test.rs | 150 --- tests/u256_convert_test.rs | 331 ------ tests/u256_mul_div_test.rs | 346 ------ tests/u256_test_add.rs | 236 ---- tests/u256_test_bits.rs | 198 ---- tests/u256_test_compare.rs | 166 --- tests/u256_test_div.rs | 609 ---------- tests/u256_test_sub_mul.rs | 366 ------ tests/u32_convert_test.rs | 225 ---- tests/u32_mul_div_test.rs | 100 -- tests/u64_convert_test.rs | 260 ----- tests/u64_mul_div_test.rs | 100 -- tests/u8_convert_test.rs | 170 --- tests/u8_mul_div_test.rs | 100 -- 133 files changed, 6685 insertions(+), 7524 deletions(-) create mode 100644 .github/workflows/lint.yml delete mode 100644 simf/asserts_test.simf delete mode 100644 simf/binary_test.simf delete mode 100644 simf/helper.simf delete mode 100644 simf/op_return_test.simf delete mode 100644 simf/secp256k1_operations_test.simf create mode 100644 simf/tests/asserts.simf create mode 100644 simf/tests/binary.simf create mode 100644 simf/tests/op_return.simf create mode 100644 simf/tests/secp256k1/operations.simf create mode 100644 simf/tests/support/dispatch.simf create mode 100644 simf/tests/support/expect.simf rename simf/{u1_convert_test.simf => tests/u1/convert.simf} (83%) rename simf/{u128_test_bits.simf => tests/u128/bit.simf} (51%) create mode 100644 simf/tests/u128/comparison.simf rename simf/{u128_convert_test.simf => tests/u128/convert.simf} (92%) create mode 100644 simf/tests/u128/math/api.simf rename simf/{u128_basic_math_test.simf => tests/u128/math/primitives.simf} (54%) rename simf/{u128_mul_div_test.simf => tests/u128/mul_div.simf} (83%) rename simf/{u16_convert_test.simf => tests/u16/convert.simf} (84%) create mode 100644 simf/tests/u16/math.simf rename simf/{u16_mul_div_test.simf => tests/u16/mul_div.simf} (83%) rename simf/{u256_test_bits.simf => tests/u256/bit.simf} (62%) create mode 100644 simf/tests/u256/comparison.simf rename simf/{u256_convert_test.simf => tests/u256/convert.simf} (95%) create mode 100644 simf/tests/u256/math/add.simf create mode 100644 simf/tests/u256/math/api.simf rename simf/{u256_test_div.simf => tests/u256/math/div.simf} (84%) rename simf/{u256_test_sub_mul.simf => tests/u256/math/sub_mul.simf} (75%) rename simf/{u256_mul_div_test.simf => tests/u256/mul_div.simf} (83%) rename simf/{u32_convert_test.simf => tests/u32/convert.simf} (86%) create mode 100644 simf/tests/u32/math.simf rename simf/{u32_mul_div_test.simf => tests/u32/mul_div.simf} (83%) rename simf/{u64_convert_test.simf => tests/u64/convert.simf} (89%) create mode 100644 simf/tests/u64/math.simf rename simf/{u64_mul_div_test.simf => tests/u64/mul_div.simf} (83%) rename simf/{u8_convert_test.simf => tests/u8/convert.simf} (87%) create mode 100644 simf/tests/u8/math.simf rename simf/{u8_mul_div_test.simf => tests/u8/mul_div.simf} (82%) delete mode 100644 simf/u128_math_test.simf delete mode 100644 simf/u128_test_compare.simf delete mode 100644 simf/u16_math_test.simf delete mode 100644 simf/u256_math_test.simf delete mode 100644 simf/u256_test_add.simf delete mode 100644 simf/u256_test_compare.simf delete mode 100644 simf/u32_math_test.simf delete mode 100644 simf/u64_math_test.simf delete mode 100644 simf/u8_math_test.simf delete mode 100644 tests/asserts_test.rs delete mode 100644 tests/binary_test.rs delete mode 100644 tests/helper_test.rs delete mode 100644 tests/op_return_test.rs delete mode 100644 tests/secp256k1_operations_test.rs create mode 100644 tests/stdlib/asserts.rs create mode 100644 tests/stdlib/binary.rs rename tests/{ => stdlib}/common/core.rs (100%) rename tests/{ => stdlib}/common/helper.rs (50%) rename tests/{ => stdlib}/common/mod.rs (100%) rename tests/{ => stdlib}/common/u256_wrapper.rs (98%) rename tests/{ => stdlib}/common/uint.rs (99%) create mode 100644 tests/stdlib/main.rs create mode 100644 tests/stdlib/op_return.rs create mode 100644 tests/stdlib/secp256k1/mod.rs create mode 100644 tests/stdlib/secp256k1/operations.rs create mode 100644 tests/stdlib/u1/convert.rs create mode 100644 tests/stdlib/u1/mod.rs create mode 100644 tests/stdlib/u128/bit.rs create mode 100644 tests/stdlib/u128/comparison.rs create mode 100644 tests/stdlib/u128/convert.rs rename tests/{u128_math_test.rs => stdlib/u128/math/api.rs} (64%) create mode 100644 tests/stdlib/u128/math/mod.rs create mode 100644 tests/stdlib/u128/math/primitives.rs create mode 100644 tests/stdlib/u128/mod.rs create mode 100644 tests/stdlib/u128/mul_div.rs create mode 100644 tests/stdlib/u16/convert.rs rename tests/{u16_math_test.rs => stdlib/u16/math.rs} (63%) create mode 100644 tests/stdlib/u16/mod.rs create mode 100644 tests/stdlib/u16/mul_div.rs create mode 100644 tests/stdlib/u256/bit.rs create mode 100644 tests/stdlib/u256/comparison.rs create mode 100644 tests/stdlib/u256/convert.rs create mode 100644 tests/stdlib/u256/math/add.rs rename tests/{u256_math_test.rs => stdlib/u256/math/api.rs} (69%) create mode 100644 tests/stdlib/u256/math/div.rs create mode 100644 tests/stdlib/u256/math/mod.rs create mode 100644 tests/stdlib/u256/math/sub_mul.rs create mode 100644 tests/stdlib/u256/mod.rs create mode 100644 tests/stdlib/u256/mul_div.rs create mode 100644 tests/stdlib/u32/convert.rs rename tests/{u32_math_test.rs => stdlib/u32/math.rs} (63%) create mode 100644 tests/stdlib/u32/mod.rs create mode 100644 tests/stdlib/u32/mul_div.rs create mode 100644 tests/stdlib/u64/convert.rs rename tests/{u64_math_test.rs => stdlib/u64/math.rs} (63%) create mode 100644 tests/stdlib/u64/mod.rs create mode 100644 tests/stdlib/u64/mul_div.rs create mode 100644 tests/stdlib/u8/convert.rs rename tests/{u8_math_test.rs => stdlib/u8/math.rs} (63%) create mode 100644 tests/stdlib/u8/mod.rs create mode 100644 tests/stdlib/u8/mul_div.rs delete mode 100644 tests/u128_basic_math_test.rs delete mode 100644 tests/u128_convert_test.rs delete mode 100644 tests/u128_mul_div_test.rs delete mode 100644 tests/u128_test_bits.rs delete mode 100644 tests/u128_test_compare.rs delete mode 100644 tests/u16_convert_test.rs delete mode 100644 tests/u16_mul_div_test.rs delete mode 100644 tests/u1_convert_test.rs delete mode 100644 tests/u256_convert_test.rs delete mode 100644 tests/u256_mul_div_test.rs delete mode 100644 tests/u256_test_add.rs delete mode 100644 tests/u256_test_bits.rs delete mode 100644 tests/u256_test_compare.rs delete mode 100644 tests/u256_test_div.rs delete mode 100644 tests/u256_test_sub_mul.rs delete mode 100644 tests/u32_convert_test.rs delete mode 100644 tests/u32_mul_div_test.rs delete mode 100644 tests/u64_convert_test.rs delete mode 100644 tests/u64_mul_div_test.rs delete mode 100644 tests/u8_convert_test.rs delete mode 100644 tests/u8_mul_div_test.rs diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..421c13b --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,61 @@ +name: Smplx-lint + +on: + workflow_dispatch: + push: + branches: + - master + pull_request: + branches: + - master + - dev + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +env: + CARGO_TERM_COLOR: always + SIMPLEX_VERSION: 0.0.11 + +jobs: + lint: + runs-on: ubuntu-latest + timeout-minutes: 15 + permissions: + contents: read + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + submodules: true + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + toolchain: "1.91.0" + components: rustfmt, clippy + + - name: Use Rust cache + uses: Swatinem/rust-cache@v2 + + - name: Install simplex-cli + uses: BlockstreamResearch/simplicityhl-toolchain@v0.0.2 + with: + components: simplex@${{ env.SIMPLEX_VERSION }} + + # For building generated files + - name: Generate contract artifacts + shell: bash + run: | + simplex build + + - name: Check formatting + run: | + cargo fmt --all --check + + - name: Run clippy + run: | + cargo clippy --workspace --all-targets --all-features -- -D warnings diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index da161af..c4df5a4 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -17,6 +17,7 @@ concurrency: env: CARGO_TERM_COLOR: always RUST_BACKTRACE: full + SIMPLEX_VERSION: 0.0.11 jobs: simplex-tests: @@ -47,7 +48,7 @@ jobs: - name: Install simplex-cli uses: BlockstreamResearch/simplicityhl-toolchain@v0.0.2 with: - components: simplex + components: simplex@${{ env.SIMPLEX_VERSION }} - name: Generate contract artifacts shell: bash diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fdee6fd..cc14879 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -12,7 +12,7 @@ When submitting a feature request, please provide as many details as possible fo ## Pull Requests -If you're interested in contributing code to the library, start by [forking the repository](https://github.com/BlockstreamResearch/simplicityhl-std/fork) and submitting a pull request. +If you're interested in contributing code to the library, start by [forking the repository](https://github.com/BlockstreamResearch/simplicityhl-std/fork) and submitting a pull request. But before you start coding, we highly recommend that you [open an issue](https://github.com/BlockstreamResearch/simplicityhl-std/issues/new/choose) first to discuss the changes you want to make. diff --git a/simf/asserts_test.simf b/simf/asserts_test.simf deleted file mode 100644 index 36e1708..0000000 --- a/simf/asserts_test.simf +++ /dev/null @@ -1,62 +0,0 @@ -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_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; - -fn main() { - let fn_idx: u8 = witness::FUNCTION_INDEX; - - let a_u1: Option = witness::FIRST_ARG_U1; - let b_u1: Option = witness::SECOND_ARG_U1; - - let a_u8: Option = witness::FIRST_ARG_U8; - let b_u8: Option = witness::SECOND_ARG_U8; - - let a_u16: Option = witness::FIRST_ARG_U16; - let b_u16: Option = witness::SECOND_ARG_U16; - - let a_u32: Option = witness::FIRST_ARG_U32; - let b_u32: Option = witness::SECOND_ARG_U32; - - let a_u64: Option = witness::FIRST_ARG_U64; - let b_u64: Option = witness::SECOND_ARG_U64; - - let a_u128: Option = witness::FIRST_ARG_U128; - let b_u128: Option = witness::SECOND_ARG_U128; - - let a_u256: Option = witness::FIRST_ARG_U256; - let b_u256: Option = witness::SECOND_ARG_U256; - - // Assert Eq - match if_test_this_function(0, fn_idx) { true => { assert_eq_1(unwrap(a_u1), unwrap(b_u1)); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { assert_eq_8(unwrap(a_u8), unwrap(b_u8)); }, false => (), }; - match if_test_this_function(2, fn_idx) { true => { assert_eq_16(unwrap(a_u16), unwrap(b_u16)); }, false => (), }; - match if_test_this_function(3, fn_idx) { true => { assert_eq_32(unwrap(a_u32), unwrap(b_u32)); }, false => (), }; - 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(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/binary_test.simf b/simf/binary_test.simf deleted file mode 100644 index ec4ab30..0000000 --- a/simf/binary_test.simf +++ /dev/null @@ -1,21 +0,0 @@ -use crate::lib::binary::{not, or, and, xor}; - -fn main() { - assert!(not(false)); - assert!(not(not(true))); - - assert!(or(true, false)); - assert!(or(false, true)); - assert!(or(true, true)); - assert!(not(or(false, false))); - - assert!(and(true, true)); - assert!(not(and(true, false))); - assert!(not(and(false, true))); - assert!(not(and(false, false))); - - assert!(xor(true, false)); - assert!(xor(false, true)); - assert!(not(xor(true, true))); - assert!(not(xor(false, false))); -} diff --git a/simf/helper.simf b/simf/helper.simf deleted file mode 100644 index 4bfc650..0000000 --- a/simf/helper.simf +++ /dev/null @@ -1,13 +0,0 @@ -use crate::lib::binary::not; - -pub fn if_test_this_function(index: u8, flag: u8) -> bool { - jet::eq_8(index, flag) -} - -/// Asserts a result equals the expected bool value. -pub fn assert_bool(result: bool, expected_bool: bool) { - match expected_bool { - true => assert!(result), - false => assert!(not(result)), - } -} diff --git a/simf/lib/binary.simf b/simf/lib/binary.simf index 85f67d0..9042811 100644 --- a/simf/lib/binary.simf +++ b/simf/lib/binary.simf @@ -1,19 +1,19 @@ /// Returns the result of the NOT operation on the provided value pub fn not(bit: bool) -> bool { - ::into(jet::complement_1(::into(bit))) + ::into(jet::complement_1(::into(bit))) } /// Returns the result of the OR operation on the provided values pub fn or(a: bool, b: bool) -> bool { - ::into(jet::or_1(::into(a), ::into(b))) + ::into(jet::or_1(::into(a), ::into(b))) } /// Returns the result of the AND operation on the provided values pub fn and(a: bool, b: bool) -> bool { - ::into(jet::and_1(::into(a), ::into(b))) + ::into(jet::and_1(::into(a), ::into(b))) } /// Returns the result of the XOR operation on the provided values pub fn xor(a: bool, b: bool) -> bool { - and(or(a, b), not(and(a, b))) + and(or(a, b), not(and(a, b))) } diff --git a/simf/lib/op_return.simf b/simf/lib/op_return.simf index 2f71087..d6e1d85 100644 --- a/simf/lib/op_return.simf +++ b/simf/lib/op_return.simf @@ -8,5 +8,5 @@ pub fn is_output_op_return(output_index: u32) -> bool { /// Asserts that the specified output is an OP_RETURN (null data) output pub fn assert_output_is_op_return(output_index: u32) { - assert!(is_output_op_return(output_index)); + assert!(is_output_op_return(output_index)); } diff --git a/simf/lib/secp256k1/operations.simf b/simf/lib/secp256k1/operations.simf index 0cfabbe..b4a2fcd 100644 --- a/simf/lib/secp256k1/operations.simf +++ b/simf/lib/secp256k1/operations.simf @@ -79,7 +79,7 @@ pub fn assert_scalar_eq(a: Scalar, b: Scalar) { pub fn assert_ge_eq(a: Ge, b: Ge) { let (ax, ay): (Fe, Fe) = a; let (bx, by): (Fe, Fe) = b; - + assert_fe_eq(ax, bx); assert_fe_eq(ay, by); } diff --git a/simf/lib/u128/bit.simf b/simf/lib/u128/bit.simf index 1e4f0e9..7b8e208 100644 --- a/simf/lib/u128/bit.simf +++ b/simf/lib/u128/bit.simf @@ -35,7 +35,10 @@ pub fn left_shift_128(shift: u8, a: u128) -> u128 { match jet::lt_8(shift, 64) { true => { - let (_, low_to_high_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 + 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); @@ -46,9 +49,9 @@ pub fn left_shift_128(shift: u8, a: u128) -> u128 { let (_, shift): (bool, u8) = jet::subtract_8(shift, 64); // shift >= 64 <(u64, u64)>::into((jet::left_shift_64(shift, a_low), 0)) - } + }, } - } + }, } } @@ -61,7 +64,10 @@ pub fn right_shift_128(shift: u8, a: u128) -> u128 { match jet::lt_8(shift, 64) { true => { - let (_, high_to_low_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 + 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); @@ -72,8 +78,8 @@ pub fn right_shift_128(shift: u8, a: u128) -> u128 { 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/math.simf b/simf/lib/u128/math.simf index 38a679d..313eff7 100644 --- a/simf/lib/u128/math.simf +++ b/simf/lib/u128/math.simf @@ -193,9 +193,12 @@ pub fn calculate_normalizer_base_64(b: u128, is_b_u128: bool) -> u64 { 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 + let ( + _, + norm + ): (bool, u64) = jet::add_64(norm, 1); // norm <= 2^63, so norm + 1 can not overflow norm - } + }, } } @@ -221,7 +224,11 @@ fn normalize_to_threshold_128_63(a: u128, b: u128, is_b_u128: bool) -> (u256, u1 /// 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) { + 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) @@ -309,11 +316,7 @@ pub fn div_mod_128_64(a: u128, b: u64) -> (u128, u64) { a_normalized, b_normalized, norm - ): (u256, u128, u64) = normalize_to_threshold_128_63( - a_prime, - u64_to_u128(b), - false - ); + ): (u256, u128, u64) = normalize_to_threshold_128_63(a_prime, u64_to_u128(b), false); // a_normalized fits into u128, because remainder < b and b_normalized fits into u64 let (_, a_normalized): (u128, u128) = ::into(a_normalized); diff --git a/simf/lib/u16/math.simf b/simf/lib/u16/math.simf index e146253..b87d435 100644 --- a/simf/lib/u16/math.simf +++ b/simf/lib/u16/math.simf @@ -16,7 +16,7 @@ pub fn safe_add_16(a: u16, b: u16) -> u16 { /// Returns the difference of two u16 values wrapped in Some, or None if the result overflows u16 pub fn checked_sub_16(a: u16, b: u16) -> Option { let (borrow, diff): (bool, u16) = jet::subtract_16(a, b); - + match borrow { true => None, false => Some(diff), diff --git a/simf/lib/u256/bit.simf b/simf/lib/u256/bit.simf index 2df63a7..372ebbc 100644 --- a/simf/lib/u256/bit.simf +++ b/simf/lib/u256/bit.simf @@ -27,7 +27,10 @@ pub fn left_shift_256(shift: u8, a: u256) -> u256 { match jet::lt_8(shift, 128) { true => { - let (_, low_to_high_amount): (bool, u8) = jet::subtract_8(128, shift); // shift < 128 + 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); @@ -38,9 +41,9 @@ pub fn left_shift_256(shift: u8, a: u256) -> u256 { let (_, shift): (bool, u8) = jet::subtract_8(shift, 128); // shift >= 128 <(u128, u128)>::into((left_shift_128(shift, a_low), 0)) - } + }, } - } + }, } } @@ -53,7 +56,10 @@ pub fn right_shift_256(shift: u8, a: u256) -> u256 { match jet::lt_8(shift, 128) { true => { - let (_, high_to_low_amount): (bool, u8) = jet::subtract_8(128, shift); // shift < 128 + 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); @@ -64,8 +70,8 @@ pub fn right_shift_256(shift: u8, a: u256) -> u256 { 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/math.simf b/simf/lib/u256/math.simf index f06ea7c..ed193fb 100644 --- a/simf/lib/u256/math.simf +++ b/simf/lib/u256/math.simf @@ -239,7 +239,13 @@ pub fn calculate_normalizer_base_128(b: u256) -> 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 + let ( + _, + norm + ): (bool, u128) = add_128( + norm, + 1 + ); // norm <= 2^127, so norm + 1 can not overflow norm }, } diff --git a/simf/lib/u256/mul_div.simf b/simf/lib/u256/mul_div.simf index 52ad6ef..f80fd14 100644 --- a/simf/lib/u256/mul_div.simf +++ b/simf/lib/u256/mul_div.simf @@ -23,10 +23,22 @@ use crate::lib::u256::comparison::{is_zero_256, lt_256}; /// Returns the quotient digit to use in the subsequent multiply-and-subtract step. /// Expects result to fit into u128 fn estimate_quotient_digit_base_128(u2: u128, u1: u128, u0: u128, v1: u128, v0: u128) -> u128 { - let (q_hat, r_hat, carry): (u128, u128, bool) = match lt_128(u2, v1) { + let ( + q_hat, + r_hat, + carry + ): (u128, u128, bool) = match lt_128(u2, v1) { true => { - let (q_hat, r_hat) : (u256, u128) = algorithm_d_256_128(<(u128, u128)>::into((u2, u1)), v1); - let (_, q_hat): (u128, u128) = split_u256_into_u128(q_hat); // skip the check for optimization. u2 < v1, so q_hat fits into u128 + let ( + q_hat, + r_hat + ): (u256, u128) = algorithm_d_256_128(<(u128, u128)>::into((u2, u1)), v1); + let ( + _, + q_hat + ): (u128, u128) = split_u256_into_u128( + q_hat + ); // skip the check for optimization. u2 < v1, so q_hat fits into u128 (q_hat, r_hat, false) }, @@ -123,7 +135,9 @@ fn algorithm_d_512_256(dividend_high: u256, dividend_low: u256, divisor: u256) - norm_dividend_high, norm_dividend_low, norm_divisor - ): (u128, u256, u256, u256) = (normalize_to_threshold_512_127(dividend_high, dividend_low, divisor)); + ): (u128, u256, u256, u256) = ( + normalize_to_threshold_512_127(dividend_high, dividend_low, divisor) + ); let (u3, u2): (u128, u128) = split_u256_into_u128(norm_dividend_high); let (u1, u0): (u128, u128) = split_u256_into_u128(norm_dividend_low); @@ -158,14 +172,29 @@ pub fn mul_div_256(a: u256, b: u256, denominator: u256) -> u256 { // result_high < denominator, denominator fits into u128, so result_high fits too. // split_u256_into_u128 is used instead of safe_u256_to_u128 to avoid the unnecessary check let (_, result_2): (u128, u128) = split_u256_into_u128(result_high); - let (result_1, result_0): (u128, u128) = split_u256_into_u128(result_low); + let ( + result_1, + result_0 + ): (u128, u128) = split_u256_into_u128(result_low); // result_high < denominator, so result_2 < denom_low and q1 fits into u128 - let (q1, r): (u256, u128) = div_mod_256_128(<(u128, u128)>::into((result_2, result_1)), denom_low); + let ( + q1, + r + ): (u256, u128) = div_mod_256_128( + <(u128, u128)>::into((result_2, result_1)), + denom_low + ); let (_, q1): (u128, u128) = split_u256_into_u128(q1); // r < denom_low, so q0 fits into u128 - let (q0, _): (u256, u128) = div_mod_256_128(<(u128, u128)>::into((r, result_0)), denom_low); + let ( + q0, + _ + ): (u256, u128) = div_mod_256_128( + <(u128, u128)>::into((r, result_0)), + denom_low + ); let (_, q0): (u128, u128) = split_u256_into_u128(q0); <(u128, u128)>::into((q1, q0)) diff --git a/simf/lib/u64/math.simf b/simf/lib/u64/math.simf index 77fc4aa..7617725 100644 --- a/simf/lib/u64/math.simf +++ b/simf/lib/u64/math.simf @@ -31,7 +31,7 @@ pub fn safe_sub_64(a: u64, b: u64) -> u64 { /// Returns the product of two u64 values wrapped in Some, or None if the result overflows u64 pub fn checked_mul_64(a: u64, b: u64) -> Option { let result: u128 = jet::multiply_64(a, b); - + let (high, low): (u64, u64) = ::into(result); match jet::is_zero_64(high) { diff --git a/simf/op_return_test.simf b/simf/op_return_test.simf deleted file mode 100644 index d847d8e..0000000 --- a/simf/op_return_test.simf +++ /dev/null @@ -1,12 +0,0 @@ -use crate::lib::op_return::{is_output_op_return, assert_output_is_op_return}; -use crate::helper::{if_test_this_function, assert_bool}; - -fn main() { - let fn_idx: u8 = witness::FUNCTION_INDEX; - - let index: u32 = witness::INDEX; - let expected: bool = witness::EXPECTED; - - match if_test_this_function(0, fn_idx) { true => { assert_bool(is_output_op_return(index), expected); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { assert_output_is_op_return(index); }, false => (), }; -} diff --git a/simf/secp256k1_operations_test.simf b/simf/secp256k1_operations_test.simf deleted file mode 100644 index 991eae8..0000000 --- a/simf/secp256k1_operations_test.simf +++ /dev/null @@ -1,35 +0,0 @@ -use crate::lib::secp256k1::operations::{ - ge_to_point, point_to_gej, fe_sub, scalar_sub, gej_sub, - fe_eq, scalar_eq, ge_eq, gej_point_eq, safe_gej_normalize, - assert_fe_eq, assert_scalar_eq, assert_ge_eq, assert_point_eq, assert_gej_point_eq, assert_gej_eq -}; -use crate::helper::if_test_this_function; - -fn main() { - let fn_idx: u8 = witness::FUNCTION_INDEX; - - // Interpreted as Fe or Scalar depending on the function index - let uint_a: u256 = witness::FIRST_UINT; - let uint_b: u256 = witness::SECOND_UINT; - let ge_a: Ge = witness::FIRST_GE; - let ge_b: Ge = witness::SECOND_GE; - let gej_a: Gej = witness::FIRST_GEJ; - let gej_b: Gej = witness::SECOND_GEJ; - let point_a: Point = witness::FIRST_POINT; - - let exp_uint: u256 = witness::EXPECTED_UINT; - let exp_ge: Ge = witness::EXPECTED_GE; - let exp_gej: Gej = witness::EXPECTED_GEJ; - let exp_point: Point = witness::EXPECTED_POINT; - - match if_test_this_function(0, fn_idx) { true => { assert_point_eq(ge_to_point(ge_a), exp_point); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { assert_gej_point_eq(point_to_gej(point_a), exp_point); }, false => (), }; - match if_test_this_function(2, fn_idx) { true => { assert_fe_eq(fe_sub(uint_a, uint_b), exp_uint); }, false => (), }; - match if_test_this_function(3, fn_idx) { true => { assert_scalar_eq(scalar_sub(uint_a, uint_b), exp_uint); }, false => (), }; - match if_test_this_function(4, fn_idx) { true => { assert_gej_eq(gej_sub(gej_a, gej_b), exp_gej); }, false => (), }; - match if_test_this_function(5, fn_idx) { true => { assert!(fe_eq(uint_a, uint_b)); }, false => (), }; - match if_test_this_function(6, fn_idx) { true => { assert!(scalar_eq(uint_a, uint_b)); }, false => (), }; - match if_test_this_function(7, fn_idx) { true => { assert!(ge_eq(ge_a, ge_b)); }, false => (), }; - match if_test_this_function(8, fn_idx) { true => { assert!(gej_point_eq(gej_a, point_a)); }, false => (), }; - match if_test_this_function(9, fn_idx) { true => { assert_ge_eq(safe_gej_normalize(gej_a), exp_ge); }, false => (), }; -} diff --git a/simf/tests/asserts.simf b/simf/tests/asserts.simf new file mode 100644 index 0000000..75e771a --- /dev/null +++ b/simf/tests/asserts.simf @@ -0,0 +1,138 @@ +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_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::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a_u1: Option = witness::FIRST_ARG_U1; + let b_u1: Option = witness::SECOND_ARG_U1; + + let a_u8: Option = witness::FIRST_ARG_U8; + let b_u8: Option = witness::SECOND_ARG_U8; + + let a_u16: Option = witness::FIRST_ARG_U16; + let b_u16: Option = witness::SECOND_ARG_U16; + + let a_u32: Option = witness::FIRST_ARG_U32; + let b_u32: Option = witness::SECOND_ARG_U32; + + let a_u64: Option = witness::FIRST_ARG_U64; + let b_u64: Option = witness::SECOND_ARG_U64; + + let a_u128: Option = witness::FIRST_ARG_U128; + let b_u128: Option = witness::SECOND_ARG_U128; + + let a_u256: Option = witness::FIRST_ARG_U256; + let b_u256: Option = witness::SECOND_ARG_U256; + + // Assert Eq + match is_selected(0, fn_idx) { + true => { + assert_eq_1(unwrap(a_u1), unwrap(b_u1)); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert_eq_8(unwrap(a_u8), unwrap(b_u8)); + }, + false => (), + }; + match is_selected(2, fn_idx) { + true => { + assert_eq_16(unwrap(a_u16), unwrap(b_u16)); + }, + false => (), + }; + match is_selected(3, fn_idx) { + true => { + assert_eq_32(unwrap(a_u32), unwrap(b_u32)); + }, + false => (), + }; + match is_selected(4, fn_idx) { + true => { + assert_eq_64(unwrap(a_u64), unwrap(b_u64)); + }, + false => (), + }; + match is_selected(5, fn_idx) { + true => { + assert_eq_128(unwrap(a_u128), unwrap(b_u128)); + }, + false => (), + }; + match is_selected(6, fn_idx) { + true => { + assert_eq_256(unwrap(a_u256), unwrap(b_u256)); + }, + false => (), + }; + match is_selected(7, fn_idx) { + true => { + assert_eq_bool(::into(unwrap(a_u1)), ::into(unwrap(b_u1))); + }, + false => (), + }; + + // Assert None + match is_selected(8, fn_idx) { + true => { + assert_none_1(a_u1); + }, + false => (), + }; + match is_selected(9, fn_idx) { + true => { + assert_none_8(a_u8); + }, + false => (), + }; + match is_selected(10, fn_idx) { + true => { + assert_none_16(a_u16); + }, + false => (), + }; + match is_selected(11, fn_idx) { + true => { + assert_none_32(a_u32); + }, + false => (), + }; + match is_selected(12, fn_idx) { + true => { + assert_none_64(a_u64); + }, + false => (), + }; + match is_selected(13, fn_idx) { + true => { + assert_none_128(a_u128); + }, + false => (), + }; + match is_selected(14, fn_idx) { + true => { + assert_none_256(a_u256); + }, + false => (), + }; +} diff --git a/simf/tests/binary.simf b/simf/tests/binary.simf new file mode 100644 index 0000000..e348d91 --- /dev/null +++ b/simf/tests/binary.simf @@ -0,0 +1,21 @@ +use crate::lib::binary::{not, or, and, xor}; + +fn main() { + assert!(not(false)); + assert!(not(not(true))); + + assert!(or(true, false)); + assert!(or(false, true)); + assert!(or(true, true)); + assert!(not(or(false, false))); + + assert!(and(true, true)); + assert!(not(and(true, false))); + assert!(not(and(false, true))); + assert!(not(and(false, false))); + + assert!(xor(true, false)); + assert!(xor(false, true)); + assert!(not(xor(true, true))); + assert!(not(xor(false, false))); +} diff --git a/simf/tests/op_return.simf b/simf/tests/op_return.simf new file mode 100644 index 0000000..d30a1d0 --- /dev/null +++ b/simf/tests/op_return.simf @@ -0,0 +1,24 @@ +use crate::lib::op_return::{is_output_op_return, assert_output_is_op_return}; + +use crate::tests::support::dispatch::is_selected; +use crate::tests::support::expect::assert_bool; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let index: u32 = witness::INDEX; + let expected: bool = witness::EXPECTED; + + match is_selected(0, fn_idx) { + true => { + assert_bool(is_output_op_return(index), expected); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert_output_is_op_return(index); + }, + false => (), + }; +} diff --git a/simf/tests/secp256k1/operations.simf b/simf/tests/secp256k1/operations.simf new file mode 100644 index 0000000..8e7485d --- /dev/null +++ b/simf/tests/secp256k1/operations.simf @@ -0,0 +1,98 @@ +use crate::lib::secp256k1::operations::{ + ge_to_point, + point_to_gej, + fe_sub, + scalar_sub, + gej_sub, + fe_eq, + scalar_eq, + ge_eq, + gej_point_eq, + safe_gej_normalize, + assert_fe_eq, + assert_scalar_eq, + assert_ge_eq, + assert_point_eq, + assert_gej_point_eq, + assert_gej_eq +}; +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + // Interpreted as Fe or Scalar depending on the function index + let uint_a: u256 = witness::FIRST_UINT; + let uint_b: u256 = witness::SECOND_UINT; + let ge_a: Ge = witness::FIRST_GE; + let ge_b: Ge = witness::SECOND_GE; + let gej_a: Gej = witness::FIRST_GEJ; + let gej_b: Gej = witness::SECOND_GEJ; + let point_a: Point = witness::FIRST_POINT; + + let exp_uint: u256 = witness::EXPECTED_UINT; + let exp_ge: Ge = witness::EXPECTED_GE; + let exp_gej: Gej = witness::EXPECTED_GEJ; + let exp_point: Point = witness::EXPECTED_POINT; + + match is_selected(0, fn_idx) { + true => { + assert_point_eq(ge_to_point(ge_a), exp_point); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert_gej_point_eq(point_to_gej(point_a), exp_point); + }, + false => (), + }; + match is_selected(2, fn_idx) { + true => { + assert_fe_eq(fe_sub(uint_a, uint_b), exp_uint); + }, + false => (), + }; + match is_selected(3, fn_idx) { + true => { + assert_scalar_eq(scalar_sub(uint_a, uint_b), exp_uint); + }, + false => (), + }; + match is_selected(4, fn_idx) { + true => { + assert_gej_eq(gej_sub(gej_a, gej_b), exp_gej); + }, + false => (), + }; + match is_selected(5, fn_idx) { + true => { + assert!(fe_eq(uint_a, uint_b)); + }, + false => (), + }; + match is_selected(6, fn_idx) { + true => { + assert!(scalar_eq(uint_a, uint_b)); + }, + false => (), + }; + match is_selected(7, fn_idx) { + true => { + assert!(ge_eq(ge_a, ge_b)); + }, + false => (), + }; + match is_selected(8, fn_idx) { + true => { + assert!(gej_point_eq(gej_a, point_a)); + }, + false => (), + }; + match is_selected(9, fn_idx) { + true => { + assert_ge_eq(safe_gej_normalize(gej_a), exp_ge); + }, + false => (), + }; +} diff --git a/simf/tests/support/dispatch.simf b/simf/tests/support/dispatch.simf new file mode 100644 index 0000000..721d949 --- /dev/null +++ b/simf/tests/support/dispatch.simf @@ -0,0 +1,6 @@ +/// Indices `0..192` belong to the operations shared by all unsigned widths and +/// stay in lockstep with `CommonOp` on the Rust side. `192..256` are free for +/// operations specific to a single width. +pub fn is_selected(index: u8, fn_idx: u8) -> bool { + jet::eq_8(index, fn_idx) +} diff --git a/simf/tests/support/expect.simf b/simf/tests/support/expect.simf new file mode 100644 index 0000000..73b0365 --- /dev/null +++ b/simf/tests/support/expect.simf @@ -0,0 +1,136 @@ +//! Assertions shared by the test programs. +//! +//! SimplicityHL has no generics, so each family is written once per width and +//! named with that width's suffix, exactly like `crate::lib::asserts`. + +use crate::lib::asserts::{ + assert_eq_8, + assert_eq_16, + assert_eq_32, + assert_eq_64, + assert_eq_128, + assert_eq_256, + assert_none_8, + assert_none_16, + assert_none_32, + assert_none_64, + assert_none_128, + assert_none_256 +}; +use crate::lib::binary::not; +use crate::lib::u128::bit::eq_128; + +/// Asserts a result equals the expected bool value. +pub fn assert_bool(result: bool, expected_bool: bool) { + match expected_bool { + true => assert!(result), + false => assert!(not(result)), + } +} + +/// 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. +pub fn assert_eq_opt_8(result: Option, expected: Option) { + match expected { + Some(e: u8) => assert_eq_8(unwrap(result), e), + None => assert_none_8(result), + } +} + +pub fn assert_eq_opt_16(result: Option, expected: Option) { + match expected { + Some(e: u16) => assert_eq_16(unwrap(result), e), + None => assert_none_16(result), + } +} + +pub fn assert_eq_opt_32(result: Option, expected: Option) { + match expected { + Some(e: u32) => assert_eq_32(unwrap(result), e), + None => assert_none_32(result), + } +} + +pub fn assert_eq_opt_64(result: Option, expected: Option) { + match expected { + Some(e: u64) => assert_eq_64(unwrap(result), e), + None => assert_none_64(result), + } +} + +pub fn assert_eq_opt_128(result: Option, expected: Option) { + match expected { + Some(e: u128) => assert_eq_128(unwrap(result), e), + None => assert_none_128(result), + } +} + +pub fn assert_eq_opt_256(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`. +pub fn assert_bool_by_opt_8(result: bool, expected: Option) { + match expected { + Some(_: u8) => assert!(result), + None => assert!(not(result)), + } +} + +pub fn assert_bool_by_opt_16(result: bool, expected: Option) { + match expected { + Some(_: u16) => assert!(result), + None => assert!(not(result)), + } +} + +pub fn assert_bool_by_opt_32(result: bool, expected: Option) { + match expected { + Some(_: u32) => assert!(result), + None => assert!(not(result)), + } +} + +pub fn assert_bool_by_opt_64(result: bool, expected: Option) { + match expected { + Some(_: u64) => assert!(result), + None => assert!(not(result)), + } +} + +pub fn assert_bool_by_opt_128(result: bool, expected: Option) { + match expected { + Some(_: u128) => assert!(result), + None => assert!(not(result)), + } +} + +pub fn assert_bool_by_opt_256(result: bool, expected: Option) { + match expected { + Some(_: u256) => assert!(result), + None => assert!(not(result)), + } +} + +/// Asserts a `(carry, value)` result equals the expected pair. Used by the +/// primitives that return an out-of-band carry or borrow alongside the result. +pub fn assert_eq_uint_bool_128(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)); +} + +pub fn assert_eq_uint_bool_256(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); +} diff --git a/simf/u1_convert_test.simf b/simf/tests/u1/convert.simf similarity index 83% rename from simf/u1_convert_test.simf rename to simf/tests/u1/convert.simf index 8263efc..a1df432 100644 --- a/simf/u1_convert_test.simf +++ b/simf/tests/u1/convert.simf @@ -7,7 +7,6 @@ use crate::lib::u1::convert::{ u1_to_u256, u1_to_bool }; -use crate::lib::u128::bit::eq_128; use crate::lib::asserts::{ assert_eq_bool, assert_eq_8, @@ -17,7 +16,8 @@ use crate::lib::asserts::{ assert_eq_128, assert_eq_256 }; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -26,7 +26,7 @@ fn main() { let expected: u256 = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); @@ -37,7 +37,7 @@ fn main() { false => (), }; - match if_test_this_function(1, fn_idx) { + match is_selected(1, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); @@ -47,7 +47,7 @@ fn main() { false => (), }; - match if_test_this_function(2, fn_idx) { + match is_selected(2, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let (_, expected): (u32, u32) = ::into(expected); @@ -57,7 +57,7 @@ fn main() { false => (), }; - match if_test_this_function(3, fn_idx) { + match is_selected(3, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); @@ -66,7 +66,7 @@ fn main() { false => (), }; - match if_test_this_function(4, fn_idx) { + match is_selected(4, fn_idx) { true => { let (_, expected): (u128, u128) = ::into(expected); @@ -75,14 +75,14 @@ fn main() { false => (), }; - match if_test_this_function(5, fn_idx) { + match is_selected(5, fn_idx) { true => { assert_eq_256(u1_to_u256(a), expected); }, false => (), }; - match if_test_this_function(6, fn_idx) { + match is_selected(6, fn_idx) { true => { let expected_bool: bool = jet::eq_256(expected, 1); assert_eq_bool(u1_to_bool(a), expected_bool); diff --git a/simf/u128_test_bits.simf b/simf/tests/u128/bit.simf similarity index 51% rename from simf/u128_test_bits.simf rename to simf/tests/u128/bit.simf index b3ea239..60ac37f 100644 --- a/simf/u128_test_bits.simf +++ b/simf/tests/u128/bit.simf @@ -1,5 +1,7 @@ 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}; + +use crate::tests::support::dispatch::is_selected; +use crate::tests::support::expect::assert_bool; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -12,25 +14,42 @@ fn main() { /// Bit logic - match if_test_this_function(0, fn_idx) { true => { assert!(eq_128(and_128(a, b), unwrap(expected))); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { assert!(eq_128(or_128(a, b), unwrap(expected))); }, false => (), }; - match if_test_this_function(2, fn_idx) { true => { assert_bool(eq_128(a, b), expected_bool); }, false => (), }; + match is_selected(0, fn_idx) { + true => { + assert!(eq_128(and_128(a, b), unwrap(expected))); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert!(eq_128(or_128(a, b), unwrap(expected))); + }, + false => (), + }; + match is_selected(2, fn_idx) { + true => { + assert_bool(eq_128(a, b), expected_bool); + }, + false => (), + }; - match if_test_this_function(3, fn_idx) { + match is_selected(3, fn_idx) { true => { let (_, a): (u64, u64) = ::into(a); let shift: u8 = jet::rightmost_64_8(a); - assert!(eq_128(left_shift_128(shift, b), unwrap(expected))); - }, false => (), + assert!(eq_128(left_shift_128(shift, b), unwrap(expected))); + }, + false => (), }; - match if_test_this_function(4, fn_idx) { + match is_selected(4, fn_idx) { true => { let (_, a): (u64, u64) = ::into(a); let shift: u8 = jet::rightmost_64_8(a); - assert!(eq_128(right_shift_128(shift, b), unwrap(expected))); - }, false => (), + assert!(eq_128(right_shift_128(shift, b), unwrap(expected))); + }, + false => (), }; } diff --git a/simf/tests/u128/comparison.simf b/simf/tests/u128/comparison.simf new file mode 100644 index 0000000..9a30979 --- /dev/null +++ b/simf/tests/u128/comparison.simf @@ -0,0 +1,33 @@ +use crate::lib::u128::comparison::{is_zero_128, lt_128, le_128}; + +use crate::tests::support::dispatch::is_selected; +use crate::tests::support::expect::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 is_selected(0, fn_idx) { + true => { + assert_bool(is_zero_128(a), expected_bool); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert_bool(lt_128(a, b), expected_bool); + }, + false => (), + }; + match is_selected(2, fn_idx) { + true => { + assert_bool(le_128(a, b), expected_bool); + }, + false => (), + }; +} diff --git a/simf/u128_convert_test.simf b/simf/tests/u128/convert.simf similarity index 92% rename from simf/u128_convert_test.simf rename to simf/tests/u128/convert.simf index 0d70dcf..eff41e7 100644 --- a/simf/u128_convert_test.simf +++ b/simf/tests/u128/convert.simf @@ -18,7 +18,8 @@ use crate::lib::asserts::{ assert_eq_64, assert_eq_256 }; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -27,14 +28,14 @@ fn main() { let expected: u256 = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { assert_eq_256(u128_to_u256(a), expected); }, false => {}, }; - match if_test_this_function(1, fn_idx) { + match is_selected(1, fn_idx) { true => { let ( a15, @@ -121,7 +122,7 @@ fn main() { false => {}, }; - match if_test_this_function(2, fn_idx) { + match is_selected(2, fn_idx) { true => { let ( a7, @@ -161,7 +162,7 @@ fn main() { false => {}, }; - match if_test_this_function(3, fn_idx) { + match is_selected(3, fn_idx) { true => { let (a3, a2, a1, a0): (u32, u32, u32, u32) = split_u128_into_u32(a); @@ -177,7 +178,7 @@ fn main() { false => {}, }; - match if_test_this_function(4, fn_idx) { + match is_selected(4, fn_idx) { true => { let (a1, a0): (u64, u64) = split_u128_into_u64(a); @@ -189,7 +190,7 @@ fn main() { false => {}, }; - match if_test_this_function(5, fn_idx) { + match is_selected(5, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u1 = jet::rightmost_64_1(expected); @@ -199,7 +200,7 @@ fn main() { false => {}, }; - match if_test_this_function(6, fn_idx) { + match is_selected(6, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u8 = jet::rightmost_64_8(expected); @@ -209,7 +210,7 @@ fn main() { false => {}, }; - match if_test_this_function(7, fn_idx) { + match is_selected(7, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u16 = jet::rightmost_64_16(expected); @@ -219,7 +220,7 @@ fn main() { false => {}, }; - match if_test_this_function(8, fn_idx) { + match is_selected(8, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u32 = jet::rightmost_64_32(expected); @@ -229,7 +230,7 @@ fn main() { false => {}, }; - match if_test_this_function(9, fn_idx) { + match is_selected(9, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); diff --git a/simf/tests/u128/math/api.simf b/simf/tests/u128/math/api.simf new file mode 100644 index 0000000..0f028f3 --- /dev/null +++ b/simf/tests/u128/math/api.simf @@ -0,0 +1,95 @@ +use crate::lib::u128::math::{ + checked_add_128, + safe_add_128, + checked_sub_128, + safe_sub_128, + checked_mul_128, + safe_mul_128, + checked_div_128, + safe_div_128 +}; +use crate::lib::u128::comparison::{gt_128, ge_128}; +use crate::lib::asserts::assert_eq_128; + +use crate::tests::support::expect::{assert_bool_by_opt_128, assert_eq_opt_128}; +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u128 = witness::FIRST_ARG; + let b: u128 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + /// Safe functions + + // add + match is_selected(0, fn_idx) { + true => { + assert_eq_opt_128(checked_add_128(a, b), expected); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert_eq_128(safe_add_128(a, b), unwrap(expected)); + }, + false => (), + }; + + // sub + match is_selected(2, fn_idx) { + true => { + assert_eq_opt_128(checked_sub_128(a, b), expected); + }, + false => (), + }; + match is_selected(3, fn_idx) { + true => { + assert_eq_128(safe_sub_128(a, b), unwrap(expected)); + }, + false => (), + }; + + // mul + match is_selected(4, fn_idx) { + true => { + assert_eq_opt_128(checked_mul_128(a, b), expected); + }, + false => (), + }; + match is_selected(5, fn_idx) { + true => { + assert_eq_128(safe_mul_128(a, b), unwrap(expected)); + }, + false => (), + }; + + // div + match is_selected(6, fn_idx) { + true => { + assert_eq_opt_128(checked_div_128(a, b), expected); + }, + false => (), + }; + match is_selected(7, fn_idx) { + true => { + assert_eq_128(safe_div_128(a, b), unwrap(expected)); + }, + false => (), + }; + + // gt, ge + match is_selected(8, fn_idx) { + true => { + assert_bool_by_opt_128(gt_128(a, b), expected); + }, + false => (), + }; + match is_selected(9, fn_idx) { + true => { + assert_bool_by_opt_128(ge_128(a, b), expected); + }, + false => (), + }; +} diff --git a/simf/u128_basic_math_test.simf b/simf/tests/u128/math/primitives.simf similarity index 54% rename from simf/u128_basic_math_test.simf rename to simf/tests/u128/math/primitives.simf index 17d5992..d226fb5 100644 --- a/simf/u128_basic_math_test.simf +++ b/simf/tests/u128/math/primitives.simf @@ -13,17 +13,9 @@ use crate::lib::u128::math::{ 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. -/// Used for functions that return carry or borrow bool value. -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)); -} +use crate::tests::support::expect::assert_eq_uint_bool_128; +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -37,14 +29,48 @@ fn main() { /// Arithmetic - 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 is_selected(0, fn_idx) { + true => { + assert_eq_uint_bool_128(add_128(a, b), unwrap(expected), expected_bool); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + let (_, b): (u64, u64) = ::into(b); + assert_eq_uint_bool_128(add_128_64(a, b), unwrap(expected), expected_bool); + }, + false => (), + }; + match is_selected(2, fn_idx) { + true => { + assert_eq_uint_bool_128( + 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 is_selected(3, fn_idx) { + true => { + assert_eq_uint_bool_128(sub_128(a, b), unwrap(expected), expected_bool); + }, + false => (), + }; + match is_selected(4, fn_idx) { + true => { + assert_eq_uint_bool_128( + full_sub_128(eq_128(second_expected, 1), a, b), + unwrap(expected), + expected_bool + ); + }, + false => (), + }; - match if_test_this_function(5, fn_idx) { + match is_selected(5, fn_idx) { true => { let result: u256 = mul_128(a, b); @@ -56,7 +82,7 @@ fn main() { false => (), }; - match if_test_this_function(6, fn_idx) { + match is_selected(6, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); let result: u256 = mul_128_64(a, b); @@ -69,18 +95,18 @@ fn main() { false => (), }; - match if_test_this_function(7, fn_idx) { + match is_selected(7, fn_idx) { true => { let norm: u64 = calculate_normalizer_base_64(b, expected_bool); let (_, expected_norm): (u64, u64) = ::into(unwrap(expected)); - assert!(jet::eq_64(norm, expected_norm)); + assert!(jet::eq_64(norm, expected_norm)); }, false => (), }; - match if_test_this_function(8, fn_idx) { + match is_selected(8, fn_idx) { true => { let (_, u2): (u64, u64) = ::into(a); let (u1, u0): (u64, u64) = ::into(b); @@ -90,33 +116,38 @@ fn main() { let (_, expected_q): (u64, u64) = ::into(unwrap(expected)); - assert!(jet::eq_64(q, expected_q)); + assert!(jet::eq_64(q, expected_q)); }, false => (), }; - match if_test_this_function(9, fn_idx) { + match is_selected(9, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); let (_, expected_r): (u64, u64) = ::into(second_expected); let (q, r): (u128, u64) = div_mod_128_64(a, b); - assert!(eq_128(q, unwrap(expected))); - assert!(jet::eq_64(r, expected_r)); + assert!(eq_128(q, unwrap(expected))); + assert!(jet::eq_64(r, expected_r)); }, false => (), }; - match if_test_this_function(10, fn_idx) { + match is_selected(10, fn_idx) { true => { let (q, r): (u128, u128) = div_mod_128(a, b); - assert!(eq_128(q, unwrap(expected))); - assert!(eq_128(r, second_expected)); + assert!(eq_128(q, unwrap(expected))); + assert!(eq_128(r, second_expected)); }, false => (), }; - match if_test_this_function(11, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => (), }; + match is_selected(11, fn_idx) { + true => { + assert!(eq_128(div_128(a, b), unwrap(expected))); + }, + false => (), + }; } diff --git a/simf/u128_mul_div_test.simf b/simf/tests/u128/mul_div.simf similarity index 83% rename from simf/u128_mul_div_test.simf rename to simf/tests/u128/mul_div.simf index 614349a..689305b 100644 --- a/simf/u128_mul_div_test.simf +++ b/simf/tests/u128/mul_div.simf @@ -1,7 +1,7 @@ use crate::lib::u128::mul_div::mul_div_128; - use crate::lib::asserts::assert_eq_128; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -12,7 +12,7 @@ fn main() { let expected: Option = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { assert_eq_128(mul_div_128(a, b, c), unwrap(expected)); }, diff --git a/simf/u16_convert_test.simf b/simf/tests/u16/convert.simf similarity index 84% rename from simf/u16_convert_test.simf rename to simf/tests/u16/convert.simf index 847d4f3..b0ddae5 100644 --- a/simf/u16_convert_test.simf +++ b/simf/tests/u16/convert.simf @@ -7,7 +7,6 @@ use crate::lib::u16::convert::{ safe_u16_to_u1, safe_u16_to_u8 }; -use crate::lib::u128::bit::eq_128; use crate::lib::asserts::{ assert_eq_1, assert_eq_8, @@ -16,7 +15,8 @@ use crate::lib::asserts::{ assert_eq_128, assert_eq_256 }; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -25,7 +25,7 @@ fn main() { let expected: u256 = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let (_, expected): (u32, u32) = ::into(expected); @@ -35,7 +35,7 @@ fn main() { false => {}, }; - match if_test_this_function(1, fn_idx) { + match is_selected(1, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); @@ -44,7 +44,7 @@ fn main() { false => {}, }; - match if_test_this_function(2, fn_idx) { + match is_selected(2, fn_idx) { true => { let (_, expected): (u128, u128) = ::into(expected); @@ -53,14 +53,14 @@ fn main() { false => {}, }; - match if_test_this_function(3, fn_idx) { + match is_selected(3, fn_idx) { true => { assert_eq_256(u16_to_u256(a), expected); }, false => {}, }; - match if_test_this_function(4, fn_idx) { + match is_selected(4, fn_idx) { true => { let (a1, a0): (u8, u8) = split_u16_into_u8(a); @@ -74,7 +74,7 @@ fn main() { false => {}, }; - match if_test_this_function(5, fn_idx) { + match is_selected(5, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u1 = jet::rightmost_64_1(expected); @@ -84,7 +84,7 @@ fn main() { false => {}, }; - match if_test_this_function(6, fn_idx) { + match is_selected(6, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u8 = jet::rightmost_64_8(expected); diff --git a/simf/tests/u16/math.simf b/simf/tests/u16/math.simf new file mode 100644 index 0000000..6357266 --- /dev/null +++ b/simf/tests/u16/math.simf @@ -0,0 +1,93 @@ +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::tests::support::expect::{assert_bool_by_opt_16, assert_eq_opt_16}; +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u16 = witness::FIRST_ARG; + let b: u16 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + // add + match is_selected(0, fn_idx) { + true => { + assert_eq_opt_16(checked_add_16(a, b), expected); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert!(jet::eq_16(safe_add_16(a, b), unwrap(expected))); + }, + false => (), + }; + + // sub + match is_selected(2, fn_idx) { + true => { + assert_eq_opt_16(checked_sub_16(a, b), expected); + }, + false => (), + }; + match is_selected(3, fn_idx) { + true => { + assert!(jet::eq_16(safe_sub_16(a, b), unwrap(expected))); + }, + false => (), + }; + + // mul + match is_selected(4, fn_idx) { + true => { + assert_eq_opt_16(checked_mul_16(a, b), expected); + }, + false => (), + }; + match is_selected(5, fn_idx) { + true => { + assert!(jet::eq_16(safe_mul_16(a, b), unwrap(expected))); + }, + false => (), + }; + + // div + match is_selected(6, fn_idx) { + true => { + assert_eq_opt_16(checked_div_16(a, b), expected); + }, + false => (), + }; + match is_selected(7, fn_idx) { + true => { + assert!(jet::eq_16(safe_div_16(a, b), unwrap(expected))); + }, + false => (), + }; + + // gt, ge + match is_selected(8, fn_idx) { + true => { + assert_bool_by_opt_16(gt_16(a, b), expected); + }, + false => (), + }; + match is_selected(9, fn_idx) { + true => { + assert_bool_by_opt_16(ge_16(a, b), expected); + }, + false => (), + }; +} diff --git a/simf/u16_mul_div_test.simf b/simf/tests/u16/mul_div.simf similarity index 83% rename from simf/u16_mul_div_test.simf rename to simf/tests/u16/mul_div.simf index 646fde3..80e7e0c 100644 --- a/simf/u16_mul_div_test.simf +++ b/simf/tests/u16/mul_div.simf @@ -1,7 +1,7 @@ use crate::lib::u16::mul_div::mul_div_16; - use crate::lib::asserts::assert_eq_16; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -12,7 +12,7 @@ fn main() { let expected: Option = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { assert_eq_16(mul_div_16(a, b, c), unwrap(expected)); }, diff --git a/simf/u256_test_bits.simf b/simf/tests/u256/bit.simf similarity index 62% rename from simf/u256_test_bits.simf rename to simf/tests/u256/bit.simf index 797c0e8..42075b5 100644 --- a/simf/u256_test_bits.simf +++ b/simf/tests/u256/bit.simf @@ -1,7 +1,8 @@ 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; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -12,24 +13,36 @@ fn main() { /// Bit logic - match if_test_this_function(0, fn_idx) { true => { assert_eq_256(and_256(a, b), unwrap(expected)); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { assert_eq_256(or_256(a, b), unwrap(expected)); }, false => (), }; + match is_selected(0, fn_idx) { + true => { + assert_eq_256(and_256(a, b), unwrap(expected)); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert_eq_256(or_256(a, b), unwrap(expected)); + }, + false => (), + }; - match if_test_this_function(2, fn_idx) { + match is_selected(2, fn_idx) { true => { 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)); - }, false => (), + assert_eq_256(left_shift_256(shift, b), unwrap(expected)); + }, + false => (), }; - match if_test_this_function(3, fn_idx) { + match is_selected(3, fn_idx) { true => { 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)); - }, false => (), + assert_eq_256(right_shift_256(shift, b), unwrap(expected)); + }, + false => (), }; } diff --git a/simf/tests/u256/comparison.simf b/simf/tests/u256/comparison.simf new file mode 100644 index 0000000..86cf498 --- /dev/null +++ b/simf/tests/u256/comparison.simf @@ -0,0 +1,33 @@ +use crate::lib::u256::comparison::{is_zero_256, lt_256, le_256}; + +use crate::tests::support::dispatch::is_selected; +use crate::tests::support::expect::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; + + /// Comparison operations + + match is_selected(0, fn_idx) { + true => { + assert_bool(is_zero_256(a), expected_bool); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert_bool(lt_256(a, b), expected_bool); + }, + false => (), + }; + match is_selected(2, fn_idx) { + true => { + assert_bool(le_256(a, b), expected_bool); + }, + false => (), + }; +} diff --git a/simf/u256_convert_test.simf b/simf/tests/u256/convert.simf similarity index 95% rename from simf/u256_convert_test.simf rename to simf/tests/u256/convert.simf index fe659dd..46791dd 100644 --- a/simf/u256_convert_test.simf +++ b/simf/tests/u256/convert.simf @@ -19,7 +19,8 @@ use crate::lib::asserts::{ assert_eq_64, assert_eq_128 }; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -28,7 +29,7 @@ fn main() { let expected: u256 = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { let ( a31, @@ -185,7 +186,7 @@ fn main() { false => {}, }; - match if_test_this_function(1, fn_idx) { + match is_selected(1, fn_idx) { true => { let ( a15, @@ -275,7 +276,7 @@ fn main() { false => {}, }; - match if_test_this_function(2, fn_idx) { + match is_selected(2, fn_idx) { true => { let ( a7, @@ -315,7 +316,7 @@ fn main() { false => {}, }; - match if_test_this_function(3, fn_idx) { + match is_selected(3, fn_idx) { true => { let (a3, a2, a1, a0): (u64, u64, u64, u64) = split_u256_into_u64(a); @@ -334,7 +335,7 @@ fn main() { false => {}, }; - match if_test_this_function(4, fn_idx) { + match is_selected(4, fn_idx) { true => { let (a1, a0): (u128, u128) = split_u256_into_u128(a); @@ -346,7 +347,7 @@ fn main() { false => {}, }; - match if_test_this_function(5, fn_idx) { + match is_selected(5, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u1 = jet::rightmost_64_1(expected); @@ -356,7 +357,7 @@ fn main() { false => {}, }; - match if_test_this_function(6, fn_idx) { + match is_selected(6, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u8 = jet::rightmost_64_8(expected); @@ -366,7 +367,7 @@ fn main() { false => {}, }; - match if_test_this_function(7, fn_idx) { + match is_selected(7, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u16 = jet::rightmost_64_16(expected); @@ -376,7 +377,7 @@ fn main() { false => {}, }; - match if_test_this_function(8, fn_idx) { + match is_selected(8, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u32 = jet::rightmost_64_32(expected); @@ -386,7 +387,7 @@ fn main() { false => {}, }; - match if_test_this_function(9, fn_idx) { + match is_selected(9, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); @@ -395,7 +396,7 @@ fn main() { false => {}, }; - match if_test_this_function(10, fn_idx) { + match is_selected(10, fn_idx) { true => { let (_, expected): (u128, u128) = ::into(expected); diff --git a/simf/tests/u256/math/add.simf b/simf/tests/u256/math/add.simf new file mode 100644 index 0000000..398c4c5 --- /dev/null +++ b/simf/tests/u256/math/add.simf @@ -0,0 +1,38 @@ +use crate::lib::u256::math::{add_256, add_256_128, full_add_256}; + +use crate::tests::support::expect::assert_eq_uint_bool_256; +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let carry_in: bool = witness::THIRD_ARG; + let expected: Option = witness::EXPECTED; + let expected_bool: bool = witness::EXPECTED_BOOL; + + /// Arithmetic + + match is_selected(0, fn_idx) { + true => { + assert_eq_uint_bool_256(add_256(a, b), unwrap(expected), expected_bool); + }, + false => (), + }; + + match is_selected(1, fn_idx) { + true => { + let (_, b): (u128, u128) = ::into(b); + assert_eq_uint_bool_256(add_256_128(a, b), unwrap(expected), expected_bool); + }, + false => (), + }; + + match is_selected(2, fn_idx) { + true => { + assert_eq_uint_bool_256(full_add_256(carry_in, a, b), unwrap(expected), expected_bool); + }, + false => (), + }; +} diff --git a/simf/tests/u256/math/api.simf b/simf/tests/u256/math/api.simf new file mode 100644 index 0000000..4558aa9 --- /dev/null +++ b/simf/tests/u256/math/api.simf @@ -0,0 +1,95 @@ +use crate::lib::u256::math::{ + checked_add_256, + safe_add_256, + checked_sub_256, + safe_sub_256, + checked_mul_256, + safe_mul_256, + checked_div_256, + safe_div_256 +}; +use crate::lib::u256::comparison::{gt_256, ge_256}; +use crate::lib::asserts::assert_eq_256; + +use crate::tests::support::expect::{assert_bool_by_opt_256, assert_eq_opt_256}; +use crate::tests::support::dispatch::is_selected; + +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 is_selected(0, fn_idx) { + true => { + assert_eq_opt_256(checked_add_256(a, b), expected); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert_eq_256(safe_add_256(a, b), unwrap(expected)); + }, + false => (), + }; + + // sub + match is_selected(2, fn_idx) { + true => { + assert_eq_opt_256(checked_sub_256(a, b), expected); + }, + false => (), + }; + match is_selected(3, fn_idx) { + true => { + assert_eq_256(safe_sub_256(a, b), unwrap(expected)); + }, + false => (), + }; + + // mul + match is_selected(4, fn_idx) { + true => { + assert_eq_opt_256(checked_mul_256(a, b), expected); + }, + false => (), + }; + match is_selected(5, fn_idx) { + true => { + assert_eq_256(safe_mul_256(a, b), unwrap(expected)); + }, + false => (), + }; + + // div + match is_selected(6, fn_idx) { + true => { + assert_eq_opt_256(checked_div_256(a, b), expected); + }, + false => (), + }; + match is_selected(7, fn_idx) { + true => { + assert_eq_256(safe_div_256(a, b), unwrap(expected)); + }, + false => (), + }; + + // gt, ge + match is_selected(8, fn_idx) { + true => { + assert_bool_by_opt_256(gt_256(a, b), expected); + }, + false => (), + }; + match is_selected(9, fn_idx) { + true => { + assert_bool_by_opt_256(ge_256(a, b), expected); + }, + false => (), + }; +} diff --git a/simf/u256_test_div.simf b/simf/tests/u256/math/div.simf similarity index 84% rename from simf/u256_test_div.simf rename to simf/tests/u256/math/div.simf index 854a857..333dc42 100644 --- a/simf/u256_test_div.simf +++ b/simf/tests/u256/math/div.simf @@ -8,7 +8,8 @@ use crate::lib::u256::math::{ }; 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; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -21,7 +22,7 @@ fn main() { /// Arithmetic - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { let result: u128 = calculate_normalizer_base_128(a); @@ -32,7 +33,7 @@ fn main() { false => (), }; - match if_test_this_function(1, fn_idx) { + match is_selected(1, fn_idx) { true => { let (_, _, _, b): (u64, u64, u64, u64) = split_u256_into_u64(b); let (_, _, _, expected_r): (u64, u64, u64, u64) = split_u256_into_u64(second_expected); @@ -45,7 +46,7 @@ fn main() { false => (), }; - match if_test_this_function(2, fn_idx) { + match is_selected(2, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); let (_, expected_r): (u128, u128) = ::into(second_expected); @@ -58,7 +59,7 @@ fn main() { false => (), }; - match if_test_this_function(3, fn_idx) { + match is_selected(3, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); let (_, expected_r): (u128, u128) = ::into(second_expected); @@ -71,7 +72,7 @@ fn main() { false => (), }; - match if_test_this_function(4, fn_idx) { + match is_selected(4, fn_idx) { true => { let (q, r): (u256, u256) = div_mod_256(a, b); @@ -81,5 +82,10 @@ fn main() { false => (), }; - match if_test_this_function(5, fn_idx) { true => { assert_eq_256(div_256(a, b), unwrap(expected)); }, false => (), }; + match is_selected(5, fn_idx) { + true => { + assert_eq_256(div_256(a, b), unwrap(expected)); + }, + false => (), + }; } diff --git a/simf/u256_test_sub_mul.simf b/simf/tests/u256/math/sub_mul.simf similarity index 75% rename from simf/u256_test_sub_mul.simf rename to simf/tests/u256/math/sub_mul.simf index bbadd85..4b525a0 100644 --- a/simf/u256_test_sub_mul.simf +++ b/simf/tests/u256/math/sub_mul.simf @@ -8,17 +8,9 @@ use crate::lib::u256::math::{ }; 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}; -/// Asserts a result equals expected u256 and bool values. -/// Used for functions that return carry or borrow bool value. -fn assert_eq_uint_bool(result: (bool, u256), expected: u256, expected_bool: bool) { - let (bool_res, uint_res): (bool, u256) = result; - - assert_bool(bool_res, expected_bool); - - assert_eq_256(uint_res, expected); -} +use crate::tests::support::expect::assert_eq_uint_bool_256; +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -34,14 +26,14 @@ fn main() { /// Arithmetic - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { - assert_eq_uint_bool(sub_256(a, b), unwrap(expected), expected_bool); + assert_eq_uint_bool_256(sub_256(a, b), unwrap(expected), expected_bool); }, false => (), }; - match if_test_this_function(1, fn_idx) { + match is_selected(1, fn_idx) { true => { let (result_high, result_low): (u256, u256) = mul_256(a, b); @@ -51,7 +43,7 @@ fn main() { false => (), }; - match if_test_this_function(2, fn_idx) { + match is_selected(2, fn_idx) { true => { let (_, _, _, b): (u64, u64, u64, u64) = split_u256_into_u64(b); let (_, _, _, expected): (u64, u64, u64, u64) = split_u256_into_u64(unwrap(expected)); @@ -64,7 +56,7 @@ fn main() { false => (), }; - match if_test_this_function(3, fn_idx) { + match is_selected(3, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); let (_, expected): (u128, u128) = ::into(unwrap(expected)); @@ -77,7 +69,7 @@ fn main() { false => (), }; - match if_test_this_function(4, fn_idx) { + match is_selected(4, fn_idx) { true => { let (result_2, result_1, result_0): (u128, u256, u256) = mul_512_128(a, b, c); @@ -90,7 +82,7 @@ fn main() { false => (), }; - match if_test_this_function(5, fn_idx) { + match is_selected(5, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); let result: u256 = safe_mul_256_128(a, b); diff --git a/simf/u256_mul_div_test.simf b/simf/tests/u256/mul_div.simf similarity index 83% rename from simf/u256_mul_div_test.simf rename to simf/tests/u256/mul_div.simf index aeddd10..6ce1f52 100644 --- a/simf/u256_mul_div_test.simf +++ b/simf/tests/u256/mul_div.simf @@ -1,7 +1,7 @@ use crate::lib::u256::mul_div::mul_div_256; - use crate::lib::asserts::assert_eq_256; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -12,7 +12,7 @@ fn main() { let expected: Option = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { assert_eq_256(mul_div_256(a, b, c), unwrap(expected)); }, diff --git a/simf/u32_convert_test.simf b/simf/tests/u32/convert.simf similarity index 86% rename from simf/u32_convert_test.simf rename to simf/tests/u32/convert.simf index 658cc58..9e409db 100644 --- a/simf/u32_convert_test.simf +++ b/simf/tests/u32/convert.simf @@ -8,7 +8,6 @@ use crate::lib::u32::convert::{ safe_u32_to_u8, safe_u32_to_u16 }; -use crate::lib::u128::bit::eq_128; use crate::lib::asserts::{ assert_eq_1, assert_eq_8, @@ -17,7 +16,8 @@ use crate::lib::asserts::{ assert_eq_128, assert_eq_256 }; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -26,7 +26,7 @@ fn main() { let expected: u256 = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); @@ -35,7 +35,7 @@ fn main() { false => {}, }; - match if_test_this_function(1, fn_idx) { + match is_selected(1, fn_idx) { true => { let (_, expected): (u128, u128) = ::into(expected); @@ -44,14 +44,14 @@ fn main() { false => {}, }; - match if_test_this_function(2, fn_idx) { + match is_selected(2, fn_idx) { true => { assert_eq_256(u32_to_u256(a), expected); }, false => {}, }; - match if_test_this_function(3, fn_idx) { + match is_selected(3, fn_idx) { true => { let (a3, a2, a1, a0): (u8, u8, u8, u8) = split_u32_into_u8(a); @@ -72,7 +72,7 @@ fn main() { false => {}, }; - match if_test_this_function(4, fn_idx) { + match is_selected(4, fn_idx) { true => { let (a1, a0): (u16, u16) = split_u32_into_u16(a); @@ -86,7 +86,7 @@ fn main() { false => {}, }; - match if_test_this_function(5, fn_idx) { + match is_selected(5, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u1 = jet::rightmost_64_1(expected); @@ -96,7 +96,7 @@ fn main() { false => {}, }; - match if_test_this_function(6, fn_idx) { + match is_selected(6, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u8 = jet::rightmost_64_8(expected); @@ -106,7 +106,7 @@ fn main() { false => {}, }; - match if_test_this_function(7, fn_idx) { + match is_selected(7, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u16 = jet::rightmost_64_16(expected); diff --git a/simf/tests/u32/math.simf b/simf/tests/u32/math.simf new file mode 100644 index 0000000..cf35bfd --- /dev/null +++ b/simf/tests/u32/math.simf @@ -0,0 +1,93 @@ +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::tests::support::expect::{assert_bool_by_opt_32, assert_eq_opt_32}; +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u32 = witness::FIRST_ARG; + let b: u32 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + // add + match is_selected(0, fn_idx) { + true => { + assert_eq_opt_32(checked_add_32(a, b), expected); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert!(jet::eq_32(safe_add_32(a, b), unwrap(expected))); + }, + false => (), + }; + + // sub + match is_selected(2, fn_idx) { + true => { + assert_eq_opt_32(checked_sub_32(a, b), expected); + }, + false => (), + }; + match is_selected(3, fn_idx) { + true => { + assert!(jet::eq_32(safe_sub_32(a, b), unwrap(expected))); + }, + false => (), + }; + + // mul + match is_selected(4, fn_idx) { + true => { + assert_eq_opt_32(checked_mul_32(a, b), expected); + }, + false => (), + }; + match is_selected(5, fn_idx) { + true => { + assert!(jet::eq_32(safe_mul_32(a, b), unwrap(expected))); + }, + false => (), + }; + + // div + match is_selected(6, fn_idx) { + true => { + assert_eq_opt_32(checked_div_32(a, b), expected); + }, + false => (), + }; + match is_selected(7, fn_idx) { + true => { + assert!(jet::eq_32(safe_div_32(a, b), unwrap(expected))); + }, + false => (), + }; + + // gt, ge + match is_selected(8, fn_idx) { + true => { + assert_bool_by_opt_32(gt_32(a, b), expected); + }, + false => (), + }; + match is_selected(9, fn_idx) { + true => { + assert_bool_by_opt_32(ge_32(a, b), expected); + }, + false => (), + }; +} diff --git a/simf/u32_mul_div_test.simf b/simf/tests/u32/mul_div.simf similarity index 83% rename from simf/u32_mul_div_test.simf rename to simf/tests/u32/mul_div.simf index b41fb21..167302e 100644 --- a/simf/u32_mul_div_test.simf +++ b/simf/tests/u32/mul_div.simf @@ -1,7 +1,7 @@ use crate::lib::u32::mul_div::mul_div_32; - use crate::lib::asserts::assert_eq_32; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -12,7 +12,7 @@ fn main() { let expected: Option = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { assert_eq_32(mul_div_32(a, b, c), unwrap(expected)); }, diff --git a/simf/u64_convert_test.simf b/simf/tests/u64/convert.simf similarity index 89% rename from simf/u64_convert_test.simf rename to simf/tests/u64/convert.simf index 00d651e..0606bd4 100644 --- a/simf/u64_convert_test.simf +++ b/simf/tests/u64/convert.simf @@ -17,7 +17,8 @@ use crate::lib::asserts::{ assert_eq_128, assert_eq_256 }; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -26,7 +27,7 @@ fn main() { let expected: u256 = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { let (_, expected): (u128, u128) = ::into(expected); @@ -35,14 +36,14 @@ fn main() { false => {}, }; - match if_test_this_function(1, fn_idx) { + match is_selected(1, fn_idx) { true => { assert_eq_256(u64_to_u256(a), expected); }, false => {}, }; - match if_test_this_function(2, fn_idx) { + match is_selected(2, fn_idx) { true => { let ( a7, @@ -79,7 +80,7 @@ fn main() { false => {}, }; - match if_test_this_function(3, fn_idx) { + match is_selected(3, fn_idx) { true => { let (a3, a2, a1, a0): (u16, u16, u16, u16) = split_u64_into_u16(a); @@ -99,7 +100,7 @@ fn main() { false => {}, }; - match if_test_this_function(4, fn_idx) { + match is_selected(4, fn_idx) { true => { let (a1, a0): (u32, u32) = split_u64_into_u32(a); @@ -112,7 +113,7 @@ fn main() { false => {}, }; - match if_test_this_function(5, fn_idx) { + match is_selected(5, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u1 = jet::rightmost_64_1(expected); @@ -122,7 +123,7 @@ fn main() { false => {}, }; - match if_test_this_function(6, fn_idx) { + match is_selected(6, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u8 = jet::rightmost_64_8(expected); @@ -132,7 +133,7 @@ fn main() { false => {}, }; - match if_test_this_function(7, fn_idx) { + match is_selected(7, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u16 = jet::rightmost_64_16(expected); @@ -142,7 +143,7 @@ fn main() { false => {}, }; - match if_test_this_function(8, fn_idx) { + match is_selected(8, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u32 = jet::rightmost_64_32(expected); diff --git a/simf/tests/u64/math.simf b/simf/tests/u64/math.simf new file mode 100644 index 0000000..f47bd9b --- /dev/null +++ b/simf/tests/u64/math.simf @@ -0,0 +1,93 @@ +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::tests::support::expect::{assert_bool_by_opt_64, assert_eq_opt_64}; +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u64 = witness::FIRST_ARG; + let b: u64 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + // add + match is_selected(0, fn_idx) { + true => { + assert_eq_opt_64(checked_add_64(a, b), expected); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert!(jet::eq_64(safe_add_64(a, b), unwrap(expected))); + }, + false => (), + }; + + // sub + match is_selected(2, fn_idx) { + true => { + assert_eq_opt_64(checked_sub_64(a, b), expected); + }, + false => (), + }; + match is_selected(3, fn_idx) { + true => { + assert!(jet::eq_64(safe_sub_64(a, b), unwrap(expected))); + }, + false => (), + }; + + // mul + match is_selected(4, fn_idx) { + true => { + assert_eq_opt_64(checked_mul_64(a, b), expected); + }, + false => (), + }; + match is_selected(5, fn_idx) { + true => { + assert!(jet::eq_64(safe_mul_64(a, b), unwrap(expected))); + }, + false => (), + }; + + // div + match is_selected(6, fn_idx) { + true => { + assert_eq_opt_64(checked_div_64(a, b), expected); + }, + false => (), + }; + match is_selected(7, fn_idx) { + true => { + assert!(jet::eq_64(safe_div_64(a, b), unwrap(expected))); + }, + false => (), + }; + + // gt, ge + match is_selected(8, fn_idx) { + true => { + assert_bool_by_opt_64(gt_64(a, b), expected); + }, + false => (), + }; + match is_selected(9, fn_idx) { + true => { + assert_bool_by_opt_64(ge_64(a, b), expected); + }, + false => (), + }; +} diff --git a/simf/u64_mul_div_test.simf b/simf/tests/u64/mul_div.simf similarity index 83% rename from simf/u64_mul_div_test.simf rename to simf/tests/u64/mul_div.simf index fe99df2..ea660c8 100644 --- a/simf/u64_mul_div_test.simf +++ b/simf/tests/u64/mul_div.simf @@ -1,7 +1,7 @@ use crate::lib::u64::mul_div::mul_div_64; - use crate::lib::asserts::assert_eq_64; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -12,7 +12,7 @@ fn main() { let expected: Option = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { assert_eq_64(mul_div_64(a, b, c), unwrap(expected)); }, diff --git a/simf/u8_convert_test.simf b/simf/tests/u8/convert.simf similarity index 87% rename from simf/u8_convert_test.simf rename to simf/tests/u8/convert.simf index 2256638..91d8d51 100644 --- a/simf/u8_convert_test.simf +++ b/simf/tests/u8/convert.simf @@ -7,7 +7,6 @@ use crate::lib::u8::convert::{ split_u8_into_u1, safe_u8_to_u1 }; -use crate::lib::u128::bit::eq_128; use crate::lib::asserts::{ assert_eq_1, assert_eq_16, @@ -16,7 +15,8 @@ use crate::lib::asserts::{ assert_eq_128, assert_eq_256 }; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -25,7 +25,7 @@ fn main() { let expected: u256 = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); @@ -35,7 +35,7 @@ fn main() { false => (), }; - match if_test_this_function(1, fn_idx) { + match is_selected(1, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let (_, expected): (u32, u32) = ::into(expected); @@ -45,7 +45,7 @@ fn main() { false => (), }; - match if_test_this_function(2, fn_idx) { + match is_selected(2, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); @@ -54,7 +54,7 @@ fn main() { false => (), }; - match if_test_this_function(3, fn_idx) { + match is_selected(3, fn_idx) { true => { let (_, expected): (u128, u128) = ::into(expected); @@ -63,14 +63,14 @@ fn main() { false => (), }; - match if_test_this_function(4, fn_idx) { + match is_selected(4, fn_idx) { true => { assert_eq_256(u8_to_u256(a), expected); }, false => (), }; - match if_test_this_function(5, fn_idx) { + match is_selected(5, fn_idx) { true => { let ( a7, @@ -108,7 +108,7 @@ fn main() { false => (), }; - match if_test_this_function(6, fn_idx) { + match is_selected(6, fn_idx) { true => { let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); let expected: u1 = jet::rightmost_64_1(expected); diff --git a/simf/tests/u8/math.simf b/simf/tests/u8/math.simf new file mode 100644 index 0000000..f600e63 --- /dev/null +++ b/simf/tests/u8/math.simf @@ -0,0 +1,93 @@ +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::tests::support::expect::{assert_bool_by_opt_8, assert_eq_opt_8}; +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u8 = witness::FIRST_ARG; + let b: u8 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + // add + match is_selected(0, fn_idx) { + true => { + assert_eq_opt_8(checked_add_8(a, b), expected); + }, + false => (), + }; + match is_selected(1, fn_idx) { + true => { + assert!(jet::eq_8(safe_add_8(a, b), unwrap(expected))); + }, + false => (), + }; + + // sub + match is_selected(2, fn_idx) { + true => { + assert_eq_opt_8(checked_sub_8(a, b), expected); + }, + false => (), + }; + match is_selected(3, fn_idx) { + true => { + assert!(jet::eq_8(safe_sub_8(a, b), unwrap(expected))); + }, + false => (), + }; + + // mul + match is_selected(4, fn_idx) { + true => { + assert_eq_opt_8(checked_mul_8(a, b), expected); + }, + false => (), + }; + match is_selected(5, fn_idx) { + true => { + assert!(jet::eq_8(safe_mul_8(a, b), unwrap(expected))); + }, + false => (), + }; + + // div + match is_selected(6, fn_idx) { + true => { + assert_eq_opt_8(checked_div_8(a, b), expected); + }, + false => (), + }; + match is_selected(7, fn_idx) { + true => { + assert!(jet::eq_8(safe_div_8(a, b), unwrap(expected))); + }, + false => (), + }; + + // gt, ge + match is_selected(8, fn_idx) { + true => { + assert_bool_by_opt_8(gt_8(a, b), expected); + }, + false => (), + }; + match is_selected(9, fn_idx) { + true => { + assert_bool_by_opt_8(ge_8(a, b), expected); + }, + false => (), + }; +} diff --git a/simf/u8_mul_div_test.simf b/simf/tests/u8/mul_div.simf similarity index 82% rename from simf/u8_mul_div_test.simf rename to simf/tests/u8/mul_div.simf index 0cb508d..45be7ec 100644 --- a/simf/u8_mul_div_test.simf +++ b/simf/tests/u8/mul_div.simf @@ -1,7 +1,7 @@ use crate::lib::u8::mul_div::mul_div_8; - use crate::lib::asserts::assert_eq_8; -use crate::helper::if_test_this_function; + +use crate::tests::support::dispatch::is_selected; fn main() { let fn_idx: u8 = witness::FUNCTION_INDEX; @@ -12,7 +12,7 @@ fn main() { let expected: Option = witness::EXPECTED; - match if_test_this_function(0, fn_idx) { + match is_selected(0, fn_idx) { true => { assert_eq_8(mul_div_8(a, b, c), unwrap(expected)); }, diff --git a/simf/u128_math_test.simf b/simf/u128_math_test.simf deleted file mode 100644 index 1c73009..0000000 --- a/simf/u128_math_test.simf +++ /dev/null @@ -1,63 +0,0 @@ -use crate::lib::u128::math::{ - checked_add_128, - safe_add_128, - checked_sub_128, - safe_sub_128, - checked_mul_128, - safe_mul_128, - checked_div_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; - -/// 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: u128) => assert_eq_128(unwrap(result), e), - None => assert_none_128(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(_: u128) => assert!(result), - None => assert!(not(result)), - } -} - -fn main() { - let fn_idx: u8 = witness::FUNCTION_INDEX; - - let a: u128 = witness::FIRST_ARG; - let b: u128 = 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_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 => (), }; - - // 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 => (), }; - - // 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 => (), }; - - // 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 => (), }; - - // gt, ge - match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_128(a, b), expected); }, false => (), }; - match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_128(a, b), expected); }, false => (), }; -} diff --git a/simf/u128_test_compare.simf b/simf/u128_test_compare.simf deleted file mode 100644 index e9badb1..0000000 --- a/simf/u128_test_compare.simf +++ /dev/null @@ -1,20 +0,0 @@ -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_math_test.simf b/simf/u16_math_test.simf deleted file mode 100644 index bc7aece..0000000 --- a/simf/u16_math_test.simf +++ /dev/null @@ -1,51 +0,0 @@ -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; - -/// Asserts a `checked_*` result equals the expected Option. -/// `None` encodes the overflow case, `Some(e)` the fitting case, so a single -/// witness value carries both, removing the need for a separate overflow flag. -fn assert_eq_opt(result: Option, expected: Option) { - match expected { - None => assert_none_16(result), - Some(e: u16) => assert_eq_16(unwrap(result), e), - } -} - -/// Asserts a result equals the expected bool value. -/// `None` encodes `false`, and `Some(_)` encodes `true`. -fn assert_bool_by_opt(result: bool, expected: Option) { - match expected { - Some(_: u16) => assert!(result), - None => assert!(not(result)), - } -} - -fn main() { - let fn_idx: u8 = witness::FUNCTION_INDEX; - - let a: u16 = witness::FIRST_ARG; - let b: u16 = witness::SECOND_ARG; - let expected: Option = witness::EXPECTED; - - // add - match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_16(a, b), expected); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { assert!(jet::eq_16(safe_add_16(a, b), unwrap(expected))); }, false => (), }; - - // sub - match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_16(a, b), expected); }, false => (), }; - match if_test_this_function(3, fn_idx) { true => { assert!(jet::eq_16(safe_sub_16(a, b), unwrap(expected))); }, false => (), }; - - // mul - match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_16(a, b), expected); }, false => (), }; - match if_test_this_function(5, fn_idx) { true => { assert!(jet::eq_16(safe_mul_16(a, b), unwrap(expected))); }, false => (), }; - - // div - match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_16(a, b), expected); }, false => (), }; - match if_test_this_function(7, fn_idx) { true => { assert!(jet::eq_16(safe_div_16(a, b), unwrap(expected))); }, false => (), }; - - // gt, ge - match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_16(a, b), expected); }, false => (), }; - match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_16(a, b), expected); }, false => (), }; -} diff --git a/simf/u256_math_test.simf b/simf/u256_math_test.simf deleted file mode 100644 index 9bc8930..0000000 --- a/simf/u256_math_test.simf +++ /dev/null @@ -1,63 +0,0 @@ -use crate::lib::u256::math::{ - checked_add_256, - safe_add_256, - checked_sub_256, - safe_sub_256, - checked_mul_256, - safe_mul_256, - checked_div_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; - -/// 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_add.simf b/simf/u256_test_add.simf deleted file mode 100644 index 5f210af..0000000 --- a/simf/u256_test_add.simf +++ /dev/null @@ -1,47 +0,0 @@ -use crate::lib::u256::math::{add_256, add_256_128, full_add_256}; -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 carry_in: bool = witness::THIRD_ARG; - let expected: Option = witness::EXPECTED; - let expected_bool: bool = witness::EXPECTED_BOOL; - - /// Arithmetic - - 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 => (), - }; - - match if_test_this_function(2, fn_idx) { - true => { - assert_eq_uint_bool(full_add_256(carry_in, a, b), unwrap(expected), expected_bool); - }, - false => (), - }; -} diff --git a/simf/u256_test_compare.simf b/simf/u256_test_compare.simf deleted file mode 100644 index 5192369..0000000 --- a/simf/u256_test_compare.simf +++ /dev/null @@ -1,16 +0,0 @@ -use crate::lib::u256::comparison::{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; - - /// 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 => (), }; - match if_test_this_function(2, fn_idx) { true => { assert_bool(le_256(a, b), expected_bool); }, false => (), }; -} diff --git a/simf/u32_math_test.simf b/simf/u32_math_test.simf deleted file mode 100644 index 0d8274e..0000000 --- a/simf/u32_math_test.simf +++ /dev/null @@ -1,51 +0,0 @@ -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; - -/// Asserts a `checked_*` result equals the expected Option. -/// `None` encodes the overflow case, `Some(e)` the fitting case, so a single -/// witness value carries both, removing the need for a separate overflow flag. -fn assert_eq_opt(result: Option, expected: Option) { - match expected { - None => assert_none_32(result), - Some(e: u32) => assert_eq_32(unwrap(result), e), - } -} - -/// Asserts a result equals the expected bool value. -/// `None` encodes `false`, and `Some(_)` encodes `true`. -fn assert_bool_by_opt(result: bool, expected: Option) { - match expected { - Some(_: u32) => assert!(result), - None => assert!(not(result)), - } -} - -fn main() { - let fn_idx: u8 = witness::FUNCTION_INDEX; - - let a: u32 = witness::FIRST_ARG; - let b: u32 = witness::SECOND_ARG; - let expected: Option = witness::EXPECTED; - - // add - match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_32(a, b), expected); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { assert!(jet::eq_32(safe_add_32(a, b), unwrap(expected))); }, false => (), }; - - // sub - match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_32(a, b), expected); }, false => (), }; - match if_test_this_function(3, fn_idx) { true => { assert!(jet::eq_32(safe_sub_32(a, b), unwrap(expected))); }, false => (), }; - - // mul - match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_32(a, b), expected); }, false => (), }; - match if_test_this_function(5, fn_idx) { true => { assert!(jet::eq_32(safe_mul_32(a, b), unwrap(expected))); }, false => (), }; - - // div - match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_32(a, b), expected); }, false => (), }; - match if_test_this_function(7, fn_idx) { true => { assert!(jet::eq_32(safe_div_32(a, b), unwrap(expected))); }, false => (), }; - - // gt, ge - match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_32(a, b), expected); }, false => (), }; - match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_32(a, b), expected); }, false => (), }; -} diff --git a/simf/u64_math_test.simf b/simf/u64_math_test.simf deleted file mode 100644 index 0866a57..0000000 --- a/simf/u64_math_test.simf +++ /dev/null @@ -1,62 +0,0 @@ -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; -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: u64) => assert_eq_64(unwrap(result), e), - None => assert_none_64(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(_: u64) => assert!(result), - None => assert!(not(result)), - } -} - -fn main() { - let fn_idx: u8 = witness::FUNCTION_INDEX; - - let a: u64 = witness::FIRST_ARG; - let b: u64 = witness::SECOND_ARG; - let expected: Option = witness::EXPECTED; - - // 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 => (), }; - - // sub - match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_64(a, b), expected); }, false => (), }; - match if_test_this_function(3, fn_idx) { true => { assert!(jet::eq_64(safe_sub_64(a, b), unwrap(expected))); }, false => (), }; - - // mul - match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_64(a, b), expected); }, false => (), }; - match if_test_this_function(5, fn_idx) { true => { assert!(jet::eq_64(safe_mul_64(a, b), unwrap(expected))); }, false => (), }; - - // div - match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_64(a, b), expected); }, false => (), }; - match if_test_this_function(7, fn_idx) { true => { assert!(jet::eq_64(safe_div_64(a, b), unwrap(expected))); }, false => (), }; - - // 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 => (), }; -} diff --git a/simf/u8_math_test.simf b/simf/u8_math_test.simf deleted file mode 100644 index d8c4f44..0000000 --- a/simf/u8_math_test.simf +++ /dev/null @@ -1,62 +0,0 @@ -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; - -/// 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: u8) => assert_eq_8(unwrap(result), e), - None => assert_none_8(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(_: u8) => assert!(result), - None => assert!(not(result)), - } -} - -fn main() { - let fn_idx: u8 = witness::FUNCTION_INDEX; - - let a: u8 = witness::FIRST_ARG; - let b: u8 = witness::SECOND_ARG; - 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 => (), }; - - // 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 => (), }; - - // 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 => (), }; - - // 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 => (), }; - - // 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 => (), }; -} diff --git a/tests/asserts_test.rs b/tests/asserts_test.rs deleted file mode 100644 index 94fcee0..0000000 --- a/tests/asserts_test.rs +++ /dev/null @@ -1,499 +0,0 @@ -mod common; - -use rand::Rng; - -use common::core::{Expect, run}; - -use simplicityhl_std::artifacts::asserts_test::AssertsTestProgram; -use simplicityhl_std::artifacts::asserts_test::derived_asserts_test::{ - AssertsTestArguments, AssertsTestWitness, -}; - -// Dispatch indices — must match the `if_test_this_function(N, ..)` arms in -// simf/asserts_test.simf. -enum FunctionToTest { - AssertEq1, - AssertEq8, - AssertEq16, - AssertEq32, - AssertEq64, - AssertEq128, - AssertEq256, - AssertEqBool, - - AssertNone1, - AssertNone8, - AssertNone16, - AssertNone32, - AssertNone64, - AssertNone128, - AssertNone256, -} - -const DEFAULT_SOME_U8: Option = Some(0); -const DEFAULT_SOME_U16: Option = Some(0); -const DEFAULT_SOME_U32: Option = Some(0); -const DEFAULT_SOME_U64: Option = Some(0); -const DEFAULT_SOME_U128: Option = Some(0); -const DEFAULT_SOME_U256: Option<[u8; 32]> = Some([0; 32]); - -fn program() -> AssertsTestProgram { - AssertsTestProgram::new(&AssertsTestArguments {}) -} - -/// Returns two values in `[min, max]` that are equal when `same`, distinct otherwise. -pub fn generate_uints_in_one_range(same: bool, min_val: u128, max_val: u128) -> (u128, u128) { - let some_u = rand::thread_rng().gen_range(min_val..=max_val); - - if same { - return (some_u, some_u); - } - - assert!( - min_val != max_val, - "cannot generate distinct values in a single-value range" - ); - - let mut other_u = rand::thread_rng().gen_range(min_val..=max_val); - - while other_u == some_u { - other_u = rand::thread_rng().gen_range(min_val..=max_val); - } - - (some_u, other_u) -} - -/// Builds the witness for one assert call. `same` controls the two `assert_eq` -/// args; `none` makes the single `assert_none` arg `None`. -fn build_witness(function: FunctionToTest, same: bool, none: bool) -> AssertsTestWitness { - let mut witness = AssertsTestWitness { - function_index: 0, - first_arg_u1: DEFAULT_SOME_U8, // u1 in Simplicity is represented as u8 - second_arg_u1: DEFAULT_SOME_U8, - first_arg_u8: DEFAULT_SOME_U8, - second_arg_u8: DEFAULT_SOME_U8, - first_arg_u16: DEFAULT_SOME_U16, - second_arg_u16: DEFAULT_SOME_U16, - first_arg_u32: DEFAULT_SOME_U32, - second_arg_u32: DEFAULT_SOME_U32, - first_arg_u64: DEFAULT_SOME_U64, - second_arg_u64: DEFAULT_SOME_U64, - first_arg_u128: DEFAULT_SOME_U128, - second_arg_u128: DEFAULT_SOME_U128, - first_arg_u256: DEFAULT_SOME_U256, - second_arg_u256: DEFAULT_SOME_U256, - }; - - match function { - FunctionToTest::AssertEq1 => { - 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::AssertEq8 => { - let (a, b) = generate_uints_in_one_range(same, 0, u8::MAX as u128); - (witness.first_arg_u8, witness.second_arg_u8) = (Some(a as u8), Some(b as u8)); - } - FunctionToTest::AssertEq16 => { - let (a, b) = generate_uints_in_one_range(same, 0, u16::MAX as u128); - (witness.first_arg_u16, witness.second_arg_u16) = (Some(a as u16), Some(b as u16)); - } - FunctionToTest::AssertEq32 => { - let (a, b) = generate_uints_in_one_range(same, 0, u32::MAX as u128); - (witness.first_arg_u32, witness.second_arg_u32) = (Some(a as u32), Some(b as u32)); - } - FunctionToTest::AssertEq64 => { - let (a, b) = generate_uints_in_one_range(same, 0, u64::MAX as u128); - (witness.first_arg_u64, witness.second_arg_u64) = (Some(a as u64), Some(b as u64)); - } - FunctionToTest::AssertEq128 => { - let (a, b) = generate_uints_in_one_range(same, 0, u128::MAX); - (witness.first_arg_u128, witness.second_arg_u128) = (Some(a), Some(b)); - } - FunctionToTest::AssertEq256 => { - let (a, b) = generate_uints_in_one_range(same, 0, u8::MAX as u128); - (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; - } - } - FunctionToTest::AssertNone8 => { - if none { - witness.first_arg_u8 = None; - } - } - FunctionToTest::AssertNone16 => { - if none { - witness.first_arg_u16 = None; - } - } - FunctionToTest::AssertNone32 => { - if none { - witness.first_arg_u32 = None; - } - } - FunctionToTest::AssertNone64 => { - if none { - witness.first_arg_u64 = None; - } - } - FunctionToTest::AssertNone128 => { - if none { - witness.first_arg_u128 = None; - } - } - FunctionToTest::AssertNone256 => { - if none { - witness.first_arg_u256 = None; - } - } - } - - witness.function_index = function as u8; - witness -} - -fn run_assert( - context: &simplex::TestContext, - function: FunctionToTest, - same: bool, - none: bool, - expect: Expect, -) -> anyhow::Result<()> { - run( - context, - program(), - build_witness(function, same, none), - expect, - ) -} - -mod asserts_test { - use super::*; - - // ---------- assert_eq: happy = equal args, unhappy = distinct args ---------- - #[simplex::test] - fn assert_eq_1_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert(&context, FunctionToTest::AssertEq1, true, false, Expect::Ok) - } - - #[simplex::test] - fn assert_eq_1_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq1, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_eq_8_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert(&context, FunctionToTest::AssertEq8, true, false, Expect::Ok) - } - - #[simplex::test] - fn assert_eq_8_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq8, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_eq_16_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq16, - true, - false, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_eq_16_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq16, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_eq_32_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq32, - true, - false, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_eq_32_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq32, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_eq_64_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq64, - true, - false, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_eq_64_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq64, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_eq_128_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq128, - true, - false, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_eq_128_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq128, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_eq_256_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq256, - true, - false, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_eq_256_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertEq256, - false, - false, - Expect::AssertFailed, - ) - } - - #[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<()> { - run_assert( - &context, - FunctionToTest::AssertNone1, - false, - true, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_none_1_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone1, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_none_8_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone8, - false, - true, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_none_8_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone8, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_none_16_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone16, - false, - true, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_none_16_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone16, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_none_32_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone32, - false, - true, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_none_32_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone32, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_none_64_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone64, - false, - true, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_none_64_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone64, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_none_128_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone128, - false, - true, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_none_128_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone128, - false, - false, - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn assert_none_256_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone256, - false, - true, - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_none_256_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { - run_assert( - &context, - FunctionToTest::AssertNone256, - false, - false, - Expect::AssertFailed, - ) - } -} diff --git a/tests/binary_test.rs b/tests/binary_test.rs deleted file mode 100644 index 2da001e..0000000 --- a/tests/binary_test.rs +++ /dev/null @@ -1,18 +0,0 @@ -mod common; - -use common::core::{Expect, run}; - -use simplicityhl_std::artifacts::binary_test::BinaryTestProgram; -use simplicityhl_std::artifacts::binary_test::derived_binary_test::{ - BinaryTestArguments, BinaryTestWitness, -}; - -mod binary_tests { - use super::*; - - #[simplex::test] - fn binary_test(context: simplex::TestContext) -> anyhow::Result<()> { - let program = BinaryTestProgram::new(&BinaryTestArguments {}); - run(&context, program, BinaryTestWitness {}, Expect::Ok) - } -} diff --git a/tests/helper_test.rs b/tests/helper_test.rs deleted file mode 100644 index 8843942..0000000 --- a/tests/helper_test.rs +++ /dev/null @@ -1,23 +0,0 @@ -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 deleted file mode 100644 index 4eb2fe1..0000000 --- a/tests/op_return_test.rs +++ /dev/null @@ -1,130 +0,0 @@ -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; -use simplicityhl_std::artifacts::op_return_test::derived_op_return_test::{ - OpReturnTestArguments, OpReturnTestWitness, -}; - -enum FunctionToTest { - IsOpReturn, - AssertOutputIsOpReturn, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -const DEFAULT_DATA: &[u8; 1] = &[1]; - -fn program() -> OpReturnTestProgram { - OpReturnTestProgram::new(&OpReturnTestArguments {}) -} - -fn build_witness(function: u8, index: u32, expected: bool) -> OpReturnTestWitness { - OpReturnTestWitness { - function_index: function, - index, - expected, - } -} - -mod op_return_tests { - use super::*; - - #[simplex::test] - fn is_output_op_return_true(context: simplex::TestContext) -> anyhow::Result<()> { - let index = 0; - - run_with_op_return( - &context, - program(), - build_witness(op(FunctionToTest::IsOpReturn), index, true), - Expect::Ok, - DEFAULT_DATA, - ) - } - - #[simplex::test] - fn is_output_op_return_empty_index_false(context: simplex::TestContext) -> anyhow::Result<()> { - let index = rand::thread_rng().gen_range(1..=u32::MAX); - - run_with_op_return( - &context, - program(), - build_witness(op(FunctionToTest::IsOpReturn), index, false), - Expect::Ok, - DEFAULT_DATA, - ) - } - - #[simplex::test] - fn is_output_op_return_false(context: simplex::TestContext) -> anyhow::Result<()> { - let index = 0; - - run( - &context, - program(), - build_witness(op(FunctionToTest::IsOpReturn), index, false), - Expect::Ok, - ) - } - - #[simplex::test] - fn assert_output_is_op_return_pass(context: simplex::TestContext) -> anyhow::Result<()> { - let index = 0; - - run_with_op_return( - &context, - program(), - build_witness( - op(FunctionToTest::AssertOutputIsOpReturn), - index, - DEFAULT_BOOL, - ), - Expect::Ok, - DEFAULT_DATA, - ) - } - - #[simplex::test] - fn assert_output_is_op_return_empty_index_fail( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let index = rand::thread_rng().gen_range(1..=u32::MAX); - - run_with_op_return( - &context, - program(), - build_witness( - op(FunctionToTest::AssertOutputIsOpReturn), - index, - DEFAULT_BOOL, - ), - Expect::AssertFailed, - DEFAULT_DATA, - ) - } - - #[simplex::test] - fn assert_output_is_op_return_fail(context: simplex::TestContext) -> anyhow::Result<()> { - let index = 0; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::AssertOutputIsOpReturn), - index, - DEFAULT_BOOL, - ), - Expect::AssertFailed, - ) - } -} diff --git a/tests/secp256k1_operations_test.rs b/tests/secp256k1_operations_test.rs deleted file mode 100644 index a834f63..0000000 --- a/tests/secp256k1_operations_test.rs +++ /dev/null @@ -1,569 +0,0 @@ -mod common; - -use num_bigint::BigUint; -use num_traits::Zero; -use rand::{RngCore, rngs::OsRng}; -use secp256k1_zkp::{PublicKey, Secp256k1, SecretKey, rand::rngs::OsRng as SecpOsRng}; - -use common::core::{Expect, run}; - -use simplicityhl_std::artifacts::secp256k1_operations_test::Secp256k1OperationsTestProgram; -use simplicityhl_std::artifacts::secp256k1_operations_test::derived_secp256k1_operations_test::{ - Secp256k1OperationsTestArguments, Secp256k1OperationsTestWitness, -}; - -enum FunctionToTest { - GeToPoint, - PointToGej, - FeSub, - ScalarSub, - GejSub, - FeEq, - ScalarEq, - GeEq, - GejPointEq, - SafeGejNormalize, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -const DEFAULT_UINT: [u8; 32] = [0u8; 32]; -const DEFAULT_GE: ([u8; 32], [u8; 32]) = ([0u8; 32], [0u8; 32]); -const DEFAULT_GEJ: (([u8; 32], [u8; 32]), [u8; 32]) = (([0u8; 32], [0u8; 32]), [0u8; 32]); -const DEFAULT_POINT: (u8, [u8; 32]) = (0, [0u8; 32]); - -// FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F -const SECP_P: [u8; 32] = [ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, -]; - -// FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 -const SECP_N: [u8; 32] = [ - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, - 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41, -]; - -fn program() -> Secp256k1OperationsTestProgram { - Secp256k1OperationsTestProgram::new(&Secp256k1OperationsTestArguments {}) -} - -#[allow(clippy::too_many_arguments)] -fn build_witness( - function: u8, - first_uint: [u8; 32], - second_uint: [u8; 32], - first_ge: ([u8; 32], [u8; 32]), - second_ge: ([u8; 32], [u8; 32]), - first_gej: (([u8; 32], [u8; 32]), [u8; 32]), - second_gej: (([u8; 32], [u8; 32]), [u8; 32]), - first_point: (u8, [u8; 32]), - expected_uint: [u8; 32], - expected_ge: ([u8; 32], [u8; 32]), - expected_gej: (([u8; 32], [u8; 32]), [u8; 32]), - expected_point: (u8, [u8; 32]), -) -> Secp256k1OperationsTestWitness { - Secp256k1OperationsTestWitness { - function_index: function, - first_uint, - second_uint, - first_ge, - second_ge, - first_gej, - second_gej, - first_point, - expected_uint, - expected_ge, - expected_gej, - expected_point, - } -} - -fn secp_p() -> BigUint { - BigUint::from_bytes_be(&SECP_P) -} -fn secp_n() -> BigUint { - BigUint::from_bytes_be(&SECP_N) -} - -fn to_32_be(x: &BigUint) -> [u8; 32] { - let b = x.to_bytes_be(); - - let mut out = [0u8; 32]; - out[32 - b.len()..].copy_from_slice(&b); - - out -} - -fn mod_p(x: &BigUint) -> BigUint { - x % secp_p() -} -fn mod_n(x: &BigUint) -> BigUint { - x % secp_n() -} - -fn fe_sub_ref(a: [u8; 32], b: [u8; 32]) -> [u8; 32] { - let p = secp_p(); - let a = BigUint::from_bytes_be(&a) % &p; - let b = BigUint::from_bytes_be(&b) % &p; - - to_32_be(&((a + &p - b) % &p)) -} - -fn scalar_sub_ref(a: [u8; 32], b: [u8; 32]) -> [u8; 32] { - let n = secp_n(); - let a = BigUint::from_bytes_be(&a) % &n; - let b = BigUint::from_bytes_be(&b) % &n; - - to_32_be(&((a + &n - b) % &n)) -} - -fn fe_negate_ref(a: [u8; 32]) -> [u8; 32] { - let p = secp_p(); - let a = BigUint::from_bytes_be(&a) % &p; - - if a.is_zero() { - [0u8; 32] - } else { - to_32_be(&(&p - &a)) - } -} - -fn fe_mul_ref(a: [u8; 32], b: [u8; 32]) -> [u8; 32] { - let p = secp_p(); - let a = BigUint::from_bytes_be(&a); - let b = BigUint::from_bytes_be(&b); - - to_32_be(&((a * b) % &p)) -} - -// Randomness helpers -fn random_uint_bytes() -> [u8; 32] { - let mut b = [0u8; 32]; - OsRng.fill_bytes(&mut b); - - b -} - -fn random_fe_bytes() -> [u8; 32] { - to_32_be(&mod_p(&BigUint::from_bytes_be(&random_uint_bytes()))) -} - -fn random_scalar_bytes() -> [u8; 32] { - to_32_be(&mod_n(&BigUint::from_bytes_be(&random_uint_bytes()))) -} - -fn random_ge_bytes() -> ([u8; 32], [u8; 32]) { - let secp = Secp256k1::new(); - let sk = SecretKey::new(&mut SecpOsRng); - let pk = PublicKey::from_secret_key(&secp, &sk); - let ser = pk.serialize_uncompressed(); // 0x04 || x(32) || y(32) - - let mut x = [0u8; 32]; - x.copy_from_slice(&ser[1..33]); - - let mut y = [0u8; 32]; - y.copy_from_slice(&ser[33..65]); - - (x, y) -} - -fn ge_to_gej(ge: ([u8; 32], [u8; 32])) -> (([u8; 32], [u8; 32]), [u8; 32]) { - let mut one = [0u8; 32]; - one[31] = 1; - - (ge, one) -} - -fn compress(ge: ([u8; 32], [u8; 32])) -> (u8, [u8; 32]) { - (ge.1[31] & 1, ge.0) -} - -fn pk_to_gej(pk: &PublicKey) -> (([u8; 32], [u8; 32]), [u8; 32]) { - let ser = pk.serialize_uncompressed(); - - let mut x = [0u8; 32]; - x.copy_from_slice(&ser[1..33]); - - let mut y = [0u8; 32]; - y.copy_from_slice(&ser[33..65]); - - ge_to_gej((x, y)) -} - -mod secp256k1_operations_tests { - use super::*; - - // 0. ge_to_point - #[simplex::test] - fn ge_to_point_matches_parity(context: simplex::TestContext) -> anyhow::Result<()> { - // Sample one on-curve point; whatever parity it has, that's what we expect - // ge_to_point to produce. Covers both parities across repeated runs. - let ge = random_ge_bytes(); - let expected_parity = ge.1[31] & 1; // 0 (even y) or 1 (odd y) - - run( - &context, - program(), - build_witness( - op(FunctionToTest::GeToPoint), - DEFAULT_UINT, - DEFAULT_UINT, - ge, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_GEJ, - DEFAULT_POINT, - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GEJ, - (expected_parity, ge.0), - ), - Expect::Ok, - ) - } - - // 1. point_to_gej - #[simplex::test] - fn point_to_gej_roundtrip(context: simplex::TestContext) -> anyhow::Result<()> { - let ge = random_ge_bytes(); - let point = compress(ge); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::PointToGej), - DEFAULT_UINT, - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_GEJ, - point, - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GEJ, - point, - ), - Expect::Ok, - ) - } - - // 2. fe_sub - #[simplex::test] - fn fe_sub_self_is_zero(context: simplex::TestContext) -> anyhow::Result<()> { - let a = random_fe_bytes(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::FeSub), - a, - a, - DEFAULT_GE, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_GEJ, - DEFAULT_POINT, - [0u8; 32], - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_POINT, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn fe_sub_matches_reference(context: simplex::TestContext) -> anyhow::Result<()> { - let a = random_fe_bytes(); - let b = random_fe_bytes(); - let exp = fe_sub_ref(a, b); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::FeSub), - a, - b, - DEFAULT_GE, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_GEJ, - DEFAULT_POINT, - exp, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_POINT, - ), - Expect::Ok, - ) - } - - // 3. scalar_sub - #[simplex::test] - fn scalar_sub_matches_reference(context: simplex::TestContext) -> anyhow::Result<()> { - let a = random_scalar_bytes(); - let b = random_scalar_bytes(); - let exp = scalar_sub_ref(a, b); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::ScalarSub), - a, - b, - DEFAULT_GE, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_GEJ, - DEFAULT_POINT, - exp, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_POINT, - ), - Expect::Ok, - ) - } - - // 4. gej_sub - #[simplex::test] - fn gej_sub_matches_reference(context: simplex::TestContext) -> anyhow::Result<()> { - let secp = Secp256k1::new(); - let sk_p = SecretKey::new(&mut SecpOsRng); - let sk_q = SecretKey::new(&mut SecpOsRng); - - let p = PublicKey::from_secret_key(&secp, &sk_p); - let q = PublicKey::from_secret_key(&secp, &sk_q); - let diff = p.combine(&q.negate(&secp)).expect("p - q non-infinity"); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::GejSub), - DEFAULT_UINT, - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GE, - pk_to_gej(&p), - pk_to_gej(&q), - DEFAULT_POINT, - DEFAULT_UINT, - DEFAULT_GE, - pk_to_gej(&diff), - DEFAULT_POINT, - ), - Expect::Ok, - ) - } - - // 5. fe_eq - #[simplex::test] - fn fe_eq_reflexive(context: simplex::TestContext) -> anyhow::Result<()> { - let a = random_fe_bytes(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::FeEq), - a, - a, - DEFAULT_GE, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_GEJ, - DEFAULT_POINT, - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_POINT, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn fe_eq_zero_and_p_are_equal(context: simplex::TestContext) -> anyhow::Result<()> { - run( - &context, - program(), - build_witness( - op(FunctionToTest::FeEq), - [0u8; 32], - SECP_P, - DEFAULT_GE, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_GEJ, - DEFAULT_POINT, - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_POINT, - ), - Expect::Ok, - ) - } - - // 6. scalar_eq - #[simplex::test] - fn scalar_eq_s_and_s_plus_n_are_equal(context: simplex::TestContext) -> anyhow::Result<()> { - // s = 5. s + n has no carry past the low byte. - let mut s = [0u8; 32]; - s[31] = 5; - - let mut s_plus_n = SECP_N; - s_plus_n[31] = s_plus_n[31].wrapping_add(5); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::ScalarEq), - s, - s_plus_n, - DEFAULT_GE, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_GEJ, - DEFAULT_POINT, - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_POINT, - ), - Expect::Ok, - ) - } - - // 7. ge_eq - #[simplex::test] - fn ge_eq_rejects_negation(context: simplex::TestContext) -> anyhow::Result<()> { - let ge = random_ge_bytes(); - let neg_y = fe_negate_ref(ge.1); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::GeEq), - DEFAULT_UINT, - DEFAULT_UINT, - ge, - (ge.0, neg_y), - DEFAULT_GEJ, - DEFAULT_GEJ, - DEFAULT_POINT, - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_POINT, - ), - Expect::AssertFailed, - ) - } - - // 8. gej_point_eq - #[simplex::test] - fn gej_point_eq_rescaled(context: simplex::TestContext) -> anyhow::Result<()> { - // (λ²X, λ³Y, λZ) is the same affine point as (X, Y, 1). - let ge = random_ge_bytes(); - let lambda = random_fe_bytes(); - let l2 = fe_mul_ref(lambda, lambda); - let l3 = fe_mul_ref(l2, lambda); - let g = ((fe_mul_ref(ge.0, l2), fe_mul_ref(ge.1, l3)), lambda); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::GejPointEq), - DEFAULT_UINT, - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GE, - g, - DEFAULT_GEJ, - compress(ge), - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_POINT, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn gej_point_eq_rejects_negation(context: simplex::TestContext) -> anyhow::Result<()> { - // Property under test: gej_point_eq(P, -P) == false. - // - // Construction: - // ge = random on-curve point P = (x, y) - // (parity, x) = compressed form of +P - // ge_to_gej(ge) = Gej encoding of +P - // (parity ^ 1, x) = compressed form of -P - // - // decompress((parity ^ 1, x)) inside gej_point_eq recovers (x, -y) = -P. - // Since P ≠ -P on secp256k1, the equivalence must return false. - // expected_bool = false locks that in. - let ge = random_ge_bytes(); - let (parity, x) = compress(ge); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::GejPointEq), - DEFAULT_UINT, - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GE, - ge_to_gej(ge), - DEFAULT_GEJ, - (parity ^ 1, x), - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GEJ, - DEFAULT_POINT, - ), - Expect::AssertFailed, - ) - } - - // 9. safe_gej_normalize - #[simplex::test] - fn safe_gej_normalize_roundtrip(context: simplex::TestContext) -> anyhow::Result<()> { - let ge = random_ge_bytes(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::SafeGejNormalize), - DEFAULT_UINT, - DEFAULT_UINT, - DEFAULT_GE, - DEFAULT_GE, - ge_to_gej(ge), - DEFAULT_GEJ, - DEFAULT_POINT, - DEFAULT_UINT, - ge, - DEFAULT_GEJ, - DEFAULT_POINT, - ), - Expect::Ok, - ) - } -} diff --git a/tests/stdlib/asserts.rs b/tests/stdlib/asserts.rs new file mode 100644 index 0000000..b3ca487 --- /dev/null +++ b/tests/stdlib/asserts.rs @@ -0,0 +1,352 @@ +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::asserts::AssertsProgram as AssertsTestProgram; +use simplicityhl_std::artifacts::tests::asserts::derived_asserts::{ + AssertsArguments as AssertsTestArguments, AssertsWitness as AssertsTestWitness, +}; + +// Dispatch indices which must match the `is_selected(N, ..)` arms in +// simf/tests/asserts.simf. +use FunctionToTest::*; + +enum FunctionToTest { + AssertEq1, + AssertEq8, + AssertEq16, + AssertEq32, + AssertEq64, + AssertEq128, + AssertEq256, + AssertEqBool, + + AssertNone1, + AssertNone8, + AssertNone16, + AssertNone32, + AssertNone64, + AssertNone128, + AssertNone256, +} + +const DEFAULT_SOME_U8: Option = Some(0); +const DEFAULT_SOME_U16: Option = Some(0); +const DEFAULT_SOME_U32: Option = Some(0); +const DEFAULT_SOME_U64: Option = Some(0); +const DEFAULT_SOME_U128: Option = Some(0); +const DEFAULT_SOME_U256: Option<[u8; 32]> = Some([0; 32]); + +fn program() -> AssertsTestProgram { + AssertsTestProgram::new(&AssertsTestArguments {}) +} + +/// Returns two values in `[min, max]` that are equal when `same`, distinct otherwise. +pub fn generate_uints_in_one_range(same: bool, min_val: u128, max_val: u128) -> (u128, u128) { + let some_u = rand::thread_rng().gen_range(min_val..=max_val); + + if same { + return (some_u, some_u); + } + + assert!( + min_val != max_val, + "cannot generate distinct values in a single-value range" + ); + + let mut other_u = rand::thread_rng().gen_range(min_val..=max_val); + + while other_u == some_u { + other_u = rand::thread_rng().gen_range(min_val..=max_val); + } + + (some_u, other_u) +} + +/// Builds the witness for one assert call. `same` controls the two `assert_eq` +/// args; `none` makes the single `assert_none` arg `None`. +fn build_witness(function: FunctionToTest, same: bool, none: bool) -> AssertsTestWitness { + let mut witness = AssertsTestWitness { + function_index: 0, + first_arg_u1: DEFAULT_SOME_U8, // u1 in Simplicity is represented as u8 + second_arg_u1: DEFAULT_SOME_U8, + first_arg_u8: DEFAULT_SOME_U8, + second_arg_u8: DEFAULT_SOME_U8, + first_arg_u16: DEFAULT_SOME_U16, + second_arg_u16: DEFAULT_SOME_U16, + first_arg_u32: DEFAULT_SOME_U32, + second_arg_u32: DEFAULT_SOME_U32, + first_arg_u64: DEFAULT_SOME_U64, + second_arg_u64: DEFAULT_SOME_U64, + first_arg_u128: DEFAULT_SOME_U128, + second_arg_u128: DEFAULT_SOME_U128, + first_arg_u256: DEFAULT_SOME_U256, + second_arg_u256: DEFAULT_SOME_U256, + }; + + match function { + FunctionToTest::AssertEq1 => { + 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::AssertEq8 => { + let (a, b) = generate_uints_in_one_range(same, 0, u8::MAX as u128); + (witness.first_arg_u8, witness.second_arg_u8) = (Some(a as u8), Some(b as u8)); + } + FunctionToTest::AssertEq16 => { + let (a, b) = generate_uints_in_one_range(same, 0, u16::MAX as u128); + (witness.first_arg_u16, witness.second_arg_u16) = (Some(a as u16), Some(b as u16)); + } + FunctionToTest::AssertEq32 => { + let (a, b) = generate_uints_in_one_range(same, 0, u32::MAX as u128); + (witness.first_arg_u32, witness.second_arg_u32) = (Some(a as u32), Some(b as u32)); + } + FunctionToTest::AssertEq64 => { + let (a, b) = generate_uints_in_one_range(same, 0, u64::MAX as u128); + (witness.first_arg_u64, witness.second_arg_u64) = (Some(a as u64), Some(b as u64)); + } + FunctionToTest::AssertEq128 => { + let (a, b) = generate_uints_in_one_range(same, 0, u128::MAX); + (witness.first_arg_u128, witness.second_arg_u128) = (Some(a), Some(b)); + } + FunctionToTest::AssertEq256 => { + let (a, b) = generate_uints_in_one_range(same, 0, u8::MAX as u128); + (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; + } + } + FunctionToTest::AssertNone8 => { + if none { + witness.first_arg_u8 = None; + } + } + FunctionToTest::AssertNone16 => { + if none { + witness.first_arg_u16 = None; + } + } + FunctionToTest::AssertNone32 => { + if none { + witness.first_arg_u32 = None; + } + } + FunctionToTest::AssertNone64 => { + if none { + witness.first_arg_u64 = None; + } + } + FunctionToTest::AssertNone128 => { + if none { + witness.first_arg_u128 = None; + } + } + FunctionToTest::AssertNone256 => { + if none { + witness.first_arg_u256 = None; + } + } + } + + witness.function_index = function as u8; + witness +} + +/// One assert call, described by what makes it pass. +struct Case { + function: FunctionToTest, + same: bool, + none: bool, +} + +fn case(function: FunctionToTest) -> Case { + Case { + function, + same: false, + none: false, + } +} + +impl Case { + /// Make the two `assert_eq` arguments equal. + fn equal(mut self) -> Self { + self.same = true; + self + } + + /// Make the `assert_none` argument `None`. + fn none(mut self) -> Self { + self.none = true; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + let witness = build_witness(self.function, self.same, self.none); + run(context, program(), witness, expect) + } +} + +// assert_eq: happy = equal args, unhappy = distinct args +#[simplex::test] +fn assert_eq_1_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq1).equal().run(&context) +} + +#[simplex::test] +fn assert_eq_1_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq1).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_eq_8_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq8).equal().run(&context) +} + +#[simplex::test] +fn assert_eq_8_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq8).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_eq_16_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq16).equal().run(&context) +} + +#[simplex::test] +fn assert_eq_16_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq16).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_eq_32_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq32).equal().run(&context) +} + +#[simplex::test] +fn assert_eq_32_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq32).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_eq_64_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq64).equal().run(&context) +} + +#[simplex::test] +fn assert_eq_64_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq64).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_eq_128_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq128).equal().run(&context) +} + +#[simplex::test] +fn assert_eq_128_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq128).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_eq_256_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq256).equal().run(&context) +} + +#[simplex::test] +fn assert_eq_256_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEq256).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_eq_bool_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEqBool).equal().run(&context) +} + +#[simplex::test] +fn assert_eq_bool_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertEqBool).expecting(&context, Expect::AssertFailed) +} + +// assert_none: happy = None arg, unhappy = Some arg +#[simplex::test] +fn assert_none_1_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone1).none().run(&context) +} + +#[simplex::test] +fn assert_none_1_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone1).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_none_8_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone8).none().run(&context) +} + +#[simplex::test] +fn assert_none_8_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone8).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_none_16_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone16).none().run(&context) +} + +#[simplex::test] +fn assert_none_16_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone16).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_none_32_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone32).none().run(&context) +} + +#[simplex::test] +fn assert_none_32_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone32).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_none_64_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone64).none().run(&context) +} + +#[simplex::test] +fn assert_none_64_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone64).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_none_128_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone128).none().run(&context) +} + +#[simplex::test] +fn assert_none_128_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone128).expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_none_256_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone256).none().run(&context) +} + +#[simplex::test] +fn assert_none_256_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + case(AssertNone256).expecting(&context, Expect::AssertFailed) +} diff --git a/tests/stdlib/binary.rs b/tests/stdlib/binary.rs new file mode 100644 index 0000000..ca00390 --- /dev/null +++ b/tests/stdlib/binary.rs @@ -0,0 +1,12 @@ +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::binary::BinaryProgram as BinaryTestProgram; +use simplicityhl_std::artifacts::tests::binary::derived_binary::{ + BinaryArguments as BinaryTestArguments, BinaryWitness as BinaryTestWitness, +}; + +#[simplex::test] +fn all_ops(context: simplex::TestContext) -> anyhow::Result<()> { + let program = BinaryTestProgram::new(&BinaryTestArguments {}); + run(&context, program, BinaryTestWitness {}, Expect::Ok) +} diff --git a/tests/common/core.rs b/tests/stdlib/common/core.rs similarity index 100% rename from tests/common/core.rs rename to tests/stdlib/common/core.rs diff --git a/tests/common/helper.rs b/tests/stdlib/common/helper.rs similarity index 50% rename from tests/common/helper.rs rename to tests/stdlib/common/helper.rs index 6b5a568..c161b6d 100644 --- a/tests/common/helper.rs +++ b/tests/stdlib/common/helper.rs @@ -7,16 +7,33 @@ use rand::Rng; use crate::common::u256_wrapper::U256Wrapper; -// Shared constants and helper functions used across integration tests - -pub const DEFAULT_BOOL: bool = false; +// Shared helper functions used across integration tests 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 } + +#[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/common/mod.rs b/tests/stdlib/common/mod.rs similarity index 100% rename from tests/common/mod.rs rename to tests/stdlib/common/mod.rs diff --git a/tests/common/u256_wrapper.rs b/tests/stdlib/common/u256_wrapper.rs similarity index 98% rename from tests/common/u256_wrapper.rs rename to tests/stdlib/common/u256_wrapper.rs index 97b2679..79e91ac 100644 --- a/tests/common/u256_wrapper.rs +++ b/tests/stdlib/common/u256_wrapper.rs @@ -7,7 +7,7 @@ use std::ops::{Add, Deref, DerefMut, Div, Mul, Sub}; pub struct U256Wrapper(pub U256); impl U256Wrapper { - pub fn to_be_bytes(&self) -> [u8; 32] { + pub fn to_be_bytes(self) -> [u8; 32] { self.0.to_big_endian() } } diff --git a/tests/common/uint.rs b/tests/stdlib/common/uint.rs similarity index 99% rename from tests/common/uint.rs rename to tests/stdlib/common/uint.rs index b52149e..eeb5ff1 100644 --- a/tests/common/uint.rs +++ b/tests/stdlib/common/uint.rs @@ -12,8 +12,8 @@ use simplex::program::{Program, WitnessTrait}; use super::core::{Expect, run}; /// Dispatch indices for the operations that exist for every unsigned width. -/// These map 1:1 onto the `if_test_this_function(N, ..)` arms in each width's -/// `*_test.simf`. +/// These map 1:1 onto the `is_selected(N, ..)` arms in each width's math test +/// program under `simf/tests`. pub enum CommonOp { CheckedAdd, SafeAdd, diff --git a/tests/stdlib/main.rs b/tests/stdlib/main.rs new file mode 100644 index 0000000..8698e0e --- /dev/null +++ b/tests/stdlib/main.rs @@ -0,0 +1,13 @@ +mod common; + +mod asserts; +mod binary; +mod op_return; +mod secp256k1; +mod u1; +mod u128; +mod u16; +mod u256; +mod u32; +mod u64; +mod u8; diff --git a/tests/stdlib/op_return.rs b/tests/stdlib/op_return.rs new file mode 100644 index 0000000..2b83226 --- /dev/null +++ b/tests/stdlib/op_return.rs @@ -0,0 +1,131 @@ +use rand::Rng; + +use crate::common::core::{Expect, run, run_with_op_return}; + +use simplicityhl_std::artifacts::tests::op_return::OpReturnProgram as OpReturnTestProgram; +use simplicityhl_std::artifacts::tests::op_return::derived_op_return::{ + OpReturnArguments as OpReturnTestArguments, OpReturnWitness as OpReturnTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + IsOpReturn, + AssertOutputIsOpReturn, +} + +const DEFAULT_DATA: &[u8; 1] = &[1]; + +fn program() -> OpReturnTestProgram { + OpReturnTestProgram::new(&OpReturnTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads and whether the +/// spend carries an `OP_RETURN` output. +struct Case { + witness: OpReturnTestWitness, + data: Option<&'static [u8]>, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: OpReturnTestWitness { + function_index: function as u8, + index: 0, + expected: false, + }, + data: None, + } +} + +impl Case { + /// `index`: the output the arm inspects. + fn index(mut self, index: u32) -> Self { + self.witness.index = index; + self + } + + /// `expected`: the boolean the arm should report. + fn flag(mut self, flag: bool) -> Self { + self.witness.expected = flag; + self + } + + /// Add an `OP_RETURN` output carrying `data` to the spend. + fn op_return(mut self, data: &'static [u8]) -> Self { + self.data = Some(data); + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + match self.data { + Some(data) => run_with_op_return(context, program(), self.witness, expect, data), + None => run(context, program(), self.witness, expect), + } + } +} + +#[simplex::test] +fn is_output_op_return_true(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + + case(IsOpReturn) + .index(index) + .flag(true) + .op_return(DEFAULT_DATA) + .run(&context) +} + +#[simplex::test] +fn is_output_op_return_empty_index_false(context: simplex::TestContext) -> anyhow::Result<()> { + let index = rand::thread_rng().gen_range(1..=u32::MAX); + + case(IsOpReturn) + .index(index) + .op_return(DEFAULT_DATA) + .run(&context) +} + +#[simplex::test] +fn is_output_op_return_false(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + + case(IsOpReturn).index(index).run(&context) +} + +#[simplex::test] +fn assert_output_is_op_return_pass(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + + case(AssertOutputIsOpReturn) + .index(index) + .op_return(DEFAULT_DATA) + .run(&context) +} + +#[simplex::test] +fn assert_output_is_op_return_empty_index_fail( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let index = rand::thread_rng().gen_range(1..=u32::MAX); + + case(AssertOutputIsOpReturn) + .index(index) + .op_return(DEFAULT_DATA) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn assert_output_is_op_return_fail(context: simplex::TestContext) -> anyhow::Result<()> { + let index = 0; + + case(AssertOutputIsOpReturn) + .index(index) + .expecting(&context, Expect::AssertFailed) +} diff --git a/tests/stdlib/secp256k1/mod.rs b/tests/stdlib/secp256k1/mod.rs new file mode 100644 index 0000000..9045df1 --- /dev/null +++ b/tests/stdlib/secp256k1/mod.rs @@ -0,0 +1 @@ +mod operations; diff --git a/tests/stdlib/secp256k1/operations.rs b/tests/stdlib/secp256k1/operations.rs new file mode 100644 index 0000000..673dedf --- /dev/null +++ b/tests/stdlib/secp256k1/operations.rs @@ -0,0 +1,411 @@ +use num_bigint::BigUint; +use num_traits::Zero; +use rand::{RngCore, rngs::OsRng}; +use secp256k1_zkp::{PublicKey, Secp256k1, SecretKey, rand::rngs::OsRng as SecpOsRng}; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::secp256k1::operations::OperationsProgram as Secp256k1OperationsTestProgram; +use simplicityhl_std::artifacts::tests::secp256k1::operations::derived_operations::{ + OperationsArguments as Secp256k1OperationsTestArguments, + OperationsWitness as Secp256k1OperationsTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + GeToPoint, + PointToGej, + FeSub, + ScalarSub, + GejSub, + FeEq, + ScalarEq, + GeEq, + GejPointEq, + SafeGejNormalize, +} + +// FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F +const SECP_P: [u8; 32] = [ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFC, 0x2F, +]; + +// FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 +const SECP_N: [u8; 32] = [ + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41, +]; + +fn program() -> Secp256k1OperationsTestProgram { + Secp256k1OperationsTestProgram::new(&Secp256k1OperationsTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: Secp256k1OperationsTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: Secp256k1OperationsTestWitness { + function_index: function as u8, + first_uint: [0; 32], + second_uint: [0; 32], + first_ge: ([0; 32], [0; 32]), + second_ge: ([0; 32], [0; 32]), + first_gej: (([0; 32], [0; 32]), [0; 32]), + second_gej: (([0; 32], [0; 32]), [0; 32]), + first_point: (0, [0; 32]), + expected_uint: [0; 32], + expected_ge: ([0; 32], [0; 32]), + expected_gej: (([0; 32], [0; 32]), [0; 32]), + expected_point: (0, [0; 32]), + }, + } +} + +impl Case { + /// The two field or scalar operands, `first_uint` and `second_uint`. + fn uints(mut self, first_uint: [u8; 32], second_uint: [u8; 32]) -> Self { + self.witness.first_uint = first_uint; + self.witness.second_uint = second_uint; + self + } + + /// Only `first_ge`. + fn ge(mut self, first_ge: ([u8; 32], [u8; 32])) -> Self { + self.witness.first_ge = first_ge; + self + } + + /// The two affine points, `first_ge` and `second_ge`. + fn ges(mut self, first_ge: ([u8; 32], [u8; 32]), second_ge: ([u8; 32], [u8; 32])) -> Self { + self.witness.first_ge = first_ge; + self.witness.second_ge = second_ge; + self + } + + /// Only `first_gej`. + fn gej(mut self, first_gej: (([u8; 32], [u8; 32]), [u8; 32])) -> Self { + self.witness.first_gej = first_gej; + self + } + + /// The two Jacobian points, `first_gej` and `second_gej`. + fn gejs( + mut self, + first_gej: (([u8; 32], [u8; 32]), [u8; 32]), + second_gej: (([u8; 32], [u8; 32]), [u8; 32]), + ) -> Self { + self.witness.first_gej = first_gej; + self.witness.second_gej = second_gej; + self + } + + /// `first_point`: a compressed point, parity byte and x coordinate. + fn point(mut self, first_point: (u8, [u8; 32])) -> Self { + self.witness.first_point = first_point; + self + } + + /// `expected_uint`: the field or scalar the arm should produce. + fn expect_uint(mut self, expected_uint: [u8; 32]) -> Self { + self.witness.expected_uint = expected_uint; + self + } + + /// `expected_ge`: the affine point the arm should produce. + fn expect_ge(mut self, expected_ge: ([u8; 32], [u8; 32])) -> Self { + self.witness.expected_ge = expected_ge; + self + } + + /// `expected_gej`: the Jacobian point the arm should produce. + fn expect_gej(mut self, expected_gej: (([u8; 32], [u8; 32]), [u8; 32])) -> Self { + self.witness.expected_gej = expected_gej; + self + } + + /// `expected_point`: the compressed point the arm should produce. + fn expect_point(mut self, expected_point: (u8, [u8; 32])) -> Self { + self.witness.expected_point = expected_point; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +fn secp_p() -> BigUint { + BigUint::from_bytes_be(&SECP_P) +} +fn secp_n() -> BigUint { + BigUint::from_bytes_be(&SECP_N) +} + +fn to_32_be(x: &BigUint) -> [u8; 32] { + let b = x.to_bytes_be(); + + let mut out = [0u8; 32]; + out[32 - b.len()..].copy_from_slice(&b); + + out +} + +fn mod_p(x: &BigUint) -> BigUint { + x % secp_p() +} +fn mod_n(x: &BigUint) -> BigUint { + x % secp_n() +} + +fn fe_sub_ref(a: [u8; 32], b: [u8; 32]) -> [u8; 32] { + let p = secp_p(); + let a = BigUint::from_bytes_be(&a) % &p; + let b = BigUint::from_bytes_be(&b) % &p; + + to_32_be(&((a + &p - b) % &p)) +} + +fn scalar_sub_ref(a: [u8; 32], b: [u8; 32]) -> [u8; 32] { + let n = secp_n(); + let a = BigUint::from_bytes_be(&a) % &n; + let b = BigUint::from_bytes_be(&b) % &n; + + to_32_be(&((a + &n - b) % &n)) +} + +fn fe_negate_ref(a: [u8; 32]) -> [u8; 32] { + let p = secp_p(); + let a = BigUint::from_bytes_be(&a) % &p; + + if a.is_zero() { + [0u8; 32] + } else { + to_32_be(&(&p - &a)) + } +} + +fn fe_mul_ref(a: [u8; 32], b: [u8; 32]) -> [u8; 32] { + let p = secp_p(); + let a = BigUint::from_bytes_be(&a); + let b = BigUint::from_bytes_be(&b); + + to_32_be(&((a * b) % &p)) +} + +// Randomness helpers +fn random_uint_bytes() -> [u8; 32] { + let mut b = [0u8; 32]; + OsRng.fill_bytes(&mut b); + + b +} + +fn random_fe_bytes() -> [u8; 32] { + to_32_be(&mod_p(&BigUint::from_bytes_be(&random_uint_bytes()))) +} + +fn random_scalar_bytes() -> [u8; 32] { + to_32_be(&mod_n(&BigUint::from_bytes_be(&random_uint_bytes()))) +} + +fn random_ge_bytes() -> ([u8; 32], [u8; 32]) { + let secp = Secp256k1::new(); + let sk = SecretKey::new(&mut SecpOsRng); + let pk = PublicKey::from_secret_key(&secp, &sk); + let ser = pk.serialize_uncompressed(); // 0x04 || x(32) || y(32) + + let mut x = [0u8; 32]; + x.copy_from_slice(&ser[1..33]); + + let mut y = [0u8; 32]; + y.copy_from_slice(&ser[33..65]); + + (x, y) +} + +fn ge_to_gej(ge: ([u8; 32], [u8; 32])) -> (([u8; 32], [u8; 32]), [u8; 32]) { + let mut one = [0u8; 32]; + one[31] = 1; + + (ge, one) +} + +fn compress(ge: ([u8; 32], [u8; 32])) -> (u8, [u8; 32]) { + (ge.1[31] & 1, ge.0) +} + +fn pk_to_gej(pk: &PublicKey) -> (([u8; 32], [u8; 32]), [u8; 32]) { + let ser = pk.serialize_uncompressed(); + + let mut x = [0u8; 32]; + x.copy_from_slice(&ser[1..33]); + + let mut y = [0u8; 32]; + y.copy_from_slice(&ser[33..65]); + + ge_to_gej((x, y)) +} + +// 0. ge_to_point +#[simplex::test] +fn ge_to_point_matches_parity(context: simplex::TestContext) -> anyhow::Result<()> { + // Sample one on-curve point; whatever parity it has, that's what we expect + // ge_to_point to produce. Covers both parities across repeated runs. + let ge = random_ge_bytes(); + let expected_parity = ge.1[31] & 1; // 0 (even y) or 1 (odd y) + + case(GeToPoint) + .ge(ge) + .expect_point((expected_parity, ge.0)) + .run(&context) +} + +// 1. point_to_gej +#[simplex::test] +fn point_to_gej_roundtrip(context: simplex::TestContext) -> anyhow::Result<()> { + let ge = random_ge_bytes(); + let point = compress(ge); + + case(PointToGej) + .point(point) + .expect_point(point) + .run(&context) +} + +// 2. fe_sub +#[simplex::test] +fn fe_sub_self_is_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = random_fe_bytes(); + + case(FeSub).uints(a, a).expect_uint([0u8; 32]).run(&context) +} + +#[simplex::test] +fn fe_sub_matches_reference(context: simplex::TestContext) -> anyhow::Result<()> { + let a = random_fe_bytes(); + let b = random_fe_bytes(); + let exp = fe_sub_ref(a, b); + + case(FeSub).uints(a, b).expect_uint(exp).run(&context) +} + +// 3. scalar_sub +#[simplex::test] +fn scalar_sub_matches_reference(context: simplex::TestContext) -> anyhow::Result<()> { + let a = random_scalar_bytes(); + let b = random_scalar_bytes(); + let exp = scalar_sub_ref(a, b); + + case(ScalarSub).uints(a, b).expect_uint(exp).run(&context) +} + +// 4. gej_sub +#[simplex::test] +fn gej_sub_matches_reference(context: simplex::TestContext) -> anyhow::Result<()> { + let secp = Secp256k1::new(); + let sk_p = SecretKey::new(&mut SecpOsRng); + let sk_q = SecretKey::new(&mut SecpOsRng); + + let p = PublicKey::from_secret_key(&secp, &sk_p); + let q = PublicKey::from_secret_key(&secp, &sk_q); + let diff = p.combine(&q.negate(&secp)).expect("p - q non-infinity"); + + case(GejSub) + .gejs(pk_to_gej(&p), pk_to_gej(&q)) + .expect_gej(pk_to_gej(&diff)) + .run(&context) +} + +// 5. fe_eq +#[simplex::test] +fn fe_eq_reflexive(context: simplex::TestContext) -> anyhow::Result<()> { + let a = random_fe_bytes(); + + case(FeEq).uints(a, a).run(&context) +} + +#[simplex::test] +fn fe_eq_zero_and_p_are_equal(context: simplex::TestContext) -> anyhow::Result<()> { + case(FeEq).uints([0u8; 32], SECP_P).run(&context) +} + +// 6. scalar_eq +#[simplex::test] +fn scalar_eq_s_and_s_plus_n_are_equal(context: simplex::TestContext) -> anyhow::Result<()> { + // s = 5. s + n has no carry past the low byte. + let mut s = [0u8; 32]; + s[31] = 5; + + let mut s_plus_n = SECP_N; + s_plus_n[31] = s_plus_n[31].wrapping_add(5); + + case(ScalarEq).uints(s, s_plus_n).run(&context) +} + +// 7. ge_eq +#[simplex::test] +fn ge_eq_rejects_negation(context: simplex::TestContext) -> anyhow::Result<()> { + let ge = random_ge_bytes(); + let neg_y = fe_negate_ref(ge.1); + + case(GeEq) + .ges(ge, (ge.0, neg_y)) + .expecting(&context, Expect::AssertFailed) +} + +// 8. gej_point_eq +#[simplex::test] +fn gej_point_eq_rescaled(context: simplex::TestContext) -> anyhow::Result<()> { + // (λ²X, λ³Y, λZ) is the same affine point as (X, Y, 1). + let ge = random_ge_bytes(); + let lambda = random_fe_bytes(); + let l2 = fe_mul_ref(lambda, lambda); + let l3 = fe_mul_ref(l2, lambda); + let g = ((fe_mul_ref(ge.0, l2), fe_mul_ref(ge.1, l3)), lambda); + + case(GejPointEq).gej(g).point(compress(ge)).run(&context) +} + +#[simplex::test] +fn gej_point_eq_rejects_negation(context: simplex::TestContext) -> anyhow::Result<()> { + // Property under test: gej_point_eq(P, -P) == false. + // + // Construction: + // ge = random on-curve point P = (x, y) + // (parity, x) = compressed form of +P + // ge_to_gej(ge) = Gej encoding of +P + // (parity ^ 1, x) = compressed form of -P + // + // decompress((parity ^ 1, x)) inside gej_point_eq recovers (x, -y) = -P. + // Since P ≠ -P on secp256k1, the equivalence must return false. + // expected_bool = false locks that in. + let ge = random_ge_bytes(); + let (parity, x) = compress(ge); + + case(GejPointEq) + .gej(ge_to_gej(ge)) + .point((parity ^ 1, x)) + .expecting(&context, Expect::AssertFailed) +} + +// 9. safe_gej_normalize +#[simplex::test] +fn safe_gej_normalize_roundtrip(context: simplex::TestContext) -> anyhow::Result<()> { + let ge = random_ge_bytes(); + + case(SafeGejNormalize) + .gej(ge_to_gej(ge)) + .expect_ge(ge) + .run(&context) +} diff --git a/tests/stdlib/u1/convert.rs b/tests/stdlib/u1/convert.rs new file mode 100644 index 0000000..69e84e4 --- /dev/null +++ b/tests/stdlib/u1/convert.rs @@ -0,0 +1,134 @@ +use primitive_types::U256; +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::u1::convert::ConvertProgram as U1ConvertTestProgram; +use simplicityhl_std::artifacts::tests::u1::convert::derived_convert::{ + ConvertArguments as U1ConvertTestArguments, ConvertWitness as U1ConvertTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + U1ToU8, + U1ToU16, + U1ToU32, + U1ToU64, + U1ToU128, + U1ToU256, + U1ToBool, +} + +fn program() -> U1ConvertTestProgram { + U1ConvertTestProgram::new(&U1ConvertTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U1ConvertTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U1ConvertTestWitness { + function_index: function as u8, + first_arg: 0, + expected: [0; 32], + }, + } +} + +impl Case { + /// The operand, `first_arg`. + fn arg(mut self, a: u8) -> Self { + self.witness.first_arg = a; + self + } + + /// `expected`: the value the arm should produce. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = expected; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn u1_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(U1ToU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u1_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(U1ToU16) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u1_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(U1ToU32) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u1_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(U1ToU64) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u1_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(U1ToU128) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u1_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(U1ToU256) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u1_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(U1ToBool) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} diff --git a/tests/stdlib/u1/mod.rs b/tests/stdlib/u1/mod.rs new file mode 100644 index 0000000..bb13da4 --- /dev/null +++ b/tests/stdlib/u1/mod.rs @@ -0,0 +1 @@ +mod convert; diff --git a/tests/stdlib/u128/bit.rs b/tests/stdlib/u128/bit.rs new file mode 100644 index 0000000..bba6b0b --- /dev/null +++ b/tests/stdlib/u128/bit.rs @@ -0,0 +1,168 @@ +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::u128::bit::BitProgram as U128TestBitsProgram; +use simplicityhl_std::artifacts::tests::u128::bit::derived_bit::{ + BitArguments as U128TestBitsArguments, BitWitness as U128TestBitsWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + And128, + Or128, + Eq128, + LeftShift128, + RightShift128, +} + +fn program() -> U128TestBitsProgram { + U128TestBitsProgram::new(&U128TestBitsArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U128TestBitsWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U128TestBitsWitness { + function_index: function as u8, + first_arg: 0, + second_arg: 0, + expected: None, + expected_bool: false, + }, + } +} + +impl Case { + /// The two operands, `first_arg` and `second_arg`. + fn args(mut self, a: u128, b: u128) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: u128) -> Self { + self.witness.expected = Some(expected); + self + } + + /// `expected_bool`: the boolean the arm should report. + fn flag(mut self, expected_bool: bool) -> Self { + self.witness.expected_bool = expected_bool; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn and_128(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); + let result = a & b; + + case(And128).args(a, b).expect(result).run(&context) +} + +#[simplex::test] +fn or_128(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); + let result = a | b; + + case(Or128).args(a, b).expect(result).run(&context) +} + +#[simplex::test] +fn eq_128_true(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + case(Eq128).args(a, a).expect(0).flag(true).run(&context) +} + +#[simplex::test] +fn eq_128_false(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + let b = a - 1; + + case(Eq128).args(a, b).expect(0).run(&context) +} + +#[simplex::test] +fn left_shift_128(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = rand::thread_rng().gen_range(1..=127_u128); + let val = rand::thread_rng().gen_range(0..=u128::MAX); + let result = val << shift; + + case(LeftShift128) + .args(shift, val) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn left_shift_128_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = 0; + let val = rand::thread_rng().gen_range(0..=u128::MAX); + let result = val; + + case(LeftShift128) + .args(shift, val) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn left_shift_128_out_of_range(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = rand::thread_rng().gen_range(128..=u8::MAX as u128); + let val = rand::thread_rng().gen_range(0..=u128::MAX); + + case(LeftShift128).args(shift, val).expect(0).run(&context) +} + +#[simplex::test] +fn right_shift_128(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = rand::thread_rng().gen_range(1..=127_u128); + let val = rand::thread_rng().gen_range(0..=u128::MAX); + let result = val >> shift; + + case(RightShift128) + .args(shift, val) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn right_shift_128_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = 0; + let val = rand::thread_rng().gen_range(0..=u128::MAX); + let result = val; + + case(RightShift128) + .args(shift, val) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn right_shift_128_out_of_range(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = rand::thread_rng().gen_range(128..=u8::MAX as u128); + let val = rand::thread_rng().gen_range(0..=u128::MAX); + + case(RightShift128).args(shift, val).expect(0).run(&context) +} diff --git a/tests/stdlib/u128/comparison.rs b/tests/stdlib/u128/comparison.rs new file mode 100644 index 0000000..b97c596 --- /dev/null +++ b/tests/stdlib/u128/comparison.rs @@ -0,0 +1,129 @@ +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::u128::comparison::ComparisonProgram as U128TestCompareProgram; +use simplicityhl_std::artifacts::tests::u128::comparison::derived_comparison::{ + ComparisonArguments as U128TestCompareArguments, ComparisonWitness as U128TestCompareWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + IsZero128, + Lt128, + Le128, +} + +fn program() -> U128TestCompareProgram { + U128TestCompareProgram::new(&U128TestCompareArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U128TestCompareWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U128TestCompareWitness { + function_index: function as u8, + first_arg: 0, + second_arg: 0, + expected_bool: false, + }, + } +} + +impl Case { + /// Only `first_arg`, for the arms that ignore the second operand. + fn arg(mut self, a: u128) -> Self { + self.witness.first_arg = a; + self + } + + /// The two operands, `first_arg` and `second_arg`. + fn args(mut self, a: u128, b: u128) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self + } + + /// `expected_bool`: the boolean the arm should report. + fn flag(mut self, expected_bool: bool) -> Self { + self.witness.expected_bool = expected_bool; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn is_zero_128_true(context: simplex::TestContext) -> anyhow::Result<()> { + let a = 0; + + case(IsZero128).arg(a).flag(true).run(&context) +} + +#[simplex::test] +fn is_zero_128_false(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + + case(IsZero128).arg(a).run(&context) +} + +#[simplex::test] +fn lt_128_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a + 1; + + case(Lt128).args(a, b).flag(true).run(&context) +} + +#[simplex::test] +fn lt_128_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a; + + case(Lt128).args(a, b).run(&context) +} + +#[simplex::test] +fn lt_128_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + let b = a - 1; + + case(Lt128).args(a, b).run(&context) +} + +#[simplex::test] +fn le_128_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a + 1; + + case(Le128).args(a, b).flag(true).run(&context) +} + +#[simplex::test] +fn le_128_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a; + + case(Le128).args(a, b).flag(true).run(&context) +} + +#[simplex::test] +fn le_128_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + let b = a - 1; + + case(Le128).args(a, b).run(&context) +} diff --git a/tests/stdlib/u128/convert.rs b/tests/stdlib/u128/convert.rs new file mode 100644 index 0000000..b1cf059 --- /dev/null +++ b/tests/stdlib/u128/convert.rs @@ -0,0 +1,217 @@ +use primitive_types::U256; +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::u128::convert::ConvertProgram as U128ConvertTestProgram; +use simplicityhl_std::artifacts::tests::u128::convert::derived_convert::{ + ConvertArguments as U128ConvertTestArguments, ConvertWitness as U128ConvertTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + U128ToU256, + SplitU128IntoU8, + SplitU128IntoU16, + SplitU128IntoU32, + SplitU128IntoU64, + SafeU128ToU1, + SafeU128ToU8, + SafeU128ToU16, + SafeU128ToU32, + SafeU128ToU64, +} + +fn program() -> U128ConvertTestProgram { + U128ConvertTestProgram::new(&U128ConvertTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U128ConvertTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U128ConvertTestWitness { + function_index: function as u8, + first_arg: 0, + expected: [0; 32], + }, + } +} + +impl Case { + /// The operand, `first_arg`. + fn arg(mut self, a: u128) -> Self { + self.witness.first_arg = a; + self + } + + /// `expected`: the value the arm should produce. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = expected; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn u128_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + case(U128ToU256) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u128_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + case(SplitU128IntoU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u128_into_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + case(SplitU128IntoU16) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u128_into_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + case(SplitU128IntoU32) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u128_into_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + case(SplitU128IntoU64) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u128_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(SafeU128ToU1) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u128_to_u1_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u128::MAX); + + case(SafeU128ToU1) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u128_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX as u128); + + case(SafeU128ToU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u128_to_u8_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u8::MAX as u128 + 1..=u128::MAX); + + case(SafeU128ToU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u128_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX as u128); + + case(SafeU128ToU16) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u128_to_u16_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u16::MAX as u128 + 1..=u128::MAX); + + case(SafeU128ToU16) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u128_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX as u128); + + case(SafeU128ToU32) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u128_to_u32_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u32::MAX as u128 + 1..=u128::MAX); + + case(SafeU128ToU32) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u128_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX as u128); + + case(SafeU128ToU64) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u128_to_u64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u64::MAX as u128 + 1..=u128::MAX); + + case(SafeU128ToU64) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} diff --git a/tests/u128_math_test.rs b/tests/stdlib/u128/math/api.rs similarity index 64% rename from tests/u128_math_test.rs rename to tests/stdlib/u128/math/api.rs index 06e2d98..c624b3f 100644 --- a/tests/u128_math_test.rs +++ b/tests/stdlib/u128/math/api.rs @@ -1,10 +1,8 @@ -mod common; +use crate::common::uint::TestUint; -use common::uint::TestUint; - -use simplicityhl_std::artifacts::u128_math_test::U128MathTestProgram; -use simplicityhl_std::artifacts::u128_math_test::derived_u128_math_test::{ - U128MathTestArguments, U128MathTestWitness, +use simplicityhl_std::artifacts::tests::u128::math::api::ApiProgram as U128MathTestProgram; +use simplicityhl_std::artifacts::tests::u128::math::api::derived_api::{ + ApiArguments as U128MathTestArguments, ApiWitness as U128MathTestWitness, }; // The only per-width code for the common operations. @@ -32,9 +30,5 @@ impl TestUint for u128 { } } -mod u128_math_tests { - use super::*; - - // Stamps the 22 `#[simplex::test]` entry points for u128. Logic lives in common::uint. - uint_tests!(u128); -} +// Stamps the 22 `#[simplex::test]` entry points for u128. Logic lives in common::uint. +crate::uint_tests!(u128); diff --git a/tests/stdlib/u128/math/mod.rs b/tests/stdlib/u128/math/mod.rs new file mode 100644 index 0000000..914dfcb --- /dev/null +++ b/tests/stdlib/u128/math/mod.rs @@ -0,0 +1,2 @@ +mod api; +mod primitives; diff --git a/tests/stdlib/u128/math/primitives.rs b/tests/stdlib/u128/math/primitives.rs new file mode 100644 index 0000000..367e41f --- /dev/null +++ b/tests/stdlib/u128/math/primitives.rs @@ -0,0 +1,645 @@ +use primitive_types::U256; +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::u128::math::primitives::PrimitivesProgram as U128BasicMathTestProgram; +use simplicityhl_std::artifacts::tests::u128::math::primitives::derived_primitives::{ + PrimitivesArguments as U128BasicMathTestArguments, + PrimitivesWitness as U128BasicMathTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + Add128, + Add128_64, + FullAdd128, + Sub128, + FullSub128, + Mul128, + Mul128_64, + CalculateNormalizerBase64, + EstimateQuotientDigitBase64, + DivMod128_64, + DivMod128, + Div128, +} + +fn program() -> U128BasicMathTestProgram { + U128BasicMathTestProgram::new(&U128BasicMathTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U128BasicMathTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U128BasicMathTestWitness { + function_index: function as u8, + first_arg: 0, + second_arg: 0, + expected: None, + expected_bool: false, + second_expected: 0, + }, + } +} + +impl Case { + /// The two operands, `first_arg` and `second_arg`. + fn args(mut self, a: u128, b: u128) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: u128) -> Self { + self.witness.expected = Some(expected); + self + } + + /// `expected_bool`: the carry or borrow an arm reports beside its value. + fn flag(mut self, flag: bool) -> Self { + self.witness.expected_bool = flag; + self + } + + /// `second_expected`: the spare slot. A remainder, a low word, or a carry + /// fed back in, depending on the arm. + fn second(mut self, second: u128) -> Self { + self.witness.second_expected = second; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +fn split_helper(a: U256) -> (u128, u128) { + let a_high = (a >> 128).as_u128(); + let a_low = a.low_u128(); + + (a_high, a_low) +} + +#[simplex::test] +fn add_128_not_overflow(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; + + case(Add128).args(a, b).expect(result).run(&context) +} + +#[simplex::test] +fn add_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = u128::MAX; + let b = rand::thread_rng().gen_range(1..=u128::MAX); + let result = b - 1; + + case(Add128) + .args(a, b) + .expect(result) + .flag(true) + .run(&context) +} + +#[simplex::test] +fn add_128_64_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX / 2); + let b = rand::thread_rng().gen_range(0..=u64::MAX) as u128; + let result = a + b; + + case(Add128_64).args(a, b).expect(result).run(&context) +} + +#[simplex::test] +fn add_128_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = u128::MAX; + let b = rand::thread_rng().gen_range(1..=u64::MAX) as u128; + let result = b - 1; + + case(Add128_64) + .args(a, b) + .expect(result) + .flag(true) + .run(&context) +} + +#[simplex::test] +fn 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; + + case(FullAdd128) + .args(a, b) + .expect(result) + .flag(result_carry) + .second(carry_low) + .run(&context) +} + +#[simplex::test] +fn 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; + + case(FullAdd128) + .args(a, b) + .expect(result) + .flag(result_carry) + .second(carry_low) + .run(&context) +} + +#[simplex::test] +fn 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; + + case(FullAdd128) + .args(a, b) + .expect(result) + .flag(result_carry) + .second(carry_low) + .run(&context) +} + +#[simplex::test] +fn 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; + + case(FullAdd128) + .args(a, b) + .expect(result) + .flag(result_carry) + .second(carry_low) + .run(&context) +} + +#[simplex::test] +fn sub_128_not_overflow(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; + + case(Sub128).args(a, b).expect(result).run(&context) +} + +#[simplex::test] +fn sub_128_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + case(Sub128).args(a, a).expect(0).run(&context) +} + +#[simplex::test] +fn sub_128_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + let b_high = rand::thread_rng().gen_range(0..=u64::MAX); + + let low: u64 = a as u64; + let b = ((b_high as u128) << 64) | (low as u128); + + let carry = a < b; + let result = a.wrapping_sub(b); + + case(Sub128) + .args(a, b) + .expect(result) + .flag(carry) + .run(&context) +} + +#[simplex::test] +fn sub_128_diff_is_u64_max(context: simplex::TestContext) -> anyhow::Result<()> { + let a_low: u64 = u64::MAX; + + let a_high = rand::thread_rng().gen_range(0..=u64::MAX); + let b_high = rand::thread_rng().gen_range(0..=u64::MAX); + + let a = ((a_high as u128) << 64) | (a_low as u128); + let b = (b_high as u128) << 64; // b_low is 0 + + let carry = a < b; + let result = a.wrapping_sub(b); + + case(Sub128) + .args(a, b) + .expect(result) + .flag(carry) + .run(&context) +} + +#[simplex::test] +fn sub_128_diff_is_u128_max(context: simplex::TestContext) -> anyhow::Result<()> { + let a = u128::MAX; + let b = 0; + + case(Sub128).args(a, b).expect(a).run(&context) +} + +#[simplex::test] +fn sub_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = u128::MAX; + let result = a + 1; + + case(Sub128) + .args(a, b) + .expect(result) + .flag(true) + .run(&context) +} + +#[simplex::test] +fn 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; + + case(FullSub128) + .args(a, b) + .expect(result) + .flag(result_borrow) + .second(borrow_low) + .run(&context) +} + +#[simplex::test] +fn 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; + + case(FullSub128) + .args(a, b) + .expect(result) + .flag(result_borrow) + .second(borrow_low) + .run(&context) +} + +#[simplex::test] +fn 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; + + case(FullSub128) + .args(a, b) + .expect(result) + .flag(result_borrow) + .second(borrow_low) + .run(&context) +} + +#[simplex::test] +fn 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; + + case(FullSub128) + .args(a, b) + .expect(result - 1) + .flag(result_borrow) + .second(borrow_low) + .run(&context) +} + +#[simplex::test] +fn mul_128(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); + let result = U256::from(a) * U256::from(b); + + let (result_high, result_low) = split_helper(result); + + case(Mul128) + .args(a, b) + .expect(result_high) + .second(result_low) + .run(&context) +} + +#[simplex::test] +fn 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); + + case(Mul128_64) + .args(a, b as u128) + .expect(result_high) + .second(result_low) + .run(&context) +} + +#[simplex::test] +fn calculate_normalizer_base_64_b_is_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let threshold = 1u128 << 63; + + let b = rand::thread_rng().gen_range(1..threshold); + + let norm: u128 = threshold.div_ceil(b); + + case(CalculateNormalizerBase64) + .args(0, b) + .expect(norm) + .run(&context) +} + +#[simplex::test] +fn calculate_normalizer_base_64_b_is_big_enough_not_normalize( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let threshold = 1u128 << 63; + + let b = rand::thread_rng().gen_range(threshold..=u64::MAX as u128); + + case(CalculateNormalizerBase64) + .args(0, b) + .expect(1) + .run(&context) +} + +#[simplex::test] +fn calculate_normalizer_base_64_b_is_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let threshold = 1u128 << 63; + + 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); + + case(CalculateNormalizerBase64) + .args(0, b) + .expect(norm) + .flag(true) + .run(&context) +} + +#[simplex::test] +fn calculate_normalizer_base_64_b_is_u64_fail(context: simplex::TestContext) -> anyhow::Result<()> { + let b = rand::thread_rng().gen_range((u64::MAX as u128) + 1..=u128::MAX); + + case(CalculateNormalizerBase64) + .args(0, b) + .expect(0) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn calculate_normalizer_base_64_b_is_u128_fail( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let b = rand::thread_rng().gen_range(1..=u64::MAX as u128); + + case(CalculateNormalizerBase64) + .args(0, b) + .expect(0) + .flag(true) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn calculate_normalizer_base_64_b_is_zero_fail( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let b = 0; + + case(CalculateNormalizerBase64) + .args(0, b) + .expect(0) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn estimate_quotient_digit_base_64(context: simplex::TestContext) -> anyhow::Result<()> { + let threshold = 1u64 << 63; + + 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(); + + case(EstimateQuotientDigitBase64) + .args(a_high as u128, a_low) + .expect(q) + .second(b) + .run(&context) +} + +#[simplex::test] +fn 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 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(); + + case(EstimateQuotientDigitBase64) + .args(a_high as u128, a_low) + .expect(q) + .second(b) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn div_mod_128_64(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); + + let q = a / b; + let r = a % b; + + case(DivMod128_64) + .args(a, b) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn div_mod_128_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + let b = 0; + + case(DivMod128_64) + .args(a, b) + .expect(0) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn div_mod_128_a_less_than_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = rand::thread_rng().gen_range(a + 1..=u128::MAX); + + let q = a / b; + let r = a % b; + + case(DivMod128).args(a, b).expect(q).second(r).run(&context) +} + +#[simplex::test] +fn div_mod_128_div_64(context: simplex::TestContext) -> anyhow::Result<()> { + let b = rand::thread_rng().gen_range(1..=u64::MAX); + let a = rand::thread_rng().gen_range(b..=u64::MAX) as u128; + + let q = a / b as u128; + let r = a % b as u128; + + case(DivMod128) + .args(a, b as u128) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn div_mod_128_q_is_1(context: simplex::TestContext) -> anyhow::Result<()> { + // case where a >= b and a_high = b_high != 0 + let b_low = rand::thread_rng().gen_range(0..=u64::MAX); + let a_low = rand::thread_rng().gen_range(b_low..=u64::MAX); + let high = rand::thread_rng().gen_range(1..u64::MAX); + + let a = ((high as u128) << 64) | (a_low as u128); + let b = ((high as u128) << 64) | (b_low as u128); + + let q = a / b; + let r = a % b; + + case(DivMod128).args(a, b).expect(q).second(r).run(&context) +} + +#[simplex::test] +fn div_mod_128_b_is_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let b = rand::thread_rng().gen_range(1..=u64::MAX as u128); + let a = rand::thread_rng().gen_range(u64::MAX as u128 + 1..=u128::MAX); + + let q = a / b; + let r = a % b; + + case(DivMod128).args(a, b).expect(q).second(r).run(&context) +} + +#[simplex::test] +fn div_mod_128_b_is_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let b_high = rand::thread_rng().gen_range(1..u64::MAX); + let a_high = rand::thread_rng().gen_range(b_high + 1..=u64::MAX); + + let a = ((a_high as u128) << 64) | (rand::thread_rng().gen_range(0..u64::MAX) as u128); + let b = ((b_high as u128) << 64) | (rand::thread_rng().gen_range(0..u64::MAX) as u128); + + let q = a / b; + let r = a % b; + + case(DivMod128).args(a, b).expect(q).second(r).run(&context) +} + +#[simplex::test] +fn div_mod_128_a_equal_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + + case(DivMod128) + .args(a, a) + .expect(1u128) + .second(0u128) + .run(&context) +} + +#[simplex::test] +fn div_mod_128_equal_high_words_max_low_diff(context: simplex::TestContext) -> anyhow::Result<()> { + let high = rand::thread_rng().gen_range(1..=u64::MAX); + + let a = ((high as u128) << 64) | (u64::MAX as u128); + let b = (high as u128) << 64; + + case(DivMod128) + .args(a, b) + .expect(1u128) + .second(u64::MAX as u128) + .run(&context) +} + +#[simplex::test] +fn div_mod_128_eq_high_words_a_less_than_b(context: simplex::TestContext) -> anyhow::Result<()> { + let high = rand::thread_rng().gen_range(1..=u64::MAX); + + let a = (high as u128) << 64; + let b = ((high as u128) << 64) | (u64::MAX as u128); + + case(DivMod128) + .args(a, b) + .expect(0u128) + .second(a) + .run(&context) +} + +#[simplex::test] +fn div_128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + let b = rand::thread_rng().gen_range(1..=u128::MAX); + let result = a / b; + + case(Div128).args(a, b).expect(result).run(&context) +} + +#[simplex::test] +fn div_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + let b = 0; + + case(Div128).args(a, b).expect(0).run(&context) +} diff --git a/tests/stdlib/u128/mod.rs b/tests/stdlib/u128/mod.rs new file mode 100644 index 0000000..5af8775 --- /dev/null +++ b/tests/stdlib/u128/mod.rs @@ -0,0 +1,5 @@ +mod bit; +mod comparison; +mod convert; +mod math; +mod mul_div; diff --git a/tests/stdlib/u128/mul_div.rs b/tests/stdlib/u128/mul_div.rs new file mode 100644 index 0000000..90f4315 --- /dev/null +++ b/tests/stdlib/u128/mul_div.rs @@ -0,0 +1,109 @@ +use primitive_types::U256; +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::u128::mul_div::MulDivProgram as U128MulDivTestProgram; +use simplicityhl_std::artifacts::tests::u128::mul_div::derived_mul_div::{ + MulDivArguments as U128MulDivTestArguments, MulDivWitness as U128MulDivTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + MulDiv, +} + +fn program() -> U128MulDivTestProgram { + U128MulDivTestProgram::new(&U128MulDivTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U128MulDivTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U128MulDivTestWitness { + function_index: function as u8, + first_arg: 0, + second_arg: 0, + third_arg: 0, + expected: None, + }, + } +} + +impl Case { + /// The three operands, `first_arg`, `second_arg` and `third_arg`. + fn args(mut self, a: u128, b: u128, c: u128) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self.witness.third_arg = c; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: u128) -> Self { + self.witness.expected = Some(expected); + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn mul_div_128_product_is_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX) as u128; + let b = rand::thread_rng().gen_range(0..=u64::MAX) as u128; + let c = rand::thread_rng().gen_range(1..=u128::MAX); + + let res = a * b / c; + + case(MulDiv).args(a, b, c).expect(res).run(&context) +} + +#[simplex::test] +fn mul_div_128_intermediate_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u128::MAX); + let b = u128::MAX; + let c = rand::thread_rng().gen_range(a..=u128::MAX); + + let res = U256::from(a) * U256::from(b) / U256::from(c); + + case(MulDiv) + .args(a, b, c) + .expect(res.low_u128()) + .run(&context) +} + +#[simplex::test] +fn mul_div_128_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u128::MAX); + let b = u128::MAX; + let c = rand::thread_rng().gen_range(1..a); + + case(MulDiv) + .args(a, b, c) + .expect(0) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn mul_div_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + let b = rand::thread_rng().gen_range(1..=u128::MAX); + let c = 0; + + case(MulDiv).args(a, b, c).expect(0).run(&context) +} diff --git a/tests/stdlib/u16/convert.rs b/tests/stdlib/u16/convert.rs new file mode 100644 index 0000000..036a8a4 --- /dev/null +++ b/tests/stdlib/u16/convert.rs @@ -0,0 +1,154 @@ +use primitive_types::U256; +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::u16::convert::ConvertProgram as U16ConvertTestProgram; +use simplicityhl_std::artifacts::tests::u16::convert::derived_convert::{ + ConvertArguments as U16ConvertTestArguments, ConvertWitness as U16ConvertTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + U16ToU32, + U16ToU64, + U16ToU128, + U16ToU256, + SplitU16IntoU8, + SafeU16ToU1, + SafeU16ToU8, +} + +fn program() -> U16ConvertTestProgram { + U16ConvertTestProgram::new(&U16ConvertTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U16ConvertTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U16ConvertTestWitness { + function_index: function as u8, + first_arg: 0, + expected: [0; 32], + }, + } +} + +impl Case { + /// The operand, `first_arg`. + fn arg(mut self, a: u16) -> Self { + self.witness.first_arg = a; + self + } + + /// `expected`: the value the arm should produce. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = expected; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn u16_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX); + + case(U16ToU32) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u16_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX); + + case(U16ToU64) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u16_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX); + + case(U16ToU128) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u16_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX); + + case(U16ToU256) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u16_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX); + + case(SplitU16IntoU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u16_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(SafeU16ToU1) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u16_to_u1_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u16::MAX); + + case(SafeU16ToU1) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u16_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX as u16); + + case(SafeU16ToU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u16_to_u8_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u8::MAX as u16 + 1..=u16::MAX); + + case(SafeU16ToU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} diff --git a/tests/u16_math_test.rs b/tests/stdlib/u16/math.rs similarity index 63% rename from tests/u16_math_test.rs rename to tests/stdlib/u16/math.rs index 07b4c00..a343cfb 100644 --- a/tests/u16_math_test.rs +++ b/tests/stdlib/u16/math.rs @@ -1,10 +1,8 @@ -mod common; +use crate::common::uint::TestUint; -use common::uint::TestUint; - -use simplicityhl_std::artifacts::u16_math_test::U16MathTestProgram; -use simplicityhl_std::artifacts::u16_math_test::derived_u16_math_test::{ - U16MathTestArguments, U16MathTestWitness, +use simplicityhl_std::artifacts::tests::u16::math::MathProgram as U16MathTestProgram; +use simplicityhl_std::artifacts::tests::u16::math::derived_math::{ + MathArguments as U16MathTestArguments, MathWitness as U16MathTestWitness, }; // The only per-width code for the common operations. @@ -32,9 +30,5 @@ impl TestUint for u16 { } } -mod u16_math_tests { - use super::*; - - // Stamps the 22 `#[simplex::test]` entry points for u16. Logic lives in common::uint. - uint_tests!(u16); -} +// Stamps the 22 `#[simplex::test]` entry points for u16. Logic lives in common::uint. +crate::uint_tests!(u16); diff --git a/tests/stdlib/u16/mod.rs b/tests/stdlib/u16/mod.rs new file mode 100644 index 0000000..828ff4c --- /dev/null +++ b/tests/stdlib/u16/mod.rs @@ -0,0 +1,3 @@ +mod convert; +mod math; +mod mul_div; diff --git a/tests/stdlib/u16/mul_div.rs b/tests/stdlib/u16/mul_div.rs new file mode 100644 index 0000000..7fd260a --- /dev/null +++ b/tests/stdlib/u16/mul_div.rs @@ -0,0 +1,104 @@ +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::tests::u16::mul_div::MulDivProgram as U16MulDivTestProgram; +use simplicityhl_std::artifacts::tests::u16::mul_div::derived_mul_div::{ + MulDivArguments as U16MulDivTestArguments, MulDivWitness as U16MulDivTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + MulDiv, +} + +fn program() -> U16MulDivTestProgram { + U16MulDivTestProgram::new(&U16MulDivTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U16MulDivTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U16MulDivTestWitness { + function_index: function as u8, + first_arg: 0, + second_arg: 0, + third_arg: 0, + expected: None, + }, + } +} + +impl Case { + /// The three operands, `first_arg`, `second_arg` and `third_arg`. + fn args(mut self, a: u16, b: u16, c: u16) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self.witness.third_arg = c; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: u16) -> Self { + self.witness.expected = Some(expected); + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn mul_div_16_product_is_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX) as u16; + let b = rand::thread_rng().gen_range(0..=u8::MAX) as u16; + let c = rand::thread_rng().gen_range(1..=u16::MAX); + + let res = a * b / c; + + case(MulDiv).args(a, b, c).expect(res).run(&context) +} + +#[simplex::test] +fn mul_div_16_intermediate_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u16::MAX); + let b = u16::MAX; + let c = rand::thread_rng().gen_range(a..=u16::MAX); + + let res = (a as u32) * (b as u32) / (c as u32); + + case(MulDiv).args(a, b, c).expect(res as u16).run(&context) +} + +#[simplex::test] +fn mul_div_16_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u16::MAX); + let b = u16::MAX; + let c = rand::thread_rng().gen_range(1..a); + + case(MulDiv) + .args(a, b, c) + .expect(0) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn mul_div_16_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u16::MAX); + let b = rand::thread_rng().gen_range(1..=u16::MAX); + let c = 0; + + case(MulDiv).args(a, b, c).expect(0).run(&context) +} diff --git a/tests/stdlib/u256/bit.rs b/tests/stdlib/u256/bit.rs new file mode 100644 index 0000000..0d0db36 --- /dev/null +++ b/tests/stdlib/u256/bit.rs @@ -0,0 +1,161 @@ +use primitive_types::U256; +use rand::Rng; + +use crate::common::core::{Expect, run}; +use crate::common::helper::generate_u256; + +use simplicityhl_std::artifacts::tests::u256::bit::BitProgram as U256TestBitsProgram; +use simplicityhl_std::artifacts::tests::u256::bit::derived_bit::{ + BitArguments as U256TestBitsArguments, BitWitness as U256TestBitsWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + And256, + Or256, + LeftShift256, + RightShift256, +} + +fn program() -> U256TestBitsProgram { + U256TestBitsProgram::new(&U256TestBitsArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U256TestBitsWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U256TestBitsWitness { + function_index: function as u8, + first_arg: [0; 32], + second_arg: [0; 32], + expected: None, + }, + } +} + +impl Case { + /// The two operands, `first_arg` and `second_arg`. + fn args(mut self, a: [u8; 32], b: [u8; 32]) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = Some(expected); + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn 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(); + + case(And256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(Or256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(LeftShift256) + .args(U256::from(shift).to_big_endian(), val.to_big_endian()) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn 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; + + case(LeftShift256) + .args(U256::from(shift).to_big_endian(), val) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(LeftShift256) + .args(U256::from(shift).to_big_endian(), val.to_big_endian()) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(RightShift256) + .args(U256::from(shift).to_big_endian(), val.to_big_endian()) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn 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; + + case(RightShift256) + .args(U256::from(shift).to_big_endian(), val) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(RightShift256) + .args(U256::from(shift).to_big_endian(), val.to_big_endian()) + .expect(result) + .run(&context) +} diff --git a/tests/stdlib/u256/comparison.rs b/tests/stdlib/u256/comparison.rs new file mode 100644 index 0000000..27bdbb5 --- /dev/null +++ b/tests/stdlib/u256/comparison.rs @@ -0,0 +1,138 @@ +use primitive_types::U256; + +use crate::common::core::{Expect, run}; +use crate::common::helper::generate_u256; + +use simplicityhl_std::artifacts::tests::u256::comparison::ComparisonProgram as U256TestCompareProgram; +use simplicityhl_std::artifacts::tests::u256::comparison::derived_comparison::{ + ComparisonArguments as U256TestCompareArguments, ComparisonWitness as U256TestCompareWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + IsZero256, + Lt256, + Le256, +} + +fn program() -> U256TestCompareProgram { + U256TestCompareProgram::new(&U256TestCompareArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U256TestCompareWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U256TestCompareWitness { + function_index: function as u8, + first_arg: [0; 32], + second_arg: [0; 32], + expected_bool: false, + }, + } +} + +impl Case { + /// Only `first_arg`, for the arms that ignore the second operand. + fn arg(mut self, a: [u8; 32]) -> Self { + self.witness.first_arg = a; + self + } + + /// The two operands, `first_arg` and `second_arg`. + fn args(mut self, a: [u8; 32], b: [u8; 32]) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self + } + + /// `expected_bool`: the boolean the arm should report. + fn flag(mut self, expected_bool: bool) -> Self { + self.witness.expected_bool = expected_bool; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn is_zero_256_true(context: simplex::TestContext) -> anyhow::Result<()> { + let a = [0; 32]; + + case(IsZero256).arg(a).flag(true).run(&context) +} + +#[simplex::test] +fn is_zero_256_false(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + case(IsZero256).arg(a).run(&context) +} + +#[simplex::test] +fn lt_256_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX - 1); + let b = a + 1; + + case(Lt256) + .args(a.to_big_endian(), b.to_big_endian()) + .flag(true) + .run(&context) +} + +#[simplex::test] +fn lt_256_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + + case(Lt256).args(a, a).run(&context) +} + +#[simplex::test] +fn lt_256_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = a - 1; + + case(Lt256) + .args(a.to_big_endian(), b.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn le_256_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX - 1); + let b = a + 1; + + case(Le256) + .args(a.to_big_endian(), b.to_big_endian()) + .flag(true) + .run(&context) +} + +#[simplex::test] +fn le_256_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + case(Le256).args(a, a).flag(true).run(&context) +} + +#[simplex::test] +fn le_256_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = a - 1; + + case(Le256) + .args(a.to_big_endian(), b.to_big_endian()) + .run(&context) +} diff --git a/tests/stdlib/u256/convert.rs b/tests/stdlib/u256/convert.rs new file mode 100644 index 0000000..2bd68cb --- /dev/null +++ b/tests/stdlib/u256/convert.rs @@ -0,0 +1,239 @@ +use primitive_types::U256; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::u256::convert::ConvertProgram as U256ConvertTestProgram; +use simplicityhl_std::artifacts::tests::u256::convert::derived_convert::{ + ConvertArguments as U256ConvertTestArguments, ConvertWitness as U256ConvertTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + SplitU256IntoU8, + SplitU256IntoU16, + SplitU256IntoU32, + SplitU256IntoU64, + SplitU256IntoU128, + SafeU256ToU1, + SafeU256ToU8, + SafeU256ToU16, + SafeU256ToU32, + SafeU256ToU64, + SafeU256ToU128, +} + +fn program() -> U256ConvertTestProgram { + U256ConvertTestProgram::new(&U256ConvertTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U256ConvertTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U256ConvertTestWitness { + function_index: function as u8, + first_arg: [0; 32], + expected: [0; 32], + }, + } +} + +impl Case { + /// The operand, `first_arg`. + fn arg(mut self, a: [u8; 32]) -> Self { + self.witness.first_arg = a; + self + } + + /// `expected`: the value the arm should produce. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = expected; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +use crate::common::helper::generate_u256; + +#[simplex::test] +fn u256_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + case(SplitU256IntoU8) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u256_into_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + case(SplitU256IntoU16) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u256_into_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + case(SplitU256IntoU32) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u256_into_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + case(SplitU256IntoU64) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u256_into_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + case(SplitU256IntoU128) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u256_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::one()); + + case(SafeU256ToU1) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u256_to_u1_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(2), U256::MAX); + + case(SafeU256ToU1) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u256_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u8::MAX)); + + case(SafeU256ToU8) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u256_to_u8_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u8::MAX) + 1, U256::MAX); + + case(SafeU256ToU8) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u256_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u16::MAX)); + + case(SafeU256ToU16) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u256_to_u16_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u16::MAX) + 1, U256::MAX); + + case(SafeU256ToU16) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u256_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u32::MAX)); + + case(SafeU256ToU32) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u256_to_u32_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u32::MAX) + 1, U256::MAX); + + case(SafeU256ToU32) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u256_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u64::MAX)); + + case(SafeU256ToU64) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u256_to_u64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u64::MAX) + 1, U256::MAX); + + case(SafeU256ToU64) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u256_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u128::MAX)); + + case(SafeU256ToU128) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u256_to_u128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + + case(SafeU256ToU128) + .arg(a.to_big_endian()) + .expect(a.to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} diff --git a/tests/stdlib/u256/math/add.rs b/tests/stdlib/u256/math/add.rs new file mode 100644 index 0000000..bbd813c --- /dev/null +++ b/tests/stdlib/u256/math/add.rs @@ -0,0 +1,195 @@ +use primitive_types::U256; + +use crate::common::core::{Expect, run}; +use crate::common::helper::generate_u256; + +use simplicityhl_std::artifacts::tests::u256::math::add::AddProgram as U256TestAddProgram; +use simplicityhl_std::artifacts::tests::u256::math::add::derived_add::{ + AddArguments as U256TestAddArguments, AddWitness as U256TestAddWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + Add256, + Add256_128, + FullAdd256, +} + +fn program() -> U256TestAddProgram { + U256TestAddProgram::new(&U256TestAddArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U256TestAddWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U256TestAddWitness { + function_index: function as u8, + first_arg: [0; 32], + second_arg: [0; 32], + third_arg: false, + expected: None, + expected_bool: false, + }, + } +} + +impl Case { + /// The two operands, `first_arg` and `second_arg`. + fn args(mut self, a: [u8; 32], b: [u8; 32]) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self + } + + /// `third_arg`: the extra operand only some arms take. + fn third_arg(mut self, c: bool) -> Self { + self.witness.third_arg = c; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = Some(expected); + self + } + + /// `expected_bool`: the boolean the arm should report. + fn flag(mut self, expected_bool: bool) -> Self { + self.witness.expected_bool = expected_bool; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn 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(); + + case(Add256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(Add256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result) + .flag(true) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(Add256_128) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(Add256_128) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result) + .flag(true) + .run(&context) +} + +#[simplex::test] +fn full_add_256_not_overflow_carry_low_false(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(); + let result_carry = false; + let carry_low = false; + + case(FullAdd256) + .args(a.to_big_endian(), b.to_big_endian()) + .third_arg(carry_low) + .expect(result) + .flag(result_carry) + .run(&context) +} + +#[simplex::test] +fn full_add_256_overflow_carry_low_false(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::MAX); + + let result = (b - 1).to_big_endian(); + let result_carry = true; + let carry_low = false; + + case(FullAdd256) + .args(a.to_big_endian(), b.to_big_endian()) + .third_arg(carry_low) + .expect(result) + .flag(result_carry) + .run(&context) +} + +#[simplex::test] +fn full_add_256_not_overflow_carry_low_true(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 + 1).to_big_endian(); + let result_carry = false; + let carry_low = true; + + case(FullAdd256) + .args(a.to_big_endian(), b.to_big_endian()) + .third_arg(carry_low) + .expect(result) + .flag(result_carry) + .run(&context) +} + +#[simplex::test] +fn full_add_256_overflow_carry_low_true(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + let result = b; + let result_carry = true; + let carry_low = true; + + case(FullAdd256) + .args(a.to_big_endian(), b) + .third_arg(carry_low) + .expect(result) + .flag(result_carry) + .run(&context) +} diff --git a/tests/u256_math_test.rs b/tests/stdlib/u256/math/api.rs similarity index 69% rename from tests/u256_math_test.rs rename to tests/stdlib/u256/math/api.rs index 67ebc4a..3964165 100644 --- a/tests/u256_math_test.rs +++ b/tests/stdlib/u256/math/api.rs @@ -1,13 +1,11 @@ -mod common; - use primitive_types::U256; -use common::u256_wrapper::U256Wrapper; -use common::uint::TestUint; +use crate::common::u256_wrapper::U256Wrapper; +use crate::common::uint::TestUint; -use simplicityhl_std::artifacts::u256_math_test::U256MathTestProgram; -use simplicityhl_std::artifacts::u256_math_test::derived_u256_math_test::{ - U256MathTestArguments, U256MathTestWitness, +use simplicityhl_std::artifacts::tests::u256::math::api::ApiProgram as U256MathTestProgram; +use simplicityhl_std::artifacts::tests::u256::math::api::derived_api::{ + ApiArguments as U256MathTestArguments, ApiWitness as U256MathTestWitness, }; // The only per-width code for the common operations. @@ -40,9 +38,5 @@ impl TestUint for U256Wrapper { } } -mod u256_math_tests { - use super::*; - - // Stamps the 22 `#[simplex::test]` entry points for U256Wrapper. Logic lives in common::uint. - uint_tests!(U256Wrapper); -} +// Stamps the 22 `#[simplex::test]` entry points for U256Wrapper. Logic lives in common::uint. +crate::uint_tests!(U256Wrapper); diff --git a/tests/stdlib/u256/math/div.rs b/tests/stdlib/u256/math/div.rs new file mode 100644 index 0000000..8fb84db --- /dev/null +++ b/tests/stdlib/u256/math/div.rs @@ -0,0 +1,453 @@ +use primitive_types::U256; + +use crate::common::core::{Expect, run}; +use crate::common::helper::generate_u256; + +use simplicityhl_std::artifacts::tests::u256::math::div::DivProgram as U256TestDivProgram; +use simplicityhl_std::artifacts::tests::u256::math::div::derived_div::{ + DivArguments as U256TestDivArguments, DivWitness as U256TestDivWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + CalculateNormalizerBase128, + DivMod256_64, + AlgorithmD256_128, + DivMod256_128, + DivMod256, + Div256, +} + +fn program() -> U256TestDivProgram { + U256TestDivProgram::new(&U256TestDivArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U256TestDivWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U256TestDivWitness { + function_index: function as u8, + first_arg: [0; 32], + second_arg: [0; 32], + expected: None, + second_expected: [0; 32], + }, + } +} + +impl Case { + /// Only `first_arg`, for the arms that ignore the second operand. + fn arg(mut self, a: [u8; 32]) -> Self { + self.witness.first_arg = a; + self + } + + /// The two operands, `first_arg` and `second_arg`. + fn args(mut self, a: [u8; 32], b: [u8; 32]) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = Some(expected); + self + } + + /// `second_expected`: the arm's second result. + fn second(mut self, second_expected: [u8; 32]) -> Self { + self.witness.second_expected = second_expected; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn calculate_normalizer_base_128(context: simplex::TestContext) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let a_high = (a >> 128).as_u128(); + + let norm = threshold.div_ceil(a_high); + + case(CalculateNormalizerBase128) + .arg(a.to_big_endian()) + .expect(U256::from(norm).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn calculate_normalizer_base_128_norm_is_1(context: simplex::TestContext) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + // a >= 2^255 keeps a_high >= 2^127, so the divisor is already normalized + let a = generate_u256(U256::from(2).pow(U256::from(255)), U256::MAX); + let a_high = (a >> 128).as_u128(); + + let norm = threshold.div_ceil(a_high); + assert_eq!(norm, 1); + + case(CalculateNormalizerBase128) + .arg(a.to_big_endian()) + .expect(U256::from(norm).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn calculate_normalizer_base_128_norm_greater_than_1( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + // a < 2^255 keeps a_high < 2^127, so the divisor has to be scaled up + let a = generate_u256( + U256::from(u128::MAX) + 1, + U256::from(2).pow(U256::from(255)) - 1, + ); + let a_high = (a >> 128).as_u128(); + + let norm = threshold.div_ceil(a_high); + assert!(norm > 1); + + case(CalculateNormalizerBase128) + .arg(a.to_big_endian()) + .expect(U256::from(norm).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn calculate_normalizer_base_128_a_is_u128_fail( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + let a = generate_u256(U256::one(), U256::from(threshold) - 1); + + let norm: u128 = threshold.div_ceil(a.low_u128()); + + case(CalculateNormalizerBase128) + .arg(a.to_big_endian()) + .expect(U256::from(norm).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn calculate_normalizer_base_128_b_is_zero_fail( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let a = [0; 32]; + + case(CalculateNormalizerBase128) + .arg(a) + .expect([0; 32]) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn 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(); + + case(DivMod256_64) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn div_mod_256_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + case(DivMod256_64) + .args(a.to_big_endian(), b) + .expect([0; 32]) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn algorithm_d_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(); + + case(AlgorithmD256_128) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn algorithm_d_256_128_fail_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(); + + case(AlgorithmD256_128) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(q) + .second(r) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn algorithm_d_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + case(AlgorithmD256_128) + .args(a.to_big_endian(), b) + .expect([0; 32]) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn algorithm_d_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(); + + case(AlgorithmD256_128) + .args(a, a) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(DivMod256_128) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(DivMod256_128) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn div_mod_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + case(DivMod256_128) + .args(a.to_big_endian(), b) + .expect([0; 32]) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn 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(); + + case(DivMod256_128) + .args(a, a) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(DivMod256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(DivMod256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(DivMod256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(DivMod256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(DivMod256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(q) + .second(r) + .run(&context) +} + +#[simplex::test] +fn div_mod_256_a_equal_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + case(DivMod256) + .args(a, a) + .expect(U256::one().to_big_endian()) + .second([0; 32]) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(DivMod256) + .args(a, b) + .expect(U256::one().to_big_endian()) + .second(U256::from(u128::MAX).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(DivMod256) + .args(a, b) + .expect([0; 32]) + .second(a) + .run(&context) +} + +#[simplex::test] +fn 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); + + case(DivMod256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(q.to_big_endian()) + .second(r.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn 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(); + + case(Div256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn div_256_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + case(Div256) + .args(a.to_big_endian(), b) + .expect([0; 32]) + .run(&context) +} diff --git a/tests/stdlib/u256/math/mod.rs b/tests/stdlib/u256/math/mod.rs new file mode 100644 index 0000000..297b0a0 --- /dev/null +++ b/tests/stdlib/u256/math/mod.rs @@ -0,0 +1,4 @@ +mod add; +mod api; +mod div; +mod sub_mul; diff --git a/tests/stdlib/u256/math/sub_mul.rs b/tests/stdlib/u256/math/sub_mul.rs new file mode 100644 index 0000000..2d5a64d --- /dev/null +++ b/tests/stdlib/u256/math/sub_mul.rs @@ -0,0 +1,276 @@ +use primitive_types::U256; +use primitive_types::U512; +use rand::Rng; + +use crate::common::core::{Expect, run}; +use crate::common::helper::generate_u256; + +use simplicityhl_std::artifacts::tests::u256::math::sub_mul::SubMulProgram as U256TestSubMulProgram; +use simplicityhl_std::artifacts::tests::u256::math::sub_mul::derived_sub_mul::{ + SubMulArguments as U256TestSubMulArguments, SubMulWitness as U256TestSubMulWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + Sub256, + Mul256, + Mul256_64, + Mul256_128, + Mul512_128, + SafeMul256_128, +} + +fn program() -> U256TestSubMulProgram { + U256TestSubMulProgram::new(&U256TestSubMulArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U256TestSubMulWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U256TestSubMulWitness { + function_index: function as u8, + first_arg: [0; 32], + second_arg: [0; 32], + third_arg: 0, + expected: None, + expected_bool: false, + second_expected: [0; 32], + third_expected: [0; 32], + }, + } +} + +impl Case { + /// The two operands, `first_arg` and `second_arg`. + fn args(mut self, a: [u8; 32], b: [u8; 32]) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self + } + + /// `third_arg`: the extra operand only some arms take. + fn third_arg(mut self, c: u128) -> Self { + self.witness.third_arg = c; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = Some(expected); + self + } + + /// `expected_bool`: the boolean the arm should report. + fn flag(mut self, expected_bool: bool) -> Self { + self.witness.expected_bool = expected_bool; + self + } + + /// `second_expected`: the arm's second result. + fn second(mut self, second_expected: [u8; 32]) -> Self { + self.witness.second_expected = second_expected; + self + } + + /// `third_expected`: the arm's third result. + fn third(mut self, third_expected: [u8; 32]) -> Self { + self.witness.third_expected = third_expected; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +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()) +} + +#[simplex::test] +fn 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(); + + case(Sub256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result) + .run(&context) +} + +#[simplex::test] +fn sub_256_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + + case(Sub256).args(a, a).expect([0; 32]).run(&context) +} + +#[simplex::test] +fn 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); + + case(Sub256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result.to_big_endian()) + .flag(carry) + .run(&context) +} + +#[simplex::test] +fn 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); + + case(Sub256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result.to_big_endian()) + .flag(carry) + .run(&context) +} + +#[simplex::test] +fn sub_256_diff_is_u256_max(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX.to_big_endian(); + let b = U256::zero(); + + case(Sub256) + .args(a, b.to_big_endian()) + .expect(a) + .run(&context) +} + +#[simplex::test] +fn 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; + + case(Sub256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result.to_big_endian()) + .flag(true) + .run(&context) +} + +#[simplex::test] +fn 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); + + case(Mul256) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result_high) + .second(result_low) + .run(&context) +} + +#[simplex::test] +fn 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); + + case(Mul256_64) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result_high) + .second(result_low) + .run(&context) +} + +#[simplex::test] +fn 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); + + case(Mul256_128) + .args(a.to_big_endian(), b.to_big_endian()) + .expect(result_high) + .second(result_low) + .run(&context) +} + +#[simplex::test] +fn mul_512_128(context: simplex::TestContext) -> anyhow::Result<()> { + let a_1 = generate_u256(U256::one(), U256::MAX); + let a_0 = generate_u256(U256::one(), U256::MAX); + let b = rand::thread_rng().gen_range(1..=u128::MAX); + + let result_low = U512::from(a_0) * U512::from(b); + let result_high = U512::from(a_1) * U512::from(b); + + let (res_1, res_0) = split_u512(result_low.to_big_endian()); + let (res_3, res_2) = split_u512(result_high.to_big_endian()); + + let res_2_1 = U512::from_big_endian(&res_1) + U512::from_big_endian(&res_2); + let (res_3_1, res_2_1) = split_u512(res_2_1.to_big_endian()); + + let res_3_final = + (U256::from_big_endian(&res_3_1) + U256::from_big_endian(&res_3)).to_big_endian(); + + case(Mul512_128) + .args(a_1.to_big_endian(), a_0.to_big_endian()) + .third_arg(b) + .expect(res_3_final) + .second(res_2_1) + .third(res_0) + .run(&context) +} + +#[simplex::test] +fn safe_mul_256_128_fitting(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u128::MAX)); + let b = generate_u256(U256::zero(), U256::from(u128::MAX)); + + case(SafeMul256_128) + .args(a.to_big_endian(), b.to_big_endian()) + .expect((a * b).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_mul_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let b = U256::from(u128::MAX); + + case(SafeMul256_128) + .args(a.to_big_endian(), b.to_big_endian()) + .expect([0; 32]) + .expecting(&context, Expect::AssertFailed) +} diff --git a/tests/stdlib/u256/mod.rs b/tests/stdlib/u256/mod.rs new file mode 100644 index 0000000..5af8775 --- /dev/null +++ b/tests/stdlib/u256/mod.rs @@ -0,0 +1,5 @@ +mod bit; +mod comparison; +mod convert; +mod math; +mod mul_div; diff --git a/tests/stdlib/u256/mul_div.rs b/tests/stdlib/u256/mul_div.rs new file mode 100644 index 0000000..afd5123 --- /dev/null +++ b/tests/stdlib/u256/mul_div.rs @@ -0,0 +1,262 @@ +use primitive_types::U256; +use std::cmp::max; +use std::ops::Div; + +use crate::common::core::{Expect, run}; +use crate::common::helper::generate_u256; + +use simplicityhl_std::artifacts::tests::u256::mul_div::MulDivProgram as U256MulDivTestProgram; +use simplicityhl_std::artifacts::tests::u256::mul_div::derived_mul_div::{ + MulDivArguments as U256MulDivTestArguments, MulDivWitness as U256MulDivTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + MulDiv, +} + +fn program() -> U256MulDivTestProgram { + U256MulDivTestProgram::new(&U256MulDivTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U256MulDivTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U256MulDivTestWitness { + function_index: function as u8, + first_arg: [0; 32], + second_arg: [0; 32], + third_arg: [0; 32], + expected: None, + }, + } +} + +impl Case { + /// The three operands, `first_arg`, `second_arg` and `third_arg`. + fn args(mut self, a: [u8; 32], b: [u8; 32], c: [u8; 32]) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self.witness.third_arg = c; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = Some(expected); + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +fn safe_u512_to_u256(a: [u8; 64]) -> [u8; 32] { + let high = U256::from_big_endian(&a[0..32]); + let low = U256::from_big_endian(&a[32..64]); + + assert!(high == U256::zero()); + + low.to_big_endian() +} + +#[simplex::test] +fn mul_div_256_product_fits_into_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u128::MAX)); + let b = generate_u256(U256::zero(), U256::from(u128::MAX)); + let c = generate_u256(U256::one(), U256::MAX); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + case(MulDiv) + .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) + .expect(res) + .run(&context) +} + +#[simplex::test] +fn mul_div_256_intermediate_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(2), U256::MAX); + let b = U256::MAX; + let c = generate_u256(a, U256::MAX); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + case(MulDiv) + .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) + .expect(res) + .run(&context) +} + +#[simplex::test] +fn mul_div_256_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(2), U256::MAX); + let b = U256::MAX; + let c = generate_u256(U256::one(), a); + + case(MulDiv) + .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) + .expect([0; 32]) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn mul_div_256_remainder_is_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let b = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let c = a; + + case(MulDiv) + .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) + .expect(b.to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn mul_div_256_denominator_is_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let c = generate_u256(b, U256::from(u128::MAX)); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + case(MulDiv) + .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) + .expect(res) + .run(&context) +} + +#[simplex::test] +fn mul_div_256_min_denom_high(context: simplex::TestContext) -> anyhow::Result<()> { + let pow129: U256 = U256::from(2).pow(U256::from(129)); + + let a = generate_u256(U256::from(u128::MAX) + 1, pow129); + let b = generate_u256(U256::from(u128::MAX) + 1, pow129); + let c = generate_u256(U256::from(u128::MAX) + 1, pow129 - 1); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + case(MulDiv) + .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) + .expect(res) + .run(&context) +} + +#[simplex::test] +fn mul_div_256_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::MAX); + let c = U256::zero(); + + case(MulDiv) + .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) + .expect([0; 32]) + .run(&context) +} + +#[simplex::test] +fn mul_div_256_algorithm_d_512_256_check(context: simplex::TestContext) -> anyhow::Result<()> { + let pow2_128: U256 = U256::from(u128::MAX) + 1; + + let a = generate_u256(pow2_128, U256::MAX); + let b = generate_u256(pow2_128, U256::MAX - pow2_128); + + let product = a.full_mul(b); + let result_high = U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); + + let c = generate_u256(max(U256::from(u128::MAX) + 1, result_high + 1), U256::MAX); + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + case(MulDiv) + .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) + .expect(res) + .run(&context) +} + +#[simplex::test] +fn mul_div_256_algorithm_d_512_256_c_is_res_high( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let pow2_128: U256 = U256::from(u128::MAX) + 1; + + let a: U256 = generate_u256(pow2_128, U256::MAX); + let b = generate_u256(pow2_128, U256::MAX - pow2_128); + + let product = a.full_mul(b); + let result_high = U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); + + let c = result_high + 1; + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + case(MulDiv) + .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) + .expect(res) + .run(&context) +} + +#[simplex::test] +fn mul_div_256_normalize_to_threshold_512_127_norm_is_1( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let pow2_128: U256 = U256::from(u128::MAX) + 1; + + let a: U256 = generate_u256(pow2_128, U256::MAX); + let b = generate_u256(pow2_128, U256::MAX - pow2_128); + + let product = a.full_mul(b); + let result_high = U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); + + let c = generate_u256( + max(U256::from(2).pow(U256::from(255)), result_high + 1), + U256::MAX, + ); + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + case(MulDiv) + .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) + .expect(res) + .run(&context) +} + +#[simplex::test] +fn mul_div_256_normalize_to_threshold_512_127_norm_greater_than_1( + context: simplex::TestContext, +) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let b = generate_u256( + U256::from(u128::MAX) + 1, + U256::from(2).pow(U256::from(192)), + ); + + let product = a.full_mul(b); + let result_high = + U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))) + 1; + + let c = generate_u256( + max(U256::from(u128::MAX) + 1, result_high), + U256::from(2).pow(U256::from(255)) - 1, + ); + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + case(MulDiv) + .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) + .expect(res) + .run(&context) +} diff --git a/tests/stdlib/u32/convert.rs b/tests/stdlib/u32/convert.rs new file mode 100644 index 0000000..ccf0cf9 --- /dev/null +++ b/tests/stdlib/u32/convert.rs @@ -0,0 +1,175 @@ +use primitive_types::U256; +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::u32::convert::ConvertProgram as U32ConvertTestProgram; +use simplicityhl_std::artifacts::tests::u32::convert::derived_convert::{ + ConvertArguments as U32ConvertTestArguments, ConvertWitness as U32ConvertTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + U32ToU64, + U32ToU128, + U32ToU256, + SplitU32IntoU8, + SplitU32IntoU16, + SafeU32ToU1, + SafeU32ToU8, + SafeU32ToU16, +} + +fn program() -> U32ConvertTestProgram { + U32ConvertTestProgram::new(&U32ConvertTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U32ConvertTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U32ConvertTestWitness { + function_index: function as u8, + first_arg: 0, + expected: [0; 32], + }, + } +} + +impl Case { + /// The operand, `first_arg`. + fn arg(mut self, a: u32) -> Self { + self.witness.first_arg = a; + self + } + + /// `expected`: the value the arm should produce. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = expected; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn u32_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX); + + case(U32ToU64) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u32_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX); + + case(U32ToU128) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u32_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX); + + case(U32ToU256) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u32_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX); + + case(SplitU32IntoU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u32_into_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX); + + case(SplitU32IntoU16) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u32_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(SafeU32ToU1) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u32_to_u1_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u32::MAX); + + case(SafeU32ToU1) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u32_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX as u32); + + case(SafeU32ToU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u32_to_u8_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u8::MAX as u32 + 1..=u32::MAX); + + case(SafeU32ToU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u32_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX as u32); + + case(SafeU32ToU16) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u32_to_u16_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u16::MAX as u32 + 1..=u32::MAX); + + case(SafeU32ToU16) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} diff --git a/tests/u32_math_test.rs b/tests/stdlib/u32/math.rs similarity index 63% rename from tests/u32_math_test.rs rename to tests/stdlib/u32/math.rs index 2db6eda..c25a816 100644 --- a/tests/u32_math_test.rs +++ b/tests/stdlib/u32/math.rs @@ -1,10 +1,8 @@ -mod common; +use crate::common::uint::TestUint; -use common::uint::TestUint; - -use simplicityhl_std::artifacts::u32_math_test::U32MathTestProgram; -use simplicityhl_std::artifacts::u32_math_test::derived_u32_math_test::{ - U32MathTestArguments, U32MathTestWitness, +use simplicityhl_std::artifacts::tests::u32::math::MathProgram as U32MathTestProgram; +use simplicityhl_std::artifacts::tests::u32::math::derived_math::{ + MathArguments as U32MathTestArguments, MathWitness as U32MathTestWitness, }; // The only per-width code for the common operations. @@ -32,9 +30,5 @@ impl TestUint for u32 { } } -mod u32_math_tests { - use super::*; - - // Stamps the 22 `#[simplex::test]` entry points for u32. Logic lives in common::uint. - uint_tests!(u32); -} +// Stamps the 22 `#[simplex::test]` entry points for u32. Logic lives in common::uint. +crate::uint_tests!(u32); diff --git a/tests/stdlib/u32/mod.rs b/tests/stdlib/u32/mod.rs new file mode 100644 index 0000000..828ff4c --- /dev/null +++ b/tests/stdlib/u32/mod.rs @@ -0,0 +1,3 @@ +mod convert; +mod math; +mod mul_div; diff --git a/tests/stdlib/u32/mul_div.rs b/tests/stdlib/u32/mul_div.rs new file mode 100644 index 0000000..1a45dca --- /dev/null +++ b/tests/stdlib/u32/mul_div.rs @@ -0,0 +1,107 @@ +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::tests::u32::mul_div::MulDivProgram as U32MulDivTestProgram; +use simplicityhl_std::artifacts::tests::u32::mul_div::derived_mul_div::{ + MulDivArguments as U32MulDivTestArguments, MulDivWitness as U32MulDivTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + MulDiv, +} + +fn program() -> U32MulDivTestProgram { + U32MulDivTestProgram::new(&U32MulDivTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +/// +/// Every field the chosen arm ignores keeps its default, so a test names only +/// the values it actually depends on. +struct Case { + witness: U32MulDivTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U32MulDivTestWitness { + function_index: function as u8, + first_arg: 0, + second_arg: 0, + third_arg: 0, + expected: None, + }, + } +} + +impl Case { + /// The three operands, `first_arg`, `second_arg` and `third_arg`. + fn args(mut self, a: u32, b: u32, c: u32) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self.witness.third_arg = c; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: u32) -> Self { + self.witness.expected = Some(expected); + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn mul_div_32_product_is_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX) as u32; + let b = rand::thread_rng().gen_range(0..=u16::MAX) as u32; + let c = rand::thread_rng().gen_range(1..=u32::MAX); + + let res = a * b / c; + + case(MulDiv).args(a, b, c).expect(res).run(&context) +} + +#[simplex::test] +fn mul_div_32_intermediate_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u32::MAX); + let b = u32::MAX; + let c = rand::thread_rng().gen_range(a..=u32::MAX); + + let res = (a as u64) * (b as u64) / (c as u64); + + case(MulDiv).args(a, b, c).expect(res as u32).run(&context) +} + +#[simplex::test] +fn mul_div_32_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u32::MAX); + let b = u32::MAX; + let c = rand::thread_rng().gen_range(1..a); + + case(MulDiv) + .args(a, b, c) + .expect(0) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn mul_div_32_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u32::MAX); + let b = rand::thread_rng().gen_range(1..=u32::MAX); + let c = 0; + + case(MulDiv).args(a, b, c).expect(0).run(&context) +} diff --git a/tests/stdlib/u64/convert.rs b/tests/stdlib/u64/convert.rs new file mode 100644 index 0000000..78a6289 --- /dev/null +++ b/tests/stdlib/u64/convert.rs @@ -0,0 +1,196 @@ +use primitive_types::U256; +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::u64::convert::ConvertProgram as U64ConvertTestProgram; +use simplicityhl_std::artifacts::tests::u64::convert::derived_convert::{ + ConvertArguments as U64ConvertTestArguments, ConvertWitness as U64ConvertTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + U64ToU128, + U64ToU256, + SplitU64IntoU8, + SplitU64IntoU16, + SplitU64IntoU32, + SafeU64ToU1, + SafeU64ToU8, + SafeU64ToU16, + SafeU64ToU32, +} + +fn program() -> U64ConvertTestProgram { + U64ConvertTestProgram::new(&U64ConvertTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U64ConvertTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U64ConvertTestWitness { + function_index: function as u8, + first_arg: 0, + expected: [0; 32], + }, + } +} + +impl Case { + /// The operand, `first_arg`. + fn arg(mut self, a: u64) -> Self { + self.witness.first_arg = a; + self + } + + /// `expected`: the value the arm should produce. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = expected; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn u64_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + case(U64ToU128) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u64_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + case(U64ToU256) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u64_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + case(SplitU64IntoU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u64_into_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + case(SplitU64IntoU16) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u64_into_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + case(SplitU64IntoU32) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u64_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(SafeU64ToU1) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u64_to_u1_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u64::MAX); + + case(SafeU64ToU1) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u64_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX as u64); + + case(SafeU64ToU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u64_to_u8_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u8::MAX as u64 + 1..=u64::MAX); + + case(SafeU64ToU8) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u64_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX as u64); + + case(SafeU64ToU16) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u64_to_u16_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u16::MAX as u64 + 1..=u64::MAX); + + case(SafeU64ToU16) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn safe_u64_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX as u64); + + case(SafeU64ToU32) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u64_to_u32_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u32::MAX as u64 + 1..=u64::MAX); + + case(SafeU64ToU32) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} diff --git a/tests/u64_math_test.rs b/tests/stdlib/u64/math.rs similarity index 63% rename from tests/u64_math_test.rs rename to tests/stdlib/u64/math.rs index 216b4bf..327894e 100644 --- a/tests/u64_math_test.rs +++ b/tests/stdlib/u64/math.rs @@ -1,10 +1,8 @@ -mod common; +use crate::common::uint::TestUint; -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, +use simplicityhl_std::artifacts::tests::u64::math::MathProgram as U64MathTestProgram; +use simplicityhl_std::artifacts::tests::u64::math::derived_math::{ + MathArguments as U64MathTestArguments, MathWitness as U64MathTestWitness, }; // The only per-width code for the common operations. @@ -32,9 +30,5 @@ impl TestUint for u64 { } } -mod u64_math_tests { - use super::*; - - // Stamps the 22 `#[simplex::test]` entry points for u64. Logic lives in common::uint. - uint_tests!(u64); -} +// Stamps the 22 `#[simplex::test]` entry points for u64. Logic lives in common::uint. +crate::uint_tests!(u64); diff --git a/tests/stdlib/u64/mod.rs b/tests/stdlib/u64/mod.rs new file mode 100644 index 0000000..828ff4c --- /dev/null +++ b/tests/stdlib/u64/mod.rs @@ -0,0 +1,3 @@ +mod convert; +mod math; +mod mul_div; diff --git a/tests/stdlib/u64/mul_div.rs b/tests/stdlib/u64/mul_div.rs new file mode 100644 index 0000000..d460e28 --- /dev/null +++ b/tests/stdlib/u64/mul_div.rs @@ -0,0 +1,104 @@ +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::tests::u64::mul_div::MulDivProgram as U64MulDivTestProgram; +use simplicityhl_std::artifacts::tests::u64::mul_div::derived_mul_div::{ + MulDivArguments as U64MulDivTestArguments, MulDivWitness as U64MulDivTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + MulDiv, +} + +fn program() -> U64MulDivTestProgram { + U64MulDivTestProgram::new(&U64MulDivTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U64MulDivTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U64MulDivTestWitness { + function_index: function as u8, + first_arg: 0, + second_arg: 0, + third_arg: 0, + expected: None, + }, + } +} + +impl Case { + /// The three operands, `first_arg`, `second_arg` and `third_arg`. + fn args(mut self, a: u64, b: u64, c: u64) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self.witness.third_arg = c; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: u64) -> Self { + self.witness.expected = Some(expected); + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn mul_div_64_product_is_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX) as u64; + let b = rand::thread_rng().gen_range(0..=u32::MAX) as u64; + let c = rand::thread_rng().gen_range(1..=u64::MAX); + + let res = a * b / c; + + case(MulDiv).args(a, b, c).expect(res).run(&context) +} + +#[simplex::test] +fn mul_div_64_intermediate_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u64::MAX); + let b = u64::MAX; + let c = rand::thread_rng().gen_range(a..=u64::MAX); + + let res = (a as u128) * (b as u128) / (c as u128); + + case(MulDiv).args(a, b, c).expect(res as u64).run(&context) +} + +#[simplex::test] +fn mul_div_64_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u64::MAX); + let b = u64::MAX; + let c = rand::thread_rng().gen_range(1..a); + + case(MulDiv) + .args(a, b, c) + .expect(0) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn mul_div_64_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u64::MAX); + let b = rand::thread_rng().gen_range(1..=u64::MAX); + let c = 0; + + case(MulDiv).args(a, b, c).expect(0).run(&context) +} diff --git a/tests/stdlib/u8/convert.rs b/tests/stdlib/u8/convert.rs new file mode 100644 index 0000000..d536dd8 --- /dev/null +++ b/tests/stdlib/u8/convert.rs @@ -0,0 +1,144 @@ +use primitive_types::U256; +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::tests::u8::convert::ConvertProgram as U8ConvertTestProgram; +use simplicityhl_std::artifacts::tests::u8::convert::derived_convert::{ + ConvertArguments as U8ConvertTestArguments, ConvertWitness as U8ConvertTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + U8ToU16, + U8ToU32, + U8ToU64, + U8ToU128, + U8ToU256, + SplitU8IntoU1, + SafeU8ToU1, +} + +fn program() -> U8ConvertTestProgram { + U8ConvertTestProgram::new(&U8ConvertTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U8ConvertTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U8ConvertTestWitness { + function_index: function as u8, + first_arg: 0, + expected: [0; 32], + }, + } +} + +impl Case { + /// The operand, `first_arg`. + fn arg(mut self, a: u8) -> Self { + self.witness.first_arg = a; + self + } + + /// `expected`: the value the arm should produce. + fn expect(mut self, expected: [u8; 32]) -> Self { + self.witness.expected = expected; + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn u8_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + case(U8ToU16) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u8_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + case(U8ToU32) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u8_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + case(U8ToU64) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u8_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + case(U8ToU128) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn u8_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + case(U8ToU256) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn split_u8_into_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX); + + case(SplitU8IntoU1) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u8_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + case(SafeU8ToU1) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .run(&context) +} + +#[simplex::test] +fn safe_u8_to_u1_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u8::MAX); + + case(SafeU8ToU1) + .arg(a) + .expect(U256::from(a).to_big_endian()) + .expecting(&context, Expect::AssertFailed) +} diff --git a/tests/u8_math_test.rs b/tests/stdlib/u8/math.rs similarity index 63% rename from tests/u8_math_test.rs rename to tests/stdlib/u8/math.rs index 3ff3d7b..cefd1b8 100644 --- a/tests/u8_math_test.rs +++ b/tests/stdlib/u8/math.rs @@ -1,10 +1,8 @@ -mod common; +use crate::common::uint::TestUint; -use common::uint::TestUint; - -use simplicityhl_std::artifacts::u8_math_test::U8MathTestProgram; -use simplicityhl_std::artifacts::u8_math_test::derived_u8_math_test::{ - U8MathTestArguments, U8MathTestWitness, +use simplicityhl_std::artifacts::tests::u8::math::MathProgram as U8MathTestProgram; +use simplicityhl_std::artifacts::tests::u8::math::derived_math::{ + MathArguments as U8MathTestArguments, MathWitness as U8MathTestWitness, }; // The only per-width code for the common operations. @@ -32,9 +30,5 @@ impl TestUint for u8 { } } -mod u8_math_tests { - use super::*; - - // Stamps the 22 `#[simplex::test]` entry points for u8. Logic lives in common::uint. - uint_tests!(u8); -} +// Stamps the 22 `#[simplex::test]` entry points for u8. Logic lives in common::uint. +crate::uint_tests!(u8); diff --git a/tests/stdlib/u8/mod.rs b/tests/stdlib/u8/mod.rs new file mode 100644 index 0000000..828ff4c --- /dev/null +++ b/tests/stdlib/u8/mod.rs @@ -0,0 +1,3 @@ +mod convert; +mod math; +mod mul_div; diff --git a/tests/stdlib/u8/mul_div.rs b/tests/stdlib/u8/mul_div.rs new file mode 100644 index 0000000..eddc18b --- /dev/null +++ b/tests/stdlib/u8/mul_div.rs @@ -0,0 +1,104 @@ +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::tests::u8::mul_div::MulDivProgram as U8MulDivTestProgram; +use simplicityhl_std::artifacts::tests::u8::mul_div::derived_mul_div::{ + MulDivArguments as U8MulDivTestArguments, MulDivWitness as U8MulDivTestWitness, +}; + +use FunctionToTest::*; + +enum FunctionToTest { + MulDiv, +} + +fn program() -> U8MulDivTestProgram { + U8MulDivTestProgram::new(&U8MulDivTestArguments {}) +} + +/// One dispatch arm of the contract, plus the witness it reads. +struct Case { + witness: U8MulDivTestWitness, +} + +fn case(function: FunctionToTest) -> Case { + Case { + witness: U8MulDivTestWitness { + function_index: function as u8, + first_arg: 0, + second_arg: 0, + third_arg: 0, + expected: None, + }, + } +} + +impl Case { + /// The three operands, `first_arg`, `second_arg` and `third_arg`. + fn args(mut self, a: u8, b: u8, c: u8) -> Self { + self.witness.first_arg = a; + self.witness.second_arg = b; + self.witness.third_arg = c; + self + } + + /// The value the arm should return. `None`, the default, means the arm is + /// expected to produce nothing. + fn expect(mut self, expected: u8) -> Self { + self.witness.expected = Some(expected); + self + } + + /// Fund, spend, and expect the spend to succeed. + fn run(self, context: &simplex::TestContext) -> anyhow::Result<()> { + self.expecting(context, Expect::Ok) + } + + /// Fund, spend, and expect `expect`. + fn expecting(self, context: &simplex::TestContext, expect: Expect) -> anyhow::Result<()> { + run(context, program(), self.witness, expect) + } +} + +#[simplex::test] +fn mul_div_8_product_is_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u8::MAX); + let b = u8::MAX / a; + let c = rand::thread_rng().gen_range(1..=u8::MAX); + + let res = a * b / c; + + case(MulDiv).args(a, b, c).expect(res).run(&context) +} + +#[simplex::test] +fn mul_div_8_intermediate_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u8::MAX); + let b = u8::MAX; + let c = rand::thread_rng().gen_range(a..=u8::MAX); + + let res = (a as u16) * (b as u16) / (c as u16); + + case(MulDiv).args(a, b, c).expect(res as u8).run(&context) +} + +#[simplex::test] +fn mul_div_8_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u8::MAX); + let b = u8::MAX; + let c = rand::thread_rng().gen_range(1..a); + + case(MulDiv) + .args(a, b, c) + .expect(0) + .expecting(&context, Expect::AssertFailed) +} + +#[simplex::test] +fn mul_div_8_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u8::MAX); + let b = rand::thread_rng().gen_range(1..=u8::MAX); + let c = 0; + + case(MulDiv).args(a, b, c).expect(0).run(&context) +} diff --git a/tests/u128_basic_math_test.rs b/tests/u128_basic_math_test.rs deleted file mode 100644 index 5d0b4ab..0000000 --- a/tests/u128_basic_math_test.rs +++ /dev/null @@ -1,1006 +0,0 @@ -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_basic_math_test::U128BasicMathTestProgram; -use simplicityhl_std::artifacts::u128_basic_math_test::derived_u128_basic_math_test::{ - U128BasicMathTestArguments, U128BasicMathTestWitness, -}; - -enum FunctionToTest { - Add128, - Add128_64, - FullAdd128, - Sub128, - FullSub128, - Mul128, - Mul128_64, - CalculateNormalizerBase64, - EstimateQuotientDigitBase64, - DivMod128_64, - DivMod128, - Div128, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -const DEFAULT_EXPECTED: u128 = 0; - -fn program() -> U128BasicMathTestProgram { - U128BasicMathTestProgram::new(&U128BasicMathTestArguments {}) -} - -fn build_witness( - function: u8, - a: u128, - b: u128, - expected: Option, - expected_bool: bool, - second_expected: u128, -) -> U128BasicMathTestWitness { - U128BasicMathTestWitness { - function_index: function, - first_arg: a, - second_arg: b, - expected, - expected_bool, - second_expected, - } -} - -fn split_helper(a: U256) -> (u128, u128) { - let a_high = (a >> 128).as_u128(); - let a_low = a.low_u128(); - - (a_high, a_low) -} - -mod u128_tests_arithmetic { - use super::*; - - #[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); - let b = rand::thread_rng().gen_range(0..=u128::MAX / 2); - let result = a + b; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add128), - a, - b, - Some(result), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_add_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = u128::MAX; - let b = rand::thread_rng().gen_range(1..=u128::MAX); - let result = b - 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add128), - a, - b, - Some(result), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_add_128_64_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX / 2); - let b = rand::thread_rng().gen_range(0..=u64::MAX) as u128; - let result = a + b; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add128_64), - a, - b, - Some(result), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_add_128_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = u128::MAX; - let b = rand::thread_rng().gen_range(1..=u64::MAX) as u128; - let result = b - 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Add128_64), - a, - b, - Some(result), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[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, - ), - 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, - ), - 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, - ), - 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, - ), - 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); - let b = rand::thread_rng().gen_range(0..=a); - let result = a - b; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Sub128), - a, - b, - Some(result), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_sub_128_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Sub128), - a, - a, - Some(0), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_sub_128_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); - let b_high = rand::thread_rng().gen_range(0..=u64::MAX); - - let low: u64 = a as u64; - let b = ((b_high as u128) << 64) | (low as u128); - - let carry = a < b; - let result = a.wrapping_sub(b); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Sub128), - a, - b, - Some(result), - carry, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_sub_128_diff_is_u64_max(context: simplex::TestContext) -> anyhow::Result<()> { - let a_low: u64 = u64::MAX; - - let a_high = rand::thread_rng().gen_range(0..=u64::MAX); - let b_high = rand::thread_rng().gen_range(0..=u64::MAX); - - let a = ((a_high as u128) << 64) | (a_low as u128); - let b = (b_high as u128) << 64; // b_low is 0 - - let carry = a < b; - let result = a.wrapping_sub(b); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Sub128), - a, - b, - Some(result), - carry, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_sub_128_diff_is_u128_max(context: simplex::TestContext) -> anyhow::Result<()> { - let a = u128::MAX; - let b = 0; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Sub128), - a, - b, - Some(a), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_sub_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..u128::MAX); - let b = u128::MAX; - let result = a + 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Sub128), - a, - b, - Some(result), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[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, - ), - 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, - ), - 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, - ), - 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, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_mul_128(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); - let result = U256::from(a) * U256::from(b); - - let (result_high, result_low) = split_helper(result); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Mul128), - a, - b, - Some(result_high), - DEFAULT_BOOL, - result_low, - ), - Expect::Ok, - ) - } - - #[simplex::test] - 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::Mul128_64), - a, - b as u128, - Some(result_high), - DEFAULT_BOOL, - result_low, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_calculate_normalizer_base_64_b_is_u64( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let threshold = 1u128 << 63; - - let b = rand::thread_rng().gen_range(1..threshold); - - let norm: u128 = threshold.div_ceil(b); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::CalculateNormalizerBase64), - DEFAULT_EXPECTED, - b, - Some(norm), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_calculate_normalizer_base_64_b_is_big_enough_not_normalize( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let threshold = 1u128 << 63; - - let b = rand::thread_rng().gen_range(threshold..=u64::MAX as u128); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::CalculateNormalizerBase64), - DEFAULT_EXPECTED, - b, - Some(1), - false, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_calculate_normalizer_base_64_b_is_u128( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let threshold = 1u128 << 63; - - 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); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::CalculateNormalizerBase64), - DEFAULT_EXPECTED, - b, - Some(norm), - true, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_calculate_normalizer_base_64_b_is_u64_fail( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let b = rand::thread_rng().gen_range((u64::MAX as u128) + 1..=u128::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::CalculateNormalizerBase64), - DEFAULT_EXPECTED, - b, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn u128_test_calculate_normalizer_base_64_b_is_u128_fail( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let b = rand::thread_rng().gen_range(1..=u64::MAX as u128); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::CalculateNormalizerBase64), - DEFAULT_EXPECTED, - b, - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn u128_test_calculate_normalizer_base_64_b_is_zero_fail( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let b = 0; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::CalculateNormalizerBase64), - DEFAULT_EXPECTED, - b, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn u128_test_estimate_quotient_digit_base_64( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let threshold = 1u64 << 63; - - 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::EstimateQuotientDigitBase64), - a_high as u128, - a_low, - Some(q), - DEFAULT_BOOL, - b, - ), - Expect::Ok, - ) - } - - #[simplex::test] - 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 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::EstimateQuotientDigitBase64), - a_high as u128, - a_low, - Some(q), - DEFAULT_BOOL, - b, - ), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn test_div_mod_128_64(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); - - let q = a / b; - let r = a % b; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod128_64), - a, - b, - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn test_div_mod_128_64_overflow(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::DivMod128_64), - a, - b, - Some(DEFAULT_EXPECTED), - DEFAULT_BOOL, - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn u128_test_div_mod_128_a_less_than_b(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..u128::MAX); - let b = rand::thread_rng().gen_range(a + 1..=u128::MAX); - - let q = a / b; - let r = a % b; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod128), - a, - b, - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_div_mod_128_div_64(context: simplex::TestContext) -> anyhow::Result<()> { - let b = rand::thread_rng().gen_range(1..=u64::MAX); - let a = rand::thread_rng().gen_range(b..=u64::MAX) as u128; - - let q = a / b as u128; - let r = a % b as u128; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod128), - a, - b as u128, - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_div_mod_128_q_is_1(context: simplex::TestContext) -> anyhow::Result<()> { - // case where a >= b and a_high = b_high != 0 - let b_low = rand::thread_rng().gen_range(0..=u64::MAX); - let a_low = rand::thread_rng().gen_range(b_low..=u64::MAX); - let high = rand::thread_rng().gen_range(1..u64::MAX); - - let a = ((high as u128) << 64) | (a_low as u128); - let b = ((high as u128) << 64) | (b_low as u128); - - let q = a / b; - let r = a % b; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod128), - a, - b, - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_div_mod_128_b_is_u64(context: simplex::TestContext) -> anyhow::Result<()> { - let b = rand::thread_rng().gen_range(1..=u64::MAX as u128); - let a = rand::thread_rng().gen_range(u64::MAX as u128 + 1..=u128::MAX); - - let q = a / b; - let r = a % b; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod128), - a, - b, - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_div_mod_128_b_is_u128(context: simplex::TestContext) -> anyhow::Result<()> { - let b_high = rand::thread_rng().gen_range(1..u64::MAX); - let a_high = rand::thread_rng().gen_range(b_high + 1..=u64::MAX); - - let a = ((a_high as u128) << 64) | (rand::thread_rng().gen_range(0..u64::MAX) as u128); - let b = ((b_high as u128) << 64) | (rand::thread_rng().gen_range(0..u64::MAX) as u128); - - let q = a / b; - let r = a % b; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod128), - a, - b, - Some(q), - DEFAULT_BOOL, - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_div_mod_128_a_equal_b(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u128::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod128), - a, - a, - Some(1u128), - DEFAULT_BOOL, - 0u128, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_div_mod_128_equal_high_words_max_low_diff( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let high = rand::thread_rng().gen_range(1..=u64::MAX); - - let a = ((high as u128) << 64) | (u64::MAX as u128); - let b = (high as u128) << 64; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod128), - a, - b, - Some(1u128), - DEFAULT_BOOL, - u64::MAX as u128, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_div_mod_128_eq_high_words_a_less_than_b( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let high = rand::thread_rng().gen_range(1..=u64::MAX); - - let a = (high as u128) << 64; - let b = ((high as u128) << 64) | (u64::MAX as u128); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::DivMod128), - a, - b, - Some(0u128), - DEFAULT_BOOL, - a, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_div_128(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); - let b = rand::thread_rng().gen_range(1..=u128::MAX); - let result = a / b; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Div128), - a, - b, - Some(result), - DEFAULT_BOOL, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_div_128_div_by_zero(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::Div128), - a, - b, - Some(0), - DEFAULT_BOOL, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } -} diff --git a/tests/u128_convert_test.rs b/tests/u128_convert_test.rs deleted file mode 100644 index e2d0a22..0000000 --- a/tests/u128_convert_test.rs +++ /dev/null @@ -1,295 +0,0 @@ -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_mul_div_test.rs b/tests/u128_mul_div_test.rs deleted file mode 100644 index 5abd937..0000000 --- a/tests/u128_mul_div_test.rs +++ /dev/null @@ -1,108 +0,0 @@ -mod common; - -use primitive_types::U256; -use rand::Rng; - -use crate::common::core::{Expect, run}; - -use simplicityhl_std::artifacts::u128_mul_div_test::U128MulDivTestProgram; -use simplicityhl_std::artifacts::u128_mul_div_test::derived_u128_mul_div_test::{ - U128MulDivTestArguments, U128MulDivTestWitness, -}; - -const DEFAULT_EXPECTED: u128 = 0; - -enum FunctionToTest { - MulDiv, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -fn program() -> U128MulDivTestProgram { - U128MulDivTestProgram::new(&U128MulDivTestArguments {}) -} - -fn build_witness( - op: u8, - a: u128, - b: u128, - c: u128, - expected: Option, -) -> U128MulDivTestWitness { - U128MulDivTestWitness { - function_index: op, - first_arg: a, - second_arg: b, - third_arg: c, - expected, - } -} - -mod u128_mul_div_test { - use super::*; - - #[simplex::test] - fn u128_test_mul_div_128_product_is_u128(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u64::MAX) as u128; - let b = rand::thread_rng().gen_range(0..=u64::MAX) as u128; - let c = rand::thread_rng().gen_range(1..=u128::MAX); - - let res = a * b / c; - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_mul_div_128_intermediate_overflow( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(2..=u128::MAX); - let b = u128::MAX; - let c = rand::thread_rng().gen_range(a..=u128::MAX); - - let res = U256::from(a) * U256::from(b) / U256::from(c); - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res.low_u128())), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_mul_div_128_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(2..=u128::MAX); - let b = u128::MAX; - let c = rand::thread_rng().gen_range(1..a); - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn u128_test_mul_div_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u128::MAX); - let b = rand::thread_rng().gen_range(1..=u128::MAX); - let c = 0; - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), - Expect::Ok, - ) - } -} diff --git a/tests/u128_test_bits.rs b/tests/u128_test_bits.rs deleted file mode 100644 index 508d5f7..0000000 --- a/tests/u128_test_bits.rs +++ /dev/null @@ -1,233 +0,0 @@ -mod common; - -use rand::Rng; - -use crate::common::helper::DEFAULT_BOOL; -use common::core::{Expect, run}; - -use simplicityhl_std::artifacts::u128_test_bits::U128TestBitsProgram; -use simplicityhl_std::artifacts::u128_test_bits::derived_u128_test_bits::{ - U128TestBitsArguments, U128TestBitsWitness, -}; - -enum FunctionToTest { - And128, - Or128, - Eq128, - LeftShift128, - RightShift128, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -const DEFAULT_EXPECTED: u128 = 0; - -fn program() -> U128TestBitsProgram { - U128TestBitsProgram::new(&U128TestBitsArguments {}) -} - -fn build_witness( - function: u8, - a: u128, - b: u128, - expected: Option, - expected_bool: bool, -) -> U128TestBitsWitness { - U128TestBitsWitness { - function_index: function, - first_arg: a, - second_arg: b, - expected, - expected_bool, - } -} - -mod u128_tests_bits { - use super::*; - - #[simplex::test] - fn u128_test_and_128(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); - let result = a & b; - - run( - &context, - program(), - build_witness(op(FunctionToTest::And128), a, b, Some(result), DEFAULT_BOOL), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_or_128(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); - let result = a | b; - - run( - &context, - program(), - build_witness(op(FunctionToTest::Or128), a, b, Some(result), DEFAULT_BOOL), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_eq_128_true(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Eq128), - a, - a, - Some(DEFAULT_EXPECTED), - true, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_eq_128_false(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::Eq128), - a, - b, - Some(DEFAULT_EXPECTED), - false, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_left_shift_128(context: simplex::TestContext) -> anyhow::Result<()> { - let shift = rand::thread_rng().gen_range(1..=127_u128); - let val = rand::thread_rng().gen_range(0..=u128::MAX); - let result = val << shift; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::LeftShift128), - shift, - val, - Some(result), - DEFAULT_BOOL, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_left_shift_128_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { - let shift = 0; - let val = rand::thread_rng().gen_range(0..=u128::MAX); - let result = val; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::LeftShift128), - shift, - val, - Some(result), - DEFAULT_BOOL, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_left_shift_128_out_of_range(context: simplex::TestContext) -> anyhow::Result<()> { - let shift = rand::thread_rng().gen_range(128..=u8::MAX as u128); - let val = rand::thread_rng().gen_range(0..=u128::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::LeftShift128), - shift, - val, - Some(0), - DEFAULT_BOOL, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_right_shift_128(context: simplex::TestContext) -> anyhow::Result<()> { - let shift = rand::thread_rng().gen_range(1..=127_u128); - let val = rand::thread_rng().gen_range(0..=u128::MAX); - let result = val >> shift; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::RightShift128), - shift, - val, - Some(result), - DEFAULT_BOOL, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_right_shift_128_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { - let shift = 0; - let val = rand::thread_rng().gen_range(0..=u128::MAX); - let result = val; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::RightShift128), - shift, - val, - Some(result), - DEFAULT_BOOL, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_right_shift_128_out_of_range(context: simplex::TestContext) -> anyhow::Result<()> { - let shift = rand::thread_rng().gen_range(128..=u8::MAX as u128); - let val = rand::thread_rng().gen_range(0..=u128::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::RightShift128), - shift, - val, - Some(0), - DEFAULT_BOOL, - ), - Expect::Ok, - ) - } -} diff --git a/tests/u128_test_compare.rs b/tests/u128_test_compare.rs deleted file mode 100644 index 1c7a01e..0000000 --- a/tests/u128_test_compare.rs +++ /dev/null @@ -1,142 +0,0 @@ -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, - ) - } -} diff --git a/tests/u16_convert_test.rs b/tests/u16_convert_test.rs deleted file mode 100644 index 65df71a..0000000 --- a/tests/u16_convert_test.rs +++ /dev/null @@ -1,190 +0,0 @@ -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, - SplitU16IntoU8, - 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_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u16::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::SplitU16IntoU8), - 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_mul_div_test.rs b/tests/u16_mul_div_test.rs deleted file mode 100644 index eb18073..0000000 --- a/tests/u16_mul_div_test.rs +++ /dev/null @@ -1,100 +0,0 @@ -mod common; - -use crate::common::core::{Expect, run}; -use rand::Rng; - -use simplicityhl_std::artifacts::u16_mul_div_test::U16MulDivTestProgram; -use simplicityhl_std::artifacts::u16_mul_div_test::derived_u16_mul_div_test::{ - U16MulDivTestArguments, U16MulDivTestWitness, -}; - -const DEFAULT_EXPECTED: u16 = 0; - -enum FunctionToTest { - MulDiv, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -fn program() -> U16MulDivTestProgram { - U16MulDivTestProgram::new(&U16MulDivTestArguments {}) -} - -fn build_witness(op: u8, a: u16, b: u16, c: u16, expected: Option) -> U16MulDivTestWitness { - U16MulDivTestWitness { - function_index: op, - first_arg: a, - second_arg: b, - third_arg: c, - expected, - } -} - -mod u16_mul_div_test { - use super::*; - - #[simplex::test] - fn u16_test_mul_div_16_product_is_u16(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u8::MAX) as u16; - let b = rand::thread_rng().gen_range(0..=u8::MAX) as u16; - let c = rand::thread_rng().gen_range(1..=u16::MAX); - - let res = a * b / c; - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), - Expect::Ok, - ) - } - - #[simplex::test] - fn u16_test_mul_div_16_intermediate_overflow( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(2..=u16::MAX); - let b = u16::MAX; - let c = rand::thread_rng().gen_range(a..=u16::MAX); - - let res = (a as u32) * (b as u32) / (c as u32); - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u16)), - Expect::Ok, - ) - } - - #[simplex::test] - fn u16_test_mul_div_16_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(2..=u16::MAX); - let b = u16::MAX; - let c = rand::thread_rng().gen_range(1..a); - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn u16_test_mul_div_16_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u16::MAX); - let b = rand::thread_rng().gen_range(1..=u16::MAX); - let c = 0; - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), - Expect::Ok, - ) - } -} diff --git a/tests/u1_convert_test.rs b/tests/u1_convert_test.rs deleted file mode 100644 index 26c743c..0000000 --- a/tests/u1_convert_test.rs +++ /dev/null @@ -1,150 +0,0 @@ -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, - ) - } -} diff --git a/tests/u256_convert_test.rs b/tests/u256_convert_test.rs deleted file mode 100644 index 923c40a..0000000 --- a/tests/u256_convert_test.rs +++ /dev/null @@ -1,331 +0,0 @@ -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_mul_div_test.rs b/tests/u256_mul_div_test.rs deleted file mode 100644 index 5723c8a..0000000 --- a/tests/u256_mul_div_test.rs +++ /dev/null @@ -1,346 +0,0 @@ -mod common; - -use primitive_types::U256; -use std::cmp::max; -use std::ops::Div; - -use crate::common::core::{Expect, run}; -use crate::common::helper::generate_u256; - -use simplicityhl_std::artifacts::u256_mul_div_test::U256MulDivTestProgram; -use simplicityhl_std::artifacts::u256_mul_div_test::derived_u256_mul_div_test::{ - U256MulDivTestArguments, U256MulDivTestWitness, -}; - -const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; - -enum FunctionToTest { - MulDiv, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -fn program() -> U256MulDivTestProgram { - U256MulDivTestProgram::new(&U256MulDivTestArguments {}) -} - -fn build_witness( - op: u8, - a: [u8; 32], - b: [u8; 32], - c: [u8; 32], - expected: Option<[u8; 32]>, -) -> U256MulDivTestWitness { - U256MulDivTestWitness { - function_index: op, - first_arg: a, - second_arg: b, - third_arg: c, - expected, - } -} - -fn safe_u512_to_u256(a: [u8; 64]) -> [u8; 32] { - let high = U256::from_big_endian(&a[0..32]); - let low = U256::from_big_endian(&a[32..64]); - - assert!(high == U256::zero()); - - low.to_big_endian() -} - -mod u256_mul_div_test { - use super::*; - - #[simplex::test] - fn u256_test_mul_div_256_product_fits_into_u256( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::from(u128::MAX)); - let b = generate_u256(U256::zero(), U256::from(u128::MAX)); - let c = generate_u256(U256::one(), U256::MAX); - - let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::MulDiv), - a.to_big_endian(), - b.to_big_endian(), - c.to_big_endian(), - Some(res), - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_mul_div_256_intermediate_overflow( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = generate_u256(U256::from(2), U256::MAX); - let b = U256::MAX; - let c = generate_u256(a, U256::MAX); - - let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::MulDiv), - a.to_big_endian(), - b.to_big_endian(), - c.to_big_endian(), - Some(res), - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_mul_div_256_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::from(2), U256::MAX); - let b = U256::MAX; - let c = generate_u256(U256::one(), a); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::MulDiv), - a.to_big_endian(), - b.to_big_endian(), - c.to_big_endian(), - Some(DEFAULT_EXPECTED), - ), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn u256_test_mul_div_256_remainder_is_zero( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); - let b = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); - let c = a; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::MulDiv), - a.to_big_endian(), - b.to_big_endian(), - c.to_big_endian(), - Some(b.to_big_endian()), - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_mul_div_256_denominator_is_u128( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX); - let b = generate_u256(U256::one(), U256::from(u128::MAX)); - let c = generate_u256(b, U256::from(u128::MAX)); - - let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::MulDiv), - a.to_big_endian(), - b.to_big_endian(), - c.to_big_endian(), - Some(res), - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_mul_div_256_min_denom_high(context: simplex::TestContext) -> anyhow::Result<()> { - let pow129: U256 = U256::from(2).pow(U256::from(129)); - - let a = generate_u256(U256::from(u128::MAX) + 1, pow129); - let b = generate_u256(U256::from(u128::MAX) + 1, pow129); - let c = generate_u256(U256::from(u128::MAX) + 1, pow129 - 1); - - let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::MulDiv), - a.to_big_endian(), - b.to_big_endian(), - c.to_big_endian(), - Some(res), - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_mul_div_256_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::one(), U256::MAX); - let b = generate_u256(U256::one(), U256::MAX); - let c = U256::zero(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::MulDiv), - a.to_big_endian(), - b.to_big_endian(), - c.to_big_endian(), - Some([0; 32]), - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_mul_div_256_algorithm_d_512_256_check( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let pow2_128: U256 = U256::from(u128::MAX) + 1; - - let a = generate_u256(pow2_128, U256::MAX); - let b = generate_u256(pow2_128, U256::MAX - pow2_128); - - let product = a.full_mul(b); - let result_high = - U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); - - let c = generate_u256(max(U256::from(u128::MAX) + 1, result_high + 1), U256::MAX); - - let res = safe_u512_to_u256(product.div(c).to_big_endian()); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::MulDiv), - a.to_big_endian(), - b.to_big_endian(), - c.to_big_endian(), - Some(res), - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_mul_div_256_algorithm_d_512_256_c_is_res_high( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let pow2_128: U256 = U256::from(u128::MAX) + 1; - - let a: U256 = generate_u256(pow2_128, U256::MAX); - let b = generate_u256(pow2_128, U256::MAX - pow2_128); - - let product = a.full_mul(b); - let result_high = - U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); - - let c = result_high + 1; - - let res = safe_u512_to_u256(product.div(c).to_big_endian()); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::MulDiv), - a.to_big_endian(), - b.to_big_endian(), - c.to_big_endian(), - Some(res), - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_mul_div_256_normalize_to_threshold_512_127_norm_is_1( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let pow2_128: U256 = U256::from(u128::MAX) + 1; - - let a: U256 = generate_u256(pow2_128, U256::MAX); - let b = generate_u256(pow2_128, U256::MAX - pow2_128); - - let product = a.full_mul(b); - let result_high = - U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); - - let c = generate_u256( - max(U256::from(2).pow(U256::from(255)), result_high + 1), - U256::MAX, - ); - - let res = safe_u512_to_u256(product.div(c).to_big_endian()); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::MulDiv), - a.to_big_endian(), - b.to_big_endian(), - c.to_big_endian(), - Some(res), - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_mul_div_256_normalize_to_threshold_512_127_norm_greater_than_1( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); - let b = generate_u256( - U256::from(u128::MAX) + 1, - U256::from(2).pow(U256::from(192)), - ); - - let product = a.full_mul(b); - let result_high = - U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))) + 1; - - let c = generate_u256( - max(U256::from(u128::MAX) + 1, result_high), - U256::from(2).pow(U256::from(255)) - 1, - ); - - let res = safe_u512_to_u256(product.div(c).to_big_endian()); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::MulDiv), - a.to_big_endian(), - b.to_big_endian(), - c.to_big_endian(), - Some(res), - ), - Expect::Ok, - ) - } -} diff --git a/tests/u256_test_add.rs b/tests/u256_test_add.rs deleted file mode 100644 index bdda2ef..0000000 --- a/tests/u256_test_add.rs +++ /dev/null @@ -1,236 +0,0 @@ -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_add::U256TestAddProgram; -use simplicityhl_std::artifacts::u256_test_add::derived_u256_test_add::{ - U256TestAddArguments, U256TestAddWitness, -}; - -enum FunctionToTest { - Add256, - Add256_128, - FullAdd256, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -fn program() -> U256TestAddProgram { - U256TestAddProgram::new(&U256TestAddArguments {}) -} - -fn build_witness( - function: u8, - a: [u8; 32], - b: [u8; 32], - c: bool, - expected: Option<[u8; 32]>, - expected_bool: bool, -) -> U256TestAddWitness { - U256TestAddWitness { - function_index: function, - first_arg: a, - second_arg: b, - third_arg: c, - expected, - expected_bool, - } -} - -mod u256_tests_arithmetic { - use super::*; - - #[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(), - DEFAULT_BOOL, - 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(), - DEFAULT_BOOL, - 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(), - DEFAULT_BOOL, - 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(), - DEFAULT_BOOL, - Some(result), - true, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_full_add_256_not_overflow_carry_low_false( - 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(); - let result_carry = false; - let carry_low = false; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::FullAdd256), - a.to_big_endian(), - b.to_big_endian(), - carry_low, - Some(result), - result_carry, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_full_add_256_overflow_carry_low_false( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = U256::MAX; - let b = generate_u256(U256::one(), U256::MAX); - - let result = (b - 1).to_big_endian(); - let result_carry = true; - let carry_low = false; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::FullAdd256), - a.to_big_endian(), - b.to_big_endian(), - carry_low, - Some(result), - result_carry, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_full_add_256_not_overflow_carry_low_true( - 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 + 1).to_big_endian(); - let result_carry = false; - let carry_low = true; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::FullAdd256), - a.to_big_endian(), - b.to_big_endian(), - carry_low, - Some(result), - result_carry, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_full_add_256_overflow_carry_low_true( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = U256::MAX; - let b = generate_u256(U256::one(), U256::MAX).to_big_endian(); - - let result = b; - let result_carry = true; - let carry_low = true; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::FullAdd256), - a.to_big_endian(), - b, - carry_low, - Some(result), - result_carry, - ), - Expect::Ok, - ) - } -} diff --git a/tests/u256_test_bits.rs b/tests/u256_test_bits.rs deleted file mode 100644 index c0c78e6..0000000 --- a/tests/u256_test_bits.rs +++ /dev/null @@ -1,198 +0,0 @@ -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 deleted file mode 100644 index 560e113..0000000 --- a/tests/u256_test_compare.rs +++ /dev/null @@ -1,166 +0,0 @@ -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_compare { - 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 deleted file mode 100644 index d74a0ba..0000000 --- a/tests/u256_test_div.rs +++ /dev/null @@ -1,609 +0,0 @@ -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 { - CalculateNormalizerBase128, - DivMod256_64, - AlgorithmD256_128, - 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 u256_test_calculate_normalizer_base_128( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let threshold = 1u128 << 127; - - let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); - let a_high = (a >> 128).as_u128(); - - let norm = threshold.div_ceil(a_high); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::CalculateNormalizerBase128), - a.to_big_endian(), - DEFAULT_EXPECTED, - Some(U256::from(norm).to_big_endian()), - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_calculate_normalizer_base_128_norm_is_1( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let threshold = 1u128 << 127; - - // a >= 2^255 keeps a_high >= 2^127, so the divisor is already normalized - let a = generate_u256(U256::from(2).pow(U256::from(255)), U256::MAX); - let a_high = (a >> 128).as_u128(); - - let norm = threshold.div_ceil(a_high); - assert_eq!(norm, 1); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::CalculateNormalizerBase128), - a.to_big_endian(), - DEFAULT_EXPECTED, - Some(U256::from(norm).to_big_endian()), - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_calculate_normalizer_base_128_norm_greater_than_1( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let threshold = 1u128 << 127; - - // a < 2^255 keeps a_high < 2^127, so the divisor has to be scaled up - let a = generate_u256( - U256::from(u128::MAX) + 1, - U256::from(2).pow(U256::from(255)) - 1, - ); - let a_high = (a >> 128).as_u128(); - - let norm = threshold.div_ceil(a_high); - assert!(norm > 1); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::CalculateNormalizerBase128), - a.to_big_endian(), - DEFAULT_EXPECTED, - Some(U256::from(norm).to_big_endian()), - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_calculate_normalizer_base_128_a_is_u128_fail( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let threshold = 1u128 << 127; - - let a = generate_u256(U256::one(), U256::from(threshold) - 1); - - let norm: u128 = threshold.div_ceil(a.low_u128()); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::CalculateNormalizerBase128), - a.to_big_endian(), - DEFAULT_EXPECTED, - Some(U256::from(norm).to_big_endian()), - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn u256_test_calculate_normalizer_base_128_b_is_zero_fail( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = [0; 32]; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::CalculateNormalizerBase128), - a, - DEFAULT_EXPECTED, - Some(DEFAULT_EXPECTED), - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } - - #[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_algorithm_d_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::AlgorithmD256_128), - a.to_big_endian(), - b.to_big_endian(), - Some(q), - r, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn test_algorithm_d_256_128_fail_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::AlgorithmD256_128), - a.to_big_endian(), - b.to_big_endian(), - Some(q), - r, - ), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn test_algorithm_d_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::AlgorithmD256_128), - a.to_big_endian(), - b, - Some(DEFAULT_EXPECTED), - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn test_algorithm_d_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::AlgorithmD256_128), a, a, Some(q), r), - Expect::Ok, - ) - } - - #[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_div_by_zero(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([0; 32]), - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } -} diff --git a/tests/u256_test_sub_mul.rs b/tests/u256_test_sub_mul.rs deleted file mode 100644 index ccb3963..0000000 --- a/tests/u256_test_sub_mul.rs +++ /dev/null @@ -1,366 +0,0 @@ -mod common; - -use primitive_types::U256; -use primitive_types::U512; -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, - Mul512_128, - SafeMul256_128, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; -const DEFAULT_U128: u128 = 0; - -fn program() -> U256TestSubMulProgram { - U256TestSubMulProgram::new(&U256TestSubMulArguments {}) -} - -fn build_witness( - function: u8, - a: [u8; 32], - b: [u8; 32], - c: u128, - expected: Option<[u8; 32]>, - expected_bool: bool, - second_expected: [u8; 32], - third_expected: [u8; 32], -) -> U256TestSubMulWitness { - U256TestSubMulWitness { - function_index: function, - first_arg: a, - second_arg: b, - third_arg: c, - expected, - expected_bool, - second_expected, - third_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(), - DEFAULT_U128, - Some(result), - false, - DEFAULT_EXPECTED, - 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, - DEFAULT_U128, - Some([0; 32]), - false, - DEFAULT_EXPECTED, - 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(), - DEFAULT_U128, - Some(result.to_big_endian()), - carry, - DEFAULT_EXPECTED, - 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(), - DEFAULT_U128, - Some(result.to_big_endian()), - carry, - DEFAULT_EXPECTED, - 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(), - DEFAULT_U128, - Some(a), - false, - DEFAULT_EXPECTED, - 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(), - DEFAULT_U128, - Some(result.to_big_endian()), - true, - DEFAULT_EXPECTED, - 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(), - DEFAULT_U128, - Some(result_high), - DEFAULT_BOOL, - result_low, - DEFAULT_EXPECTED, - ), - 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(), - DEFAULT_U128, - Some(result_high), - DEFAULT_BOOL, - result_low, - DEFAULT_EXPECTED, - ), - 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(), - DEFAULT_U128, - Some(result_high), - DEFAULT_BOOL, - result_low, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_mul_512_128(context: simplex::TestContext) -> anyhow::Result<()> { - let a_1 = generate_u256(U256::one(), U256::MAX); - let a_0 = generate_u256(U256::one(), U256::MAX); - let b = rand::thread_rng().gen_range(1..=u128::MAX); - - let result_low = U512::from(a_0) * U512::from(b); - let result_high = U512::from(a_1) * U512::from(b); - - let (res_1, res_0) = split_u512(result_low.to_big_endian()); - let (res_3, res_2) = split_u512(result_high.to_big_endian()); - - let res_2_1 = U512::from_big_endian(&res_1) + U512::from_big_endian(&res_2); - let (res_3_1, res_2_1) = split_u512(res_2_1.to_big_endian()); - - let res_3_final = - (U256::from_big_endian(&res_3_1) + U256::from_big_endian(&res_3)).to_big_endian(); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Mul512_128), - a_1.to_big_endian(), - a_0.to_big_endian(), - b, - Some(res_3_final), - DEFAULT_BOOL, - res_2_1, - res_0, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_safe_mul_256_128_fitting(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::zero(), U256::from(u128::MAX)); - let b = generate_u256(U256::zero(), U256::from(u128::MAX)); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::SafeMul256_128), - a.to_big_endian(), - b.to_big_endian(), - DEFAULT_U128, - Some((a * b).to_big_endian()), - DEFAULT_BOOL, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u256_test_safe_mul_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); - let b = U256::from(u128::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::SafeMul256_128), - a.to_big_endian(), - b.to_big_endian(), - DEFAULT_U128, - Some(DEFAULT_EXPECTED), - DEFAULT_BOOL, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::AssertFailed, - ) - } -} diff --git a/tests/u32_convert_test.rs b/tests/u32_convert_test.rs deleted file mode 100644 index e7874f0..0000000 --- a/tests/u32_convert_test.rs +++ /dev/null @@ -1,225 +0,0 @@ -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, - SplitU32IntoU8, - SplitU32IntoU16, - 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_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u32::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::SplitU32IntoU8), - a, - U256::from(a).to_big_endian(), - ), - Expect::Ok, - ) - } - - #[simplex::test] - 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::SplitU32IntoU16), - 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_mul_div_test.rs b/tests/u32_mul_div_test.rs deleted file mode 100644 index 2487f08..0000000 --- a/tests/u32_mul_div_test.rs +++ /dev/null @@ -1,100 +0,0 @@ -mod common; - -use crate::common::core::{Expect, run}; -use rand::Rng; - -use simplicityhl_std::artifacts::u32_mul_div_test::U32MulDivTestProgram; -use simplicityhl_std::artifacts::u32_mul_div_test::derived_u32_mul_div_test::{ - U32MulDivTestArguments, U32MulDivTestWitness, -}; - -const DEFAULT_EXPECTED: u32 = 0; - -enum FunctionToTest { - MulDiv, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -fn program() -> U32MulDivTestProgram { - U32MulDivTestProgram::new(&U32MulDivTestArguments {}) -} - -fn build_witness(op: u8, a: u32, b: u32, c: u32, expected: Option) -> U32MulDivTestWitness { - U32MulDivTestWitness { - function_index: op, - first_arg: a, - second_arg: b, - third_arg: c, - expected, - } -} - -mod u32_mul_div_test { - use super::*; - - #[simplex::test] - fn u32_test_mul_div_32_product_is_u32(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u16::MAX) as u32; - let b = rand::thread_rng().gen_range(0..=u16::MAX) as u32; - let c = rand::thread_rng().gen_range(1..=u32::MAX); - - let res = a * b / c; - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), - Expect::Ok, - ) - } - - #[simplex::test] - fn u32_test_mul_div_32_intermediate_overflow( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(2..=u32::MAX); - let b = u32::MAX; - let c = rand::thread_rng().gen_range(a..=u32::MAX); - - let res = (a as u64) * (b as u64) / (c as u64); - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u32)), - Expect::Ok, - ) - } - - #[simplex::test] - fn u32_test_mul_div_32_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(2..=u32::MAX); - let b = u32::MAX; - let c = rand::thread_rng().gen_range(1..a); - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn u32_test_mul_div_32_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u32::MAX); - let b = rand::thread_rng().gen_range(1..=u32::MAX); - let c = 0; - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), - Expect::Ok, - ) - } -} diff --git a/tests/u64_convert_test.rs b/tests/u64_convert_test.rs deleted file mode 100644 index 050faaa..0000000 --- a/tests/u64_convert_test.rs +++ /dev/null @@ -1,260 +0,0 @@ -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_mul_div_test.rs b/tests/u64_mul_div_test.rs deleted file mode 100644 index bbd17d3..0000000 --- a/tests/u64_mul_div_test.rs +++ /dev/null @@ -1,100 +0,0 @@ -mod common; - -use crate::common::core::{Expect, run}; -use rand::Rng; - -use simplicityhl_std::artifacts::u64_mul_div_test::U64MulDivTestProgram; -use simplicityhl_std::artifacts::u64_mul_div_test::derived_u64_mul_div_test::{ - U64MulDivTestArguments, U64MulDivTestWitness, -}; - -const DEFAULT_EXPECTED: u64 = 0; - -enum FunctionToTest { - MulDiv, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -fn program() -> U64MulDivTestProgram { - U64MulDivTestProgram::new(&U64MulDivTestArguments {}) -} - -fn build_witness(op: u8, a: u64, b: u64, c: u64, expected: Option) -> U64MulDivTestWitness { - U64MulDivTestWitness { - function_index: op, - first_arg: a, - second_arg: b, - third_arg: c, - expected, - } -} - -mod u64_mul_div_test { - use super::*; - - #[simplex::test] - fn u64_test_mul_div_64_product_is_u64(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u32::MAX) as u64; - let b = rand::thread_rng().gen_range(0..=u32::MAX) as u64; - let c = rand::thread_rng().gen_range(1..=u64::MAX); - - let res = a * b / c; - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), - Expect::Ok, - ) - } - - #[simplex::test] - fn u64_test_mul_div_64_intermediate_overflow( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(2..=u64::MAX); - let b = u64::MAX; - let c = rand::thread_rng().gen_range(a..=u64::MAX); - - let res = (a as u128) * (b as u128) / (c as u128); - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u64)), - Expect::Ok, - ) - } - - #[simplex::test] - fn u64_test_mul_div_64_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(2..=u64::MAX); - let b = u64::MAX; - let c = rand::thread_rng().gen_range(1..a); - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn u64_test_mul_div_64_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u64::MAX); - let b = rand::thread_rng().gen_range(1..=u64::MAX); - let c = 0; - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), - Expect::Ok, - ) - } -} diff --git a/tests/u8_convert_test.rs b/tests/u8_convert_test.rs deleted file mode 100644 index c44ecd0..0000000 --- a/tests/u8_convert_test.rs +++ /dev/null @@ -1,170 +0,0 @@ -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, - SplitU8IntoU1, - 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_into_u1(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u8::MAX); - - run( - &context, - program(), - build_witness( - op(FunctionToTest::SplitU8IntoU1), - 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_mul_div_test.rs b/tests/u8_mul_div_test.rs deleted file mode 100644 index f18e46a..0000000 --- a/tests/u8_mul_div_test.rs +++ /dev/null @@ -1,100 +0,0 @@ -mod common; - -use crate::common::core::{Expect, run}; -use rand::Rng; - -use simplicityhl_std::artifacts::u8_mul_div_test::U8MulDivTestProgram; -use simplicityhl_std::artifacts::u8_mul_div_test::derived_u8_mul_div_test::{ - U8MulDivTestArguments, U8MulDivTestWitness, -}; - -const DEFAULT_EXPECTED: u8 = 0; - -enum FunctionToTest { - MulDiv, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 -} - -fn program() -> U8MulDivTestProgram { - U8MulDivTestProgram::new(&U8MulDivTestArguments {}) -} - -fn build_witness(op: u8, a: u8, b: u8, c: u8, expected: Option) -> U8MulDivTestWitness { - U8MulDivTestWitness { - function_index: op, - first_arg: a, - second_arg: b, - third_arg: c, - expected, - } -} - -mod u8_mul_div_test { - use super::*; - - #[simplex::test] - fn u8_test_mul_div_8_product_is_u8(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u8::MAX); - let b = u8::MAX / a; - let c = rand::thread_rng().gen_range(1..=u8::MAX); - - let res = a * b / c; - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), - Expect::Ok, - ) - } - - #[simplex::test] - fn u8_test_mul_div_8_intermediate_overflow( - context: simplex::TestContext, - ) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(2..=u8::MAX); - let b = u8::MAX; - let c = rand::thread_rng().gen_range(a..=u8::MAX); - - let res = (a as u16) * (b as u16) / (c as u16); - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u8)), - Expect::Ok, - ) - } - - #[simplex::test] - fn u8_test_mul_div_8_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(2..=u8::MAX); - let b = u8::MAX; - let c = rand::thread_rng().gen_range(1..a); - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), - Expect::AssertFailed, - ) - } - - #[simplex::test] - fn u8_test_mul_div_8_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u8::MAX); - let b = rand::thread_rng().gen_range(1..=u8::MAX); - let c = 0; - - run( - &context, - program(), - build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), - Expect::Ok, - ) - } -} From 6be1204ba6678b35efc529fd720f5e1d5cce2a7c Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Tue, 29 Sep 2026 13:05:19 +0300 Subject: [PATCH 03/11] Panic on division by zero (#57) * refactored div functions to panic if the divisor is zero; removed safe_div_128 and safe_div_256 because they duplicate `div` functionality * refactored mul_div functions to panic if the divisor is zero * updated docs * fixed test in common::uint; added -v workaround to ci; fixed typos * fixed comments for checked_div --- .github/workflows/tests.yml | 2 +- docs/stdlib.json | 50 ++++++---------- simf/lib/u128/math.simf | 67 ++++++++++----------- simf/lib/u128/mul_div.simf | 2 +- simf/lib/u16/math.simf | 2 +- simf/lib/u16/mul_div.simf | 5 +- simf/lib/u256/math.simf | 17 +++--- simf/lib/u256/mul_div.simf | 90 +++++++++++++--------------- simf/lib/u32/math.simf | 2 +- simf/lib/u32/mul_div.simf | 5 +- simf/lib/u64/math.simf | 2 +- simf/lib/u64/mul_div.simf | 2 +- simf/lib/u8/math.simf | 2 +- simf/lib/u8/mul_div.simf | 5 +- simf/tests/u128/math/api.simf | 4 +- simf/tests/u128/math/primitives.simf | 10 +--- simf/tests/u256/math/api.simf | 4 +- simf/tests/u256/math/div.simf | 10 +--- tests/stdlib/common/uint.rs | 17 +++++- tests/stdlib/u128/math/primitives.rs | 20 +++---- tests/stdlib/u128/mul_div.rs | 5 +- tests/stdlib/u16/mul_div.rs | 5 +- tests/stdlib/u256/math/div.rs | 26 +++----- tests/stdlib/u256/mul_div.rs | 2 +- tests/stdlib/u32/mul_div.rs | 5 +- tests/stdlib/u64/mul_div.rs | 5 +- tests/stdlib/u8/mul_div.rs | 5 +- 27 files changed, 175 insertions(+), 196 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index c4df5a4..f5ba2d8 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -61,4 +61,4 @@ jobs: - name: Test run: | - simplex test --test-threads 4 + simplex test --test-threads 4 -v diff --git a/docs/stdlib.json b/docs/stdlib.json index e51a52a..801701e 100644 --- a/docs/stdlib.json +++ b/docs/stdlib.json @@ -340,14 +340,14 @@ "section": "`u8` arithmetic", "input_type": "u8, u8", "output_type": "Option", - "description": "Subtract the second `u8` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u8`." + "description": "Subtract the second `u8` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u8`." }, { "simplicityhl_name": "safe_sub_8", "section": "`u8` arithmetic", "input_type": "u8, u8", "output_type": "u8", - "description": "Subtract the second `u8` value from the first.\n\n## Panics\nPanics if the result would underflow `u8`." + "description": "Subtract the second `u8` value from the first.\n\n## Panics\nPanics if the result would overflow `u8`." }, { "simplicityhl_name": "checked_mul_8", @@ -396,7 +396,7 @@ "section": "`u8` arithmetic", "input_type": "u8, u8, u8", "output_type": "u8", - "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u8." + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u8 or if the denominator is zero." }, { "simplicityhl_name": "u8_to_u16", @@ -466,14 +466,14 @@ "section": "`u16` arithmetic", "input_type": "u16, u16", "output_type": "Option", - "description": "Subtract the second `u16` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u16`." + "description": "Subtract the second `u16` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u16`." }, { "simplicityhl_name": "safe_sub_16", "section": "`u16` arithmetic", "input_type": "u16, u16", "output_type": "u16", - "description": "Subtract the second `u16` value from the first.\n\n## Panics\nPanics if the result would underflow `u16`." + "description": "Subtract the second `u16` value from the first.\n\n## Panics\nPanics if the result would overflow `u16`." }, { "simplicityhl_name": "checked_mul_16", @@ -522,7 +522,7 @@ "section": "`u16` arithmetic", "input_type": "u16, u16, u16", "output_type": "u16", - "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u16." + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u16 or if the denominator is zero." }, { "simplicityhl_name": "u16_to_u32", @@ -592,14 +592,14 @@ "section": "`u32` arithmetic", "input_type": "u32, u32", "output_type": "Option", - "description": "Subtract the second `u32` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u32`." + "description": "Subtract the second `u32` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u32`." }, { "simplicityhl_name": "safe_sub_32", "section": "`u32` arithmetic", "input_type": "u32, u32", "output_type": "u32", - "description": "Subtract the second `u32` value from the first.\n\n## Panics\nPanics if the result would underflow `u32`." + "description": "Subtract the second `u32` value from the first.\n\n## Panics\nPanics if the result would overflow `u32`." }, { "simplicityhl_name": "checked_mul_32", @@ -648,7 +648,7 @@ "section": "`u32` arithmetic", "input_type": "u32, u32, u32", "output_type": "u32", - "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u32." + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u32 or if the denominator is zero." }, { "simplicityhl_name": "u32_to_u64", @@ -725,14 +725,14 @@ "section": "`u64` arithmetic", "input_type": "u64, u64", "output_type": "Option", - "description": "Subtract the second `u64` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u64`." + "description": "Subtract the second `u64` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u64`." }, { "simplicityhl_name": "safe_sub_64", "section": "`u64` arithmetic", "input_type": "u64, u64", "output_type": "u64", - "description": "Subtract the second `u64` value from the first.\n\n## Panics\nPanics if the result would underflow `u64`." + "description": "Subtract the second `u64` value from the first.\n\n## Panics\nPanics if the result would overflow `u64`." }, { "simplicityhl_name": "checked_mul_64", @@ -781,7 +781,7 @@ "section": "`u64` arithmetic", "input_type": "u64, u64, u64", "output_type": "u64", - "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u64." + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u64 or if the denominator is zero." }, { "simplicityhl_name": "u64_to_u128", @@ -886,14 +886,14 @@ "section": "`u128` arithmetic", "input_type": "u128, u128", "output_type": "Option", - "description": "Subtract the second `u128` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u128`." + "description": "Subtract the second `u128` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u128`." }, { "simplicityhl_name": "safe_sub_128", "section": "`u128` arithmetic", "input_type": "u128, u128", "output_type": "u128", - "description": "Subtract the second `u128` value from the first.\n\n## Panics\nPanics if the result would underflow `u128`." + "description": "Subtract the second `u128` value from the first.\n\n## Panics\nPanics if the result would overflow `u128`." }, { "simplicityhl_name": "mul_128", @@ -944,13 +944,6 @@ "output_type": "Option", "description": "Divide the first `u128` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." }, - { - "simplicityhl_name": "safe_div_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "u128", - "description": "Divide the first `u128` value by the second.\n\n## Panics\nPanics if the divisor is zero." - }, { "simplicityhl_name": "full_add_128", "section": "`u128` arithmetic", @@ -991,7 +984,7 @@ "section": "`u128` arithmetic", "input_type": "u128, u128, u128", "output_type": "u128", - "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u128." + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u128 or if the denominator is zero." }, { "simplicityhl_name": "and_128", @@ -1180,14 +1173,14 @@ "section": "`u256` arithmetic", "input_type": "u256, u256", "output_type": "Option", - "description": "Subtract the second `u256` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u256`." + "description": "Subtract the second `u256` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u256`." }, { "simplicityhl_name": "safe_sub_256", "section": "`u256` arithmetic", "input_type": "u256, u256", "output_type": "u256", - "description": "Subtract the second `u256` value from the first.\n\n## Panics\nPanics if the result would underflow `u256`." + "description": "Subtract the second `u256` value from the first.\n\n## Panics\nPanics if the result would overflow `u256`." }, { "simplicityhl_name": "mul_256", @@ -1287,19 +1280,12 @@ "output_type": "Option", "description": "Divide the first `u256` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." }, - { - "simplicityhl_name": "safe_div_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "u256", - "description": "Divide the first `u256` value by the second.\n\n## Panics\nPanics if the divisor is zero." - }, { "simplicityhl_name": "mul_div_256", "section": "`u256` arithmetic", "input_type": "u256, u256, u256", "output_type": "u256", - "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u256." + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u256 or if the denominator is zero." }, { "simplicityhl_name": "and_256", diff --git a/simf/lib/u128/math.simf b/simf/lib/u128/math.simf index 313eff7..a842565 100644 --- a/simf/lib/u128/math.simf +++ b/simf/lib/u128/math.simf @@ -301,7 +301,7 @@ fn algorithm_d_128_128(dividend: u128, divisor: u128) -> (u64, u128) { /// Divides the 128-bit integer by the 64-bit integer, /// returns a tuple of the u128 quotient and the u64 remainder. -/// Panics if divisor is equal to zero +/// Panics if the divisor is zero pub fn div_mod_128_64(a: u128, b: u64) -> (u128, u64) { let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); @@ -331,65 +331,60 @@ pub fn div_mod_128_64(a: u128, b: u64) -> (u128, u64) { /// Divides the first integer by the second integer, /// returns the quotient and the remainder +/// Panics if the divisor is zero pub fn div_mod_128(a: u128, b: u128) -> (u128, u128) { - match is_zero_128(b) { + assert!(not(is_zero_128(b))); + + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); + + match lt_128(a, b) { true => (0, a), false => { - let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); - let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); - - match lt_128(a, b) { - true => (0, a), + match and(jet::is_zero_64(a_high), jet::is_zero_64(b_high)) { + true => { + // if both a_high and b_high are zero, this narrows down to 64-bit division + let (q, r): (u64, u64) = jet::div_mod_64(a_low, b_low); + (u64_to_u128(q), u64_to_u128(r)) + }, false => { - match and(jet::is_zero_64(a_high), jet::is_zero_64(b_high)) { + match jet::eq_64(a_high, b_high) { true => { - // if both a_high and b_high are zero, this narrows down to 64-bit division - let (q, r): (u64, u64) = jet::div_mod_64(a_low, b_low); - (u64_to_u128(q), u64_to_u128(r)) + // safe: !lt_128(a, b) and a_high == b_high, so a_low >= b_low, + // and the subtraction can not overflow + let (_, diff): (bool, u64) = jet::subtract_64(a_low, b_low); + (1, u64_to_u128(diff)) }, - false => { - match jet::eq_64(a_high, b_high) { - true => { - // safe: !lt_128(a, b) and a_high == b_high, so a_low >= b_low, - // and the subtraction can not underflow - let (_, diff): (bool, u64) = jet::subtract_64(a_low, b_low); - (1, u64_to_u128(diff)) - }, - false => match jet::is_zero_64(b_high) { - true => { - let (q, r): (u128, u64) = div_mod_128_64(a, b_low); - (q, u64_to_u128(r)) - }, - false => { - let (q, r): (u64, u128) = algorithm_d_128_128(a, b); - (u64_to_u128(q), r) - }, - }, - } + false => match jet::is_zero_64(b_high) { + true => { + let (q, r): (u128, u64) = div_mod_128_64(a, b_low); + (q, u64_to_u128(r)) + }, + false => { + let (q, r): (u64, u128) = algorithm_d_128_128(a, b); + (u64_to_u128(q), r) + }, }, } }, } }, } + } /// Divide the first integer by the second integer, returns the quotient +/// Panics if the divisor is zero pub fn div_128(a: u128, b: u128) -> u128 { let (q, _): (u128, u128) = div_mod_128(a, b); q } -/// Returns the quotient of two u128 values wrapped in Some, or None if the result overflows u128 +/// Returns the quotient of two u128 values wrapped in Some, or None if b is zero pub fn checked_div_128(a: u128, b: u128) -> Option { match is_zero_128(b) { true => None, false => Some(div_128(a, b)), } } - -/// Returns the quotient of two u128 values, panics if the result overflows u128 -pub fn safe_div_128(a: u128, b: u128) -> u128 { - unwrap(checked_div_128(a, b)) -} diff --git a/simf/lib/u128/mul_div.simf b/simf/lib/u128/mul_div.simf index 6b4c0e2..2b7bf3c 100644 --- a/simf/lib/u128/mul_div.simf +++ b/simf/lib/u128/mul_div.simf @@ -5,7 +5,7 @@ use crate::lib::u256::math::div_256; use crate::lib::u256::convert::safe_u256_to_u128; /// Calculates `floor(a * b / denominator)` with full precision. -/// Panics if the result overflows a u128 +/// Panics if the result overflows a u128 or if the denominator is zero pub fn mul_div_128(a: u128, b: u128, denominator: u128) -> u128 { let denominator_256: u256 = u128_to_u256(denominator); diff --git a/simf/lib/u16/math.simf b/simf/lib/u16/math.simf index b87d435..cf3c1b6 100644 --- a/simf/lib/u16/math.simf +++ b/simf/lib/u16/math.simf @@ -45,7 +45,7 @@ pub fn safe_mul_16(a: u16, b: u16) -> u16 { unwrap(checked_mul_16(a, b)) } -/// Returns the quotient of two u16 values wrapped in Some, or None if the result overflows u16 +/// Returns the quotient of two u16 values wrapped in Some, or None if b is zero pub fn checked_div_16(a: u16, b: u16) -> Option { match jet::is_zero_16(b) { true => None, diff --git a/simf/lib/u16/mul_div.simf b/simf/lib/u16/mul_div.simf index 92e7b04..0592901 100644 --- a/simf/lib/u16/mul_div.simf +++ b/simf/lib/u16/mul_div.simf @@ -1,9 +1,12 @@ +use crate::lib::binary::not; use crate::lib::u16::convert::u16_to_u32; use crate::lib::u32::convert::safe_u32_to_u16; /// Calculates `floor(a * b / denominator)` with full precision. -/// Panics if the result overflows a u16 +/// Panics if the result overflows a u16 or if the denominator is zero pub fn mul_div_16(a: u16, b: u16, denominator: u16) -> u16 { + assert!(not(jet::is_zero_16(denominator))); + let denominator_32: u32 = u16_to_u32(denominator); let result_32: u32 = jet::divide_32(jet::multiply_16(a, b), denominator_32); diff --git a/simf/lib/u256/math.simf b/simf/lib/u256/math.simf index ed193fb..8654bf9 100644 --- a/simf/lib/u256/math.simf +++ b/simf/lib/u256/math.simf @@ -296,6 +296,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 +/// Panics if the divisor is zero pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { let (u3, u2, u1, u0): (u64, u64, u64, u64) = split_u256_into_u64(dividend); @@ -374,6 +375,7 @@ pub fn algorithm_d_256_128(dividend: u256, divisor: u128) -> (u256, u128) { /// Divides the 256-bit integer by the 128-bit integer, /// returns a tuple of the u256 quotient and the u128 remainder +/// Panics if the divisor is zero pub fn div_mod_256_128(dividend: u256, divisor: u128) -> (u256, u128) { let (divisor_high, divisor_low): (u64, u64) = split_u128_into_u64(divisor); @@ -441,11 +443,14 @@ fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { /// Divides the first integer by the second integer, /// returns the quotient and the remainder +/// Panics if the divisor is zero pub fn div_mod_256(a: u256, b: u256) -> (u256, u256) { + assert!(not(is_zero_256(b))); + let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); let (b_high, b_low): (u128, u128) = split_u256_into_u128(b); - match or(is_zero_256(b), lt_256(a, b)) { + match lt_256(a, b) { true => (0, a), false => { match and(is_zero_128(a_high), is_zero_128(b_high)) { @@ -458,7 +463,7 @@ pub fn div_mod_256(a: u256, b: u256) -> (u256, u256) { 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 + // and the subtraction can not overflow let (_, diff): (bool, u128) = sub_128(a_low, b_low); (1, u128_to_u256(diff)) }, @@ -480,21 +485,17 @@ pub fn div_mod_256(a: u256, b: u256) -> (u256, u256) { } /// Divide the first integer by the second integer, returns the quotient +/// Panics if the divisor is zero 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 +/// Returns the quotient of two u256 values wrapped in Some, or None if b is zero 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/lib/u256/mul_div.simf b/simf/lib/u256/mul_div.simf index f80fd14..9aa765b 100644 --- a/simf/lib/u256/mul_div.simf +++ b/simf/lib/u256/mul_div.simf @@ -128,7 +128,7 @@ fn mul_and_sub_128(q: u128, u2: u128, u1: u128, u0: u128, v: u256) -> (u128, u12 /// Divides a 512-bit integer by a 256-bit integer and returns a `u256` quotient. /// Implements Algorithm D by Donald Knuth. /// Requires the upper half of the divisor to be non-zero (`divisor >= 2^128`) -/// and the quotient to fit into `u256`. +/// and the quotient to fit into `u256` fn algorithm_d_512_256(dividend_high: u256, dividend_low: u256, divisor: u256) -> u256 { let ( _, @@ -152,56 +152,50 @@ fn algorithm_d_512_256(dividend_high: u256, dividend_low: u256, divisor: u256) - } /// Calculates `floor(a * b / denominator)` with full precision. -/// Panics if the result overflows a u256 +/// Panics if the result overflows a u256 or if the denominator is zero pub fn mul_div_256(a: u256, b: u256, denominator: u256) -> u256 { - match is_zero_256(denominator) { - true => 0, + assert!(not(is_zero_256(denominator))); + + let (result_high, result_low): (u256, u256) = mul_256(a, b); + + match is_zero_256(result_high) { + true => div_256(result_low, denominator), false => { - let (result_high, result_low): (u256, u256) = mul_256(a, b); - - match is_zero_256(result_high) { - true => div_256(result_low, denominator), - false => { - // if result_high >= denominator, result will overflow u256 - assert!(lt_256(result_high, denominator)); - - let (denom_high, denom_low): (u128, u128) = split_u256_into_u128(denominator); - - match is_zero_128(denom_high) { - true => { - // result_high < denominator, denominator fits into u128, so result_high fits too. - // split_u256_into_u128 is used instead of safe_u256_to_u128 to avoid the unnecessary check - let (_, result_2): (u128, u128) = split_u256_into_u128(result_high); - let ( - result_1, - result_0 - ): (u128, u128) = split_u256_into_u128(result_low); - - // result_high < denominator, so result_2 < denom_low and q1 fits into u128 - let ( - q1, - r - ): (u256, u128) = div_mod_256_128( - <(u128, u128)>::into((result_2, result_1)), - denom_low - ); - let (_, q1): (u128, u128) = split_u256_into_u128(q1); - - // r < denom_low, so q0 fits into u128 - let ( - q0, - _ - ): (u256, u128) = div_mod_256_128( - <(u128, u128)>::into((r, result_0)), - denom_low - ); - let (_, q0): (u128, u128) = split_u256_into_u128(q0); - - <(u128, u128)>::into((q1, q0)) - }, - false => algorithm_d_512_256(result_high, result_low, denominator), - } + // if result_high >= denominator, result will overflow u256 + assert!(lt_256(result_high, denominator)); + + let (denom_high, denom_low): (u128, u128) = split_u256_into_u128(denominator); + + match is_zero_128(denom_high) { + true => { + // result_high < denominator, denominator fits into u128, so result_high fits too. + // split_u256_into_u128 is used instead of safe_u256_to_u128 to avoid the unnecessary check + let (_, result_2): (u128, u128) = split_u256_into_u128(result_high); + let (result_1, result_0): (u128, u128) = split_u256_into_u128(result_low); + + // result_high < denominator, so result_2 < denom_low and q1 fits into u128 + let ( + q1, + r + ): (u256, u128) = div_mod_256_128( + <(u128, u128)>::into((result_2, result_1)), + denom_low + ); + let (_, q1): (u128, u128) = split_u256_into_u128(q1); + + // r < denom_low, so q0 fits into u128 + let ( + q0, + _ + ): (u256, u128) = div_mod_256_128( + <(u128, u128)>::into((r, result_0)), + denom_low + ); + let (_, q0): (u128, u128) = split_u256_into_u128(q0); + + <(u128, u128)>::into((q1, q0)) }, + false => algorithm_d_512_256(result_high, result_low, denominator), } }, } diff --git a/simf/lib/u32/math.simf b/simf/lib/u32/math.simf index e85f5e4..cfce540 100644 --- a/simf/lib/u32/math.simf +++ b/simf/lib/u32/math.simf @@ -45,7 +45,7 @@ pub fn safe_mul_32(a: u32, b: u32) -> u32 { unwrap(checked_mul_32(a, b)) } -/// Returns the quotient of two u32 values wrapped in Some, or None if the result overflows u32 +/// Returns the quotient of two u32 values wrapped in Some, or None if b is zero pub fn checked_div_32(a: u32, b: u32) -> Option { match jet::is_zero_32(b) { true => None, diff --git a/simf/lib/u32/mul_div.simf b/simf/lib/u32/mul_div.simf index 8e4bc43..5cd972c 100644 --- a/simf/lib/u32/mul_div.simf +++ b/simf/lib/u32/mul_div.simf @@ -1,9 +1,12 @@ +use crate::lib::binary::not; use crate::lib::u32::convert::u32_to_u64; use crate::lib::u64::convert::safe_u64_to_u32; /// Calculates `floor(a * b / denominator)` with full precision. -/// Panics if the result overflows a u32 +/// Panics if the result overflows a u32 or if the denominator is zero pub fn mul_div_32(a: u32, b: u32, denominator: u32) -> u32 { + assert!(not(jet::is_zero_32(denominator))); + let denominator_64: u64 = u32_to_u64(denominator); let result_64: u64 = jet::divide_64(jet::multiply_32(a, b), denominator_64); diff --git a/simf/lib/u64/math.simf b/simf/lib/u64/math.simf index 7617725..eebddc8 100644 --- a/simf/lib/u64/math.simf +++ b/simf/lib/u64/math.simf @@ -45,7 +45,7 @@ pub fn safe_mul_64(a: u64, b: u64) -> u64 { unwrap(checked_mul_64(a, b)) } -/// Returns the quotient of two u64 values wrapped in Some, or None if the result overflows u64 +/// Returns the quotient of two u64 values wrapped in Some, or None if b is zero pub fn checked_div_64(a: u64, b: u64) -> Option { match jet::is_zero_64(b) { true => None, diff --git a/simf/lib/u64/mul_div.simf b/simf/lib/u64/mul_div.simf index 367630a..ed061c5 100644 --- a/simf/lib/u64/mul_div.simf +++ b/simf/lib/u64/mul_div.simf @@ -3,7 +3,7 @@ use crate::lib::u128::convert::safe_u128_to_u64; use crate::lib::u128::math::div_128; /// Calculates `floor(a * b / denominator)` with full precision. -/// Panics if the result overflows a u64 +/// Panics if the result overflows a u64 or if the denominator is zero pub fn mul_div_64(a: u64, b: u64, denominator: u64) -> u64 { let denominator_128: u128 = u64_to_u128(denominator); diff --git a/simf/lib/u8/math.simf b/simf/lib/u8/math.simf index 9d50464..e2e9e88 100644 --- a/simf/lib/u8/math.simf +++ b/simf/lib/u8/math.simf @@ -45,7 +45,7 @@ pub fn safe_mul_8(a: u8, b: u8) -> u8 { unwrap(checked_mul_8(a, b)) } -/// Returns the quotient of two u8 values wrapped in Some, or None if the result overflows u8 +/// Returns the quotient of two u8 values wrapped in Some, or None if b is zero pub fn checked_div_8(a: u8, b: u8) -> Option { match jet::is_zero_8(b) { true => None, diff --git a/simf/lib/u8/mul_div.simf b/simf/lib/u8/mul_div.simf index 222f53b..c25dc35 100644 --- a/simf/lib/u8/mul_div.simf +++ b/simf/lib/u8/mul_div.simf @@ -1,9 +1,12 @@ +use crate::lib::binary::not; use crate::lib::u8::convert::u8_to_u16; use crate::lib::u16::convert::safe_u16_to_u8; /// Calculates `floor(a * b / denominator)` with full precision. -/// Panics if the result overflows a u8 +/// Panics if the result overflows a u8 or if the denominator is zero pub fn mul_div_8(a: u8, b: u8, denominator: u8) -> u8 { + assert!(not(jet::is_zero_8(denominator))); + let denominator_16: u16 = u8_to_u16(denominator); let result_16: u16 = jet::divide_16(jet::multiply_8(a, b), denominator_16); diff --git a/simf/tests/u128/math/api.simf b/simf/tests/u128/math/api.simf index 0f028f3..4769750 100644 --- a/simf/tests/u128/math/api.simf +++ b/simf/tests/u128/math/api.simf @@ -6,7 +6,7 @@ use crate::lib::u128::math::{ checked_mul_128, safe_mul_128, checked_div_128, - safe_div_128 + div_128 }; use crate::lib::u128::comparison::{gt_128, ge_128}; use crate::lib::asserts::assert_eq_128; @@ -74,7 +74,7 @@ fn main() { }; match is_selected(7, fn_idx) { true => { - assert_eq_128(safe_div_128(a, b), unwrap(expected)); + assert_eq_128(div_128(a, b), unwrap(expected)); }, false => (), }; diff --git a/simf/tests/u128/math/primitives.simf b/simf/tests/u128/math/primitives.simf index d226fb5..c8a6aa0 100644 --- a/simf/tests/u128/math/primitives.simf +++ b/simf/tests/u128/math/primitives.simf @@ -9,8 +9,7 @@ use crate::lib::u128::math::{ calculate_normalizer_base_64, estimate_quotient_digit_base_64, div_mod_128_64, - div_mod_128, - div_128 + div_mod_128 }; use crate::lib::u128::bit::eq_128; @@ -143,11 +142,4 @@ fn main() { }, false => (), }; - - match is_selected(11, fn_idx) { - true => { - assert!(eq_128(div_128(a, b), unwrap(expected))); - }, - false => (), - }; } diff --git a/simf/tests/u256/math/api.simf b/simf/tests/u256/math/api.simf index 4558aa9..8527fe0 100644 --- a/simf/tests/u256/math/api.simf +++ b/simf/tests/u256/math/api.simf @@ -6,7 +6,7 @@ use crate::lib::u256::math::{ checked_mul_256, safe_mul_256, checked_div_256, - safe_div_256 + div_256 }; use crate::lib::u256::comparison::{gt_256, ge_256}; use crate::lib::asserts::assert_eq_256; @@ -74,7 +74,7 @@ fn main() { }; match is_selected(7, fn_idx) { true => { - assert_eq_256(safe_div_256(a, b), unwrap(expected)); + assert_eq_256(div_256(a, b), unwrap(expected)); }, false => (), }; diff --git a/simf/tests/u256/math/div.simf b/simf/tests/u256/math/div.simf index 333dc42..2ae56b8 100644 --- a/simf/tests/u256/math/div.simf +++ b/simf/tests/u256/math/div.simf @@ -3,8 +3,7 @@ use crate::lib::u256::math::{ div_mod_256_64, algorithm_d_256_128, div_mod_256_128, - div_mod_256, - div_256 + div_mod_256 }; use crate::lib::u256::convert::split_u256_into_u64; use crate::lib::asserts::{assert_eq_128, assert_eq_256}; @@ -81,11 +80,4 @@ fn main() { }, false => (), }; - - match is_selected(5, fn_idx) { - true => { - assert_eq_256(div_256(a, b), unwrap(expected)); - }, - false => (), - }; } diff --git a/tests/stdlib/common/uint.rs b/tests/stdlib/common/uint.rs index eeb5ff1..f271a4b 100644 --- a/tests/stdlib/common/uint.rs +++ b/tests/stdlib/common/uint.rs @@ -2,6 +2,7 @@ // part of it, so per-crate dead-code analysis would warn about the rest. #![allow(dead_code)] +use std::any::TypeId; use std::ops::{Add, Div, Mul, Sub}; use rand::Rng; @@ -9,6 +10,8 @@ use rand::distributions::uniform::SampleUniform; use simplex::program::{Program, WitnessTrait}; +use crate::common::u256_wrapper::U256Wrapper; + use super::core::{Expect, run}; /// Dispatch indices for the operations that exist for every unsigned width. @@ -244,14 +247,24 @@ pub fn safe_div_fitting(context: simplex::TestContext) -> anyhow::R ) } -pub fn safe_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { +pub fn safe_div_by_zero( + context: simplex::TestContext, +) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(T::ZERO..=T::MAX); + let expect = if TypeId::of::() == TypeId::of::() + || TypeId::of::() == TypeId::of::() + { + Expect::AssertFailed + } else { + Expect::PrunedBranch + }; + run( &context, T::program(), T::witness(op(CommonOp::SafeDiv), a, T::ZERO, None), - Expect::PrunedBranch, + expect, ) } diff --git a/tests/stdlib/u128/math/primitives.rs b/tests/stdlib/u128/math/primitives.rs index 367e41f..f415f31 100644 --- a/tests/stdlib/u128/math/primitives.rs +++ b/tests/stdlib/u128/math/primitives.rs @@ -23,8 +23,8 @@ enum FunctionToTest { EstimateQuotientDigitBase64, DivMod128_64, DivMod128, - Div128, } +// div_128 is already tested through safe_div_fitting and safe_div_by_zero in api.rs fn program() -> U128BasicMathTestProgram { U128BasicMathTestProgram::new(&U128BasicMathTestArguments {}) @@ -511,7 +511,7 @@ fn div_mod_128_64(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn div_mod_128_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn div_mod_128_64_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = 0; @@ -628,18 +628,12 @@ fn div_mod_128_eq_high_words_a_less_than_b(context: simplex::TestContext) -> any } #[simplex::test] -fn div_128(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); - let b = rand::thread_rng().gen_range(1..=u128::MAX); - let result = a / b; - - case(Div128).args(a, b).expect(result).run(&context) -} - -#[simplex::test] -fn div_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { +fn div_mod_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = 0; - case(Div128).args(a, b).expect(0).run(&context) + case(DivMod128) + .args(a, b) + .expect(0) + .expecting(&context, Expect::AssertFailed) } diff --git a/tests/stdlib/u128/mul_div.rs b/tests/stdlib/u128/mul_div.rs index 90f4315..19ed754 100644 --- a/tests/stdlib/u128/mul_div.rs +++ b/tests/stdlib/u128/mul_div.rs @@ -105,5 +105,8 @@ fn mul_div_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> let b = rand::thread_rng().gen_range(1..=u128::MAX); let c = 0; - case(MulDiv).args(a, b, c).expect(0).run(&context) + case(MulDiv) + .args(a, b, c) + .expect(0) + .expecting(&context, Expect::AssertFailed) } diff --git a/tests/stdlib/u16/mul_div.rs b/tests/stdlib/u16/mul_div.rs index 7fd260a..dbdbcab 100644 --- a/tests/stdlib/u16/mul_div.rs +++ b/tests/stdlib/u16/mul_div.rs @@ -100,5 +100,8 @@ fn mul_div_16_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let b = rand::thread_rng().gen_range(1..=u16::MAX); let c = 0; - case(MulDiv).args(a, b, c).expect(0).run(&context) + case(MulDiv) + .args(a, b, c) + .expect(0) + .expecting(&context, Expect::AssertFailed) } diff --git a/tests/stdlib/u256/math/div.rs b/tests/stdlib/u256/math/div.rs index 8fb84db..a35f63f 100644 --- a/tests/stdlib/u256/math/div.rs +++ b/tests/stdlib/u256/math/div.rs @@ -16,8 +16,8 @@ enum FunctionToTest { AlgorithmD256_128, DivMod256_128, DivMod256, - Div256, } +// div_256 is already tested through safe_div_fitting and safe_div_by_zero in api.rs fn program() -> U256TestDivProgram { U256TestDivProgram::new(&U256TestDivArguments {}) @@ -176,7 +176,7 @@ fn div_mod_256_64(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn div_mod_256_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn div_mod_256_64_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); let b = [0; 32]; @@ -217,7 +217,7 @@ fn algorithm_d_256_128_fail_b_fits_into_u64(context: simplex::TestContext) -> an } #[simplex::test] -fn algorithm_d_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn algorithm_d_256_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); let b = [0; 32]; @@ -272,7 +272,7 @@ fn div_mod_256_128_b_fits_into_u64(context: simplex::TestContext) -> anyhow::Res } #[simplex::test] -fn div_mod_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn div_mod_256_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); let b = [0; 32]; @@ -430,24 +430,12 @@ fn div_mod_256_edge_case(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn 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(); - - case(Div256) - .args(a.to_big_endian(), b.to_big_endian()) - .expect(result) - .run(&context) -} - -#[simplex::test] -fn div_256_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { +fn div_mod_256_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); let b = [0; 32]; - case(Div256) + case(DivMod256) .args(a.to_big_endian(), b) .expect([0; 32]) - .run(&context) + .expecting(&context, Expect::AssertFailed) } diff --git a/tests/stdlib/u256/mul_div.rs b/tests/stdlib/u256/mul_div.rs index afd5123..3727bbf 100644 --- a/tests/stdlib/u256/mul_div.rs +++ b/tests/stdlib/u256/mul_div.rs @@ -164,7 +164,7 @@ fn mul_div_256_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> case(MulDiv) .args(a.to_big_endian(), b.to_big_endian(), c.to_big_endian()) .expect([0; 32]) - .run(&context) + .expecting(&context, Expect::AssertFailed) } #[simplex::test] diff --git a/tests/stdlib/u32/mul_div.rs b/tests/stdlib/u32/mul_div.rs index 1a45dca..ccec474 100644 --- a/tests/stdlib/u32/mul_div.rs +++ b/tests/stdlib/u32/mul_div.rs @@ -103,5 +103,8 @@ fn mul_div_32_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let b = rand::thread_rng().gen_range(1..=u32::MAX); let c = 0; - case(MulDiv).args(a, b, c).expect(0).run(&context) + case(MulDiv) + .args(a, b, c) + .expect(0) + .expecting(&context, Expect::AssertFailed) } diff --git a/tests/stdlib/u64/mul_div.rs b/tests/stdlib/u64/mul_div.rs index d460e28..918661b 100644 --- a/tests/stdlib/u64/mul_div.rs +++ b/tests/stdlib/u64/mul_div.rs @@ -100,5 +100,8 @@ fn mul_div_64_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let b = rand::thread_rng().gen_range(1..=u64::MAX); let c = 0; - case(MulDiv).args(a, b, c).expect(0).run(&context) + case(MulDiv) + .args(a, b, c) + .expect(0) + .expecting(&context, Expect::AssertFailed) } diff --git a/tests/stdlib/u8/mul_div.rs b/tests/stdlib/u8/mul_div.rs index eddc18b..c01383b 100644 --- a/tests/stdlib/u8/mul_div.rs +++ b/tests/stdlib/u8/mul_div.rs @@ -100,5 +100,8 @@ fn mul_div_8_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let b = rand::thread_rng().gen_range(1..=u8::MAX); let c = 0; - case(MulDiv).args(a, b, c).expect(0).run(&context) + case(MulDiv) + .args(a, b, c) + .expect(0) + .expecting(&context, Expect::AssertFailed) } From ed5e45962a9074a797f821f81b571b19d7db0a99 Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Tue, 29 Sep 2026 13:05:55 +0300 Subject: [PATCH 04/11] bumped simplex version to 0.0.12 (#60) --- Cargo.lock | 133 +++++++++++++++++++++++++---------------------------- Cargo.toml | 2 +- 2 files changed, 63 insertions(+), 72 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7eb322e..86877ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -187,9 +187,9 @@ dependencies = [ [[package]] name = "bitcoin-consensus-encoding" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" +checksum = "9daa31138eb443d5751b207f3f64154e2bb09cd59960562ccc7a7112be38147f" dependencies = [ "bitcoin-internals", "hex-conservative 1.3.0", @@ -198,9 +198,9 @@ dependencies = [ [[package]] name = "bitcoin-internals" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d573f4cf32996a8dce612e4348cece65a241f1882ed594047c9ba348e8869fa5" +checksum = "e8bea3a9f0cfece4564e37cb49a38cc245ca5184719e50d7d0dda3268722c4e2" [[package]] name = "bitcoin-io" @@ -338,9 +338,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.4.5" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80" +checksum = "f360145194ee8e21db5ee7f3fcd4fe52210864c75c985dae33218202c8bbe040" dependencies = [ "find-msvc-tools", "jobserver", @@ -350,9 +350,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" [[package]] name = "chumsky" @@ -370,18 +370,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" +checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.6.6" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" +checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d" dependencies = [ "anstream", "anstyle", @@ -391,9 +391,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" +checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" [[package]] name = "cmake" @@ -587,9 +587,9 @@ checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "find-msvc-tools" -version = "0.1.12" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" +checksum = "aedcfb3409746eddb02b9e19ebda1c3394f759a152e48ee875a0844d1b955484" [[package]] name = "fixed-hash" @@ -859,9 +859,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.105" +version = "0.3.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" +checksum = "7883d941dae510fb2d978fc3fe018c71c9e2892fd38854de3e8b92c2e5ad9cc5" dependencies = [ "cfg-if", "futures-util", @@ -1260,9 +1260,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d" dependencies = [ "bitflags 2.13.2", "errno", @@ -1273,9 +1273,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.44" +version = "0.23.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6725596c3f2c3a0aef021139e145d4eafe314a6623e4680ca83852b2c67ab2ba" +checksum = "0d41d731c7d2f962d1ccc364cec258de3c0e93b38c2fb3ba97ac74513048d634" dependencies = [ "aws-lc-rs", "log", @@ -1399,7 +1399,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] @@ -1509,9 +1509,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smplx-build" -version = "0.0.11" +version = "0.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef1538c2de418c94bfec9824b140a0f8bc2f2542cde0f18ef1b2e3a23e5c9eb1" +checksum = "a4e9561b9da1d788d3e777f8a59b085dd13a6fbace5b109f2b0576069f9f36ff" dependencies = [ "glob", "globwalk", @@ -1530,9 +1530,9 @@ dependencies = [ [[package]] name = "smplx-macros" -version = "0.0.11" +version = "0.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb08baf344083ccad07886156cbd92feb2d66b1886b5b73e61a9e03db2ef8f7" +checksum = "9f802ff11501371c4c822f0ecdbf7e9e65180ccbbb43b55f252c9a00cb849d24" dependencies = [ "smplx-build", "smplx-test", @@ -1541,9 +1541,9 @@ dependencies = [ [[package]] name = "smplx-regtest" -version = "0.0.11" +version = "0.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11d419c9a387fcd2944ac487dff8ced762fc1d52e21186366ab70e7c899931a6" +checksum = "881db46f30a472018c26c5f245637a722ccba36a873b845d5e6376e7bd5e1106" dependencies = [ "electrsd", "hex", @@ -1557,9 +1557,9 @@ dependencies = [ [[package]] name = "smplx-sdk" -version = "0.0.11" +version = "0.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ac052541ca5611bcdd23d46317970b2065c73ebc7864e53ff5607b30c96dfa9" +checksum = "98b85112288eccd99ba34bd852681d78c3581a52709eab0ce52a073b9d961d8e" dependencies = [ "bip39", "bitcoin_hashes", @@ -1577,9 +1577,9 @@ dependencies = [ [[package]] name = "smplx-std" -version = "0.0.11" +version = "0.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6a9e1bdf1d8139fbdf2be662fdcd11761ae61604aeb78628ced8506f7c6a7b9" +checksum = "e05855ac57a1b5f737a4bc244ebf5924453957a2066a0bb0fd8df033498386ba" dependencies = [ "either", "serde", @@ -1591,9 +1591,9 @@ dependencies = [ [[package]] name = "smplx-test" -version = "0.0.11" +version = "0.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3bd10a1047d731372fc1589ebf2a76444ad8c65d352f84115718e6345f3aa0c" +checksum = "0a66ea20c2701a90faf10f9a18176058dd7a82222797d5ed6da9c83f0f1ca032" dependencies = [ "electrsd", "proc-macro2", @@ -1651,9 +1651,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" dependencies = [ "proc-macro2", "quote", @@ -1675,44 +1675,35 @@ dependencies = [ "fastrand", "getrandom 0.4.3", "once_cell", - "rustix 1.1.4", + "rustix 1.1.5", "windows-sys 0.61.2", ] [[package]] name = "thiserror" -version = "2.0.20" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" +checksum = "09e52cb86a36cede5cb101bf8908837b3e4c6e5e59fe7fd85c23fb56200d189e" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.20" +version = "2.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" +checksum = "fe5197923287db20a58125f0bc85c062f7f2c892de97b18c356f9efb14b28524" dependencies = [ "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", ] [[package]] name = "tinyvec" -version = "1.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf0ded5c4e56918d8f8a339e1bb67d038d3bc6d144ac407904015ba2e4cde9b" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "fd3ca314f692efd6c868f8408f53fe444634a845f96c028b97d35f6a1f79f0ee" [[package]] name = "toml" @@ -1749,9 +1740,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.13+spec-1.1.0" +version = "0.25.15+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" +checksum = "1340ea94a5856333492c9064b02c778b191dd2c853778d9609debdcdfea3a614" dependencies = [ "indexmap", "toml_datetime 1.1.1+spec-1.1.0", @@ -1795,9 +1786,9 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" [[package]] name = "unicode-normalization" @@ -1862,9 +1853,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" +checksum = "9bb54f33acc68fd454578d9820b0bde1a1a3d17aa17bb7b6595806d02886d409" dependencies = [ "cfg-if", "once_cell", @@ -1875,9 +1866,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" +checksum = "2e29d0c35b16e224a7eeb5cd2d25e3e1968fbd65604117b44d3b789d00ee8535" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1885,22 +1876,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" +checksum = "6f501a8bc3719dba86ef8ae4728879c08001bea749eb1333ac5b91e040e2a6b7" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 3.0.5", + "syn 3.0.6", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.128" +version = "0.2.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" +checksum = "23f0c9c52aa7cd7d77769a4cfe2a9adb1b331f489a41d912ce14513d5ab995c6" dependencies = [ "unicode-ident", ] @@ -2064,18 +2055,18 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" -version = "0.8.57" +version = "0.8.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" +checksum = "6df92bf3d9227be3d53173901ddbffac2babc27ae50f397776ffd6dc33f800cb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.57" +version = "0.8.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" +checksum = "ac4f328cf2f05d084e496c3e9c3f33ed0a183656a16e1fcec4d464d8373aec82" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index d54cc7f..7703add 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["simplicity", "liquid", "elements", "smart-contracts"] categories = ["cryptography::cryptocurrencies"] [dependencies] -smplx-std = { version = "0.0.11" } +smplx-std = { version = "0.0.12" } [dev-dependencies] anyhow = { version = "1.0.101" } From 91b486add54b5d6462060ecb5852b2add7a15a94 Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:01:56 +0300 Subject: [PATCH 05/11] Remove`safe_` prefixes from math functions (#59) * removed `safe_` prefixes from div functions * removed `_safe` prefixes; `add` became `add_carry`; `sub` - `sub_borrow`; `mul` - `mul_wide`. --- docs/stdlib.json | 70 +++++++++--------- simf/lib/u128/math.simf | 32 ++++----- simf/lib/u128/mul_div.simf | 4 +- simf/lib/u16/math.simf | 8 +-- simf/lib/u256/math.simf | 104 +++++++++++++-------------- simf/lib/u256/mul_div.simf | 35 +++++---- simf/lib/u32/math.simf | 8 +-- simf/lib/u64/math.simf | 8 +-- simf/lib/u8/math.simf | 8 +-- simf/tests/u128/math/api.simf | 12 ++-- simf/tests/u128/math/primitives.simf | 20 +++--- simf/tests/u16/math.simf | 16 ++--- simf/tests/u256/math/add.simf | 6 +- simf/tests/u256/math/api.simf | 12 ++-- simf/tests/u256/math/sub_mul.simf | 24 +++---- simf/tests/u32/math.simf | 16 ++--- simf/tests/u64/math.simf | 16 ++--- simf/tests/u8/math.simf | 16 ++--- tests/stdlib/common/core.rs | 2 +- tests/stdlib/common/uint.rs | 50 +++++++------ tests/stdlib/u128/math/primitives.rs | 59 ++++++++------- tests/stdlib/u256/math/add.rs | 20 +++--- tests/stdlib/u256/math/div.rs | 1 - tests/stdlib/u256/math/sub_mul.rs | 58 +++++++-------- 24 files changed, 300 insertions(+), 305 deletions(-) diff --git a/docs/stdlib.json b/docs/stdlib.json index 801701e..93503bf 100644 --- a/docs/stdlib.json +++ b/docs/stdlib.json @@ -329,7 +329,7 @@ "description": "Add two `u8` values. Return `Some` of the sum, or `None` if the result overflows `u8`." }, { - "simplicityhl_name": "safe_add_8", + "simplicityhl_name": "add_8", "section": "`u8` arithmetic", "input_type": "u8, u8", "output_type": "u8", @@ -343,7 +343,7 @@ "description": "Subtract the second `u8` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u8`." }, { - "simplicityhl_name": "safe_sub_8", + "simplicityhl_name": "sub_8", "section": "`u8` arithmetic", "input_type": "u8, u8", "output_type": "u8", @@ -357,7 +357,7 @@ "description": "Multiply two `u8` values. Return `Some` of the product, or `None` if the result overflows `u8`." }, { - "simplicityhl_name": "safe_mul_8", + "simplicityhl_name": "mul_8", "section": "`u8` arithmetic", "input_type": "u8, u8", "output_type": "u8", @@ -371,7 +371,7 @@ "description": "Divide the first `u8` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." }, { - "simplicityhl_name": "safe_div_8", + "simplicityhl_name": "div_8", "section": "`u8` arithmetic", "input_type": "u8, u8", "output_type": "u8", @@ -455,7 +455,7 @@ "description": "Add two `u16` values. Return `Some` of the sum, or `None` if the result overflows `u16`." }, { - "simplicityhl_name": "safe_add_16", + "simplicityhl_name": "add_16", "section": "`u16` arithmetic", "input_type": "u16, u16", "output_type": "u16", @@ -469,7 +469,7 @@ "description": "Subtract the second `u16` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u16`." }, { - "simplicityhl_name": "safe_sub_16", + "simplicityhl_name": "sub_16", "section": "`u16` arithmetic", "input_type": "u16, u16", "output_type": "u16", @@ -483,7 +483,7 @@ "description": "Multiply two `u16` values. Return `Some` of the product, or `None` if the result overflows `u16`." }, { - "simplicityhl_name": "safe_mul_16", + "simplicityhl_name": "mul_16", "section": "`u16` arithmetic", "input_type": "u16, u16", "output_type": "u16", @@ -497,7 +497,7 @@ "description": "Divide the first `u16` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." }, { - "simplicityhl_name": "safe_div_16", + "simplicityhl_name": "div_16", "section": "`u16` arithmetic", "input_type": "u16, u16", "output_type": "u16", @@ -581,7 +581,7 @@ "description": "Add two `u32` values. Return `Some` of the sum, or `None` if the result overflows `u32`." }, { - "simplicityhl_name": "safe_add_32", + "simplicityhl_name": "add_32", "section": "`u32` arithmetic", "input_type": "u32, u32", "output_type": "u32", @@ -595,7 +595,7 @@ "description": "Subtract the second `u32` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u32`." }, { - "simplicityhl_name": "safe_sub_32", + "simplicityhl_name": "sub_32", "section": "`u32` arithmetic", "input_type": "u32, u32", "output_type": "u32", @@ -609,7 +609,7 @@ "description": "Multiply two `u32` values. Return `Some` of the product, or `None` if the result overflows `u32`." }, { - "simplicityhl_name": "safe_mul_32", + "simplicityhl_name": "mul_32", "section": "`u32` arithmetic", "input_type": "u32, u32", "output_type": "u32", @@ -623,7 +623,7 @@ "description": "Divide the first `u32` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." }, { - "simplicityhl_name": "safe_div_32", + "simplicityhl_name": "div_32", "section": "`u32` arithmetic", "input_type": "u32, u32", "output_type": "u32", @@ -714,7 +714,7 @@ "description": "Add two `u64` values. Return `Some` of the sum, or `None` if the result overflows `u64`." }, { - "simplicityhl_name": "safe_add_64", + "simplicityhl_name": "add_64", "section": "`u64` arithmetic", "input_type": "u64, u64", "output_type": "u64", @@ -728,7 +728,7 @@ "description": "Subtract the second `u64` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u64`." }, { - "simplicityhl_name": "safe_sub_64", + "simplicityhl_name": "sub_64", "section": "`u64` arithmetic", "input_type": "u64, u64", "output_type": "u64", @@ -742,7 +742,7 @@ "description": "Multiply two `u64` values. Return `Some` of the product, or `None` if the result overflows `u64`." }, { - "simplicityhl_name": "safe_mul_64", + "simplicityhl_name": "mul_64", "section": "`u64` arithmetic", "input_type": "u64, u64", "output_type": "u64", @@ -756,7 +756,7 @@ "description": "Divide the first `u64` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." }, { - "simplicityhl_name": "safe_div_64", + "simplicityhl_name": "div_64", "section": "`u64` arithmetic", "input_type": "u64, u64", "output_type": "u64", @@ -847,14 +847,14 @@ "description": "Narrow a `u64` value to `u32`.\n\n## Panics\nPanics if the value does not fit in `u32`." }, { - "simplicityhl_name": "add_128", + "simplicityhl_name": "add_128_carry", "section": "`u128` arithmetic", "input_type": "u128, u128", "output_type": "(bool, u128)", "description": "Add two `u128` values. Return the carry bit and the sum." }, { - "simplicityhl_name": "add_128_64", + "simplicityhl_name": "add_128_64_carry", "section": "`u128` arithmetic", "input_type": "u128, u64", "output_type": "(bool, u128)", @@ -868,14 +868,14 @@ "description": "Add two `u128` values. Return `Some` of the sum, or `None` if the result overflows `u128`." }, { - "simplicityhl_name": "safe_add_128", + "simplicityhl_name": "add_128", "section": "`u128` arithmetic", "input_type": "u128, u128", "output_type": "u128", "description": "Add two `u128` values.\n\n## Panics\nPanics if the result overflows `u128`." }, { - "simplicityhl_name": "sub_128", + "simplicityhl_name": "sub_128_borrow", "section": "`u128` arithmetic", "input_type": "u128, u128", "output_type": "(bool, u128)", @@ -889,14 +889,14 @@ "description": "Subtract the second `u128` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u128`." }, { - "simplicityhl_name": "safe_sub_128", + "simplicityhl_name": "sub_128", "section": "`u128` arithmetic", "input_type": "u128, u128", "output_type": "u128", "description": "Subtract the second `u128` value from the first.\n\n## Panics\nPanics if the result would overflow `u128`." }, { - "simplicityhl_name": "mul_128", + "simplicityhl_name": "mul_128_wide", "section": "`u128` arithmetic", "input_type": "u128, u128", "output_type": "u256", @@ -910,7 +910,7 @@ "description": "Multiply two `u128` values. Return `Some` of the product, or `None` if the result overflows `u128`." }, { - "simplicityhl_name": "safe_mul_128", + "simplicityhl_name": "mul_128", "section": "`u128` arithmetic", "input_type": "u128, u128", "output_type": "u128", @@ -959,7 +959,7 @@ "description": "Subtract the second `u128` value from the first, taking an incoming borrow bit. Return the outgoing borrow bit and the difference." }, { - "simplicityhl_name": "mul_128_64", + "simplicityhl_name": "mul_128_64_wide", "section": "`u128` arithmetic", "input_type": "u128, u64", "output_type": "u256", @@ -1127,14 +1127,14 @@ "description": "Narrow a `u128` value to `u64`.\n\n## Panics\nPanics if the value does not fit in `u64`." }, { - "simplicityhl_name": "add_256", + "simplicityhl_name": "add_256_carry", "section": "`u256` arithmetic", "input_type": "u256, u256", "output_type": "(bool, u256)", "description": "Add two `u256` values. Return the carry bit and the sum." }, { - "simplicityhl_name": "add_256_128", + "simplicityhl_name": "add_256_128_carry", "section": "`u256` arithmetic", "input_type": "u256, u128", "output_type": "(bool, u256)", @@ -1155,14 +1155,14 @@ "description": "Add two `u256` values. Return `Some` of the sum, or `None` if the result overflows `u256`." }, { - "simplicityhl_name": "safe_add_256", + "simplicityhl_name": "add_256", "section": "`u256` arithmetic", "input_type": "u256, u256", "output_type": "u256", "description": "Add two `u256` values.\n\n## Panics\nPanics if the result overflows `u256`." }, { - "simplicityhl_name": "sub_256", + "simplicityhl_name": "sub_256_borrow", "section": "`u256` arithmetic", "input_type": "u256, u256", "output_type": "(bool, u256)", @@ -1176,35 +1176,35 @@ "description": "Subtract the second `u256` value from the first. Return `Some` of the difference, or `None` if the result would overflow `u256`." }, { - "simplicityhl_name": "safe_sub_256", + "simplicityhl_name": "sub_256", "section": "`u256` arithmetic", "input_type": "u256, u256", "output_type": "u256", "description": "Subtract the second `u256` value from the first.\n\n## Panics\nPanics if the result would overflow `u256`." }, { - "simplicityhl_name": "mul_256", + "simplicityhl_name": "mul_256_wide", "section": "`u256` arithmetic", "input_type": "u256, u256", "output_type": "(u256, u256)", "description": "Multiply two `u256` values. The full, non-truncated product is returned as a pair of `u256` values, most-significant first, so this operation can never overflow." }, { - "simplicityhl_name": "mul_256_64", + "simplicityhl_name": "mul_256_64_wide", "section": "`u256` arithmetic", "input_type": "u256, u64", "output_type": "(u64, u256)", "description": "Multiply a `u256` value by a `u64` value. The full, non-truncated product is returned as a `u64`/`u256` pair, most-significant first, so this operation can never overflow." }, { - "simplicityhl_name": "mul_256_128", + "simplicityhl_name": "mul_256_128_wide", "section": "`u256` arithmetic", "input_type": "u256, u128", "output_type": "(u128, u256)", "description": "Multiply a `u256` value by a `u128` value. The full, non-truncated product is returned as a `u128`/`u256` pair, most-significant first, so this operation can never overflow." }, { - "simplicityhl_name": "mul_512_128", + "simplicityhl_name": "mul_512_128_wide", "section": "`u256` arithmetic", "input_type": "u256, u256, u128", "output_type": "(u128, u256, u256)", @@ -1218,14 +1218,14 @@ "description": "Multiply two `u256` values. Return `Some` of the product, or `None` if the result overflows `u256`." }, { - "simplicityhl_name": "safe_mul_256", + "simplicityhl_name": "mul_256", "section": "`u256` arithmetic", "input_type": "u256, u256", "output_type": "u256", "description": "Multiply two `u256` values.\n\n## Panics\nPanics if the result overflows `u256`." }, { - "simplicityhl_name": "safe_mul_256_128", + "simplicityhl_name": "mul_256_128", "section": "`u256` arithmetic", "input_type": "u256, u128", "output_type": "u256", diff --git a/simf/lib/u128/math.simf b/simf/lib/u128/math.simf index a842565..db0d26b 100644 --- a/simf/lib/u128/math.simf +++ b/simf/lib/u128/math.simf @@ -7,7 +7,7 @@ use crate::lib::u128::convert::{u128_to_u256, split_u128_into_u64}; /// Arithmetic /// Adds two integers and returns the carry -pub fn add_128(a: u128, b: u128) -> (bool, u128) { +pub fn add_128_carry(a: u128, b: u128) -> (bool, u128) { let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); @@ -19,7 +19,7 @@ pub fn add_128(a: u128, b: u128) -> (bool, u128) { } /// 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) { +pub fn add_128_64_carry(a: u128, b: u64) -> (bool, u128) { let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); let (carry_low, res_low): (bool, u64) = jet::add_64(a_low, b); @@ -43,7 +43,7 @@ pub fn full_add_128(carry_in: bool, a: u128, b: u128) -> (bool, u128) { /// Returns the sum of two u128 values wrapped in Some, or None if the result overflows u128 pub fn checked_add_128(a: u128, b: u128) -> Option { - let (carry, sum): (bool, u128) = add_128(a, b); + let (carry, sum): (bool, u128) = add_128_carry(a, b); match carry { true => None, @@ -52,12 +52,12 @@ pub fn checked_add_128(a: u128, b: u128) -> Option { } /// Returns the sum of two u128 values, panics if the result overflows u128 -pub fn safe_add_128(a: u128, b: u128) -> u128 { +pub fn add_128(a: u128, b: u128) -> u128 { unwrap(checked_add_128(a, b)) } /// Subtracts the second integer from the first integer, and returns the borrow bit -pub fn sub_128(a: u128, b: u128) -> (bool, u128) { +pub fn sub_128_borrow(a: u128, b: u128) -> (bool, u128) { let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); @@ -82,7 +82,7 @@ pub fn full_sub_128(borrow_in: bool, a: u128, b: u128) -> (bool, u128) { /// 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); + let (borrow, diff): (bool, u128) = sub_128_borrow(a, b); match borrow { true => None, @@ -91,7 +91,7 @@ pub fn checked_sub_128(a: u128, b: u128) -> Option { } /// Returns the difference of two u128 values, panics if the result overflows u128 -pub fn safe_sub_128(a: u128, b: u128) -> u128 { +pub fn sub_128(a: u128, b: u128) -> u128 { unwrap(checked_sub_128(a, b)) } @@ -100,7 +100,7 @@ pub fn safe_sub_128(a: u128, b: u128) -> u128 { /// so a = a_high * 2^64 + a_low. /// In the same way, b = b_high * 2^64 + b_low. /// Therefore, a * b = 2^128 * a_high * b_high + 2^64(a_high * b_low + a_low * b_high) + a_low * b_low. -pub fn mul_128(a: u128, b: u128) -> u256 { +pub fn mul_128_wide(a: u128, b: u128) -> u256 { let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); @@ -111,7 +111,7 @@ pub fn mul_128(a: u128, b: u128) -> u256 { let product_1: u128 = jet::multiply_64(a_high, b_low); let product_2: u128 = jet::multiply_64(b_high, a_low); - let (carry_3a, middle): (bool, u128) = add_128(product_1, product_2); + let (carry_3a, middle): (bool, u128) = add_128_carry(product_1, product_2); let (middle_2, middle_1): (u64, u64) = split_u128_into_u64(middle); @@ -124,7 +124,7 @@ pub fn mul_128(a: u128, b: u128) -> u256 { // 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 `full_add_64` - // is used instead of `safe_add_64` to avoid the unnecessary overflow check + // is used instead of `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); @@ -138,7 +138,7 @@ pub fn mul_128(a: u128, b: u128) -> u256 { /// 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 { +pub fn mul_128_64_wide(a: u128, b: u64) -> u256 { let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); let highest: u128 = jet::multiply_64(a_high, b); @@ -149,7 +149,7 @@ pub fn mul_128_64(a: u128, b: u64) -> u256 { 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 + // is used instead of `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)) @@ -157,7 +157,7 @@ pub fn mul_128_64(a: u128, b: u64) -> u256 { /// 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); + let result: u256 = mul_128_wide(a, b); let (high, low): (u128, u128) = ::into(result); match is_zero_128(high) { @@ -167,7 +167,7 @@ pub fn checked_mul_128(a: u128, b: u128) -> Option { } /// Returns the product of two u128 values, panics if the result overflows u128 -pub fn safe_mul_128(a: u128, b: u128) -> u128 { +pub fn mul_128(a: u128, b: u128) -> u128 { unwrap(checked_mul_128(a, b)) } @@ -216,7 +216,7 @@ fn normalize_to_threshold_128_63(a: u128, b: u128, is_b_u128: bool) -> (u256, u1 match jet::eq_64(norm, 1) { true => (u128_to_u256(a), b, norm), - false => (mul_128(a, norm_128), safe_mul_128(b, norm_128), norm), + false => (mul_128_wide(a, norm_128), mul_128(b, norm_128), norm), } } @@ -295,7 +295,7 @@ fn algorithm_d_128_128(dividend: u128, divisor: u128) -> (u64, u128) { 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_to_u128(q))); + let remainder: u128 = sub_128(dividend, mul_128(divisor, u64_to_u128(q))); (q, remainder) } diff --git a/simf/lib/u128/mul_div.simf b/simf/lib/u128/mul_div.simf index 2b7bf3c..e3dfc1b 100644 --- a/simf/lib/u128/mul_div.simf +++ b/simf/lib/u128/mul_div.simf @@ -1,4 +1,4 @@ -use crate::lib::u128::math::mul_128; +use crate::lib::u128::math::mul_128_wide; use crate::lib::u128::convert::u128_to_u256; use crate::lib::u256::math::div_256; @@ -9,7 +9,7 @@ use crate::lib::u256::convert::safe_u256_to_u128; pub fn mul_div_128(a: u128, b: u128, denominator: u128) -> u128 { let denominator_256: u256 = u128_to_u256(denominator); - let result_256: u256 = div_256(mul_128(a, b), denominator_256); + let result_256: u256 = div_256(mul_128_wide(a, b), denominator_256); safe_u256_to_u128(result_256) } diff --git a/simf/lib/u16/math.simf b/simf/lib/u16/math.simf index cf3c1b6..ce298c8 100644 --- a/simf/lib/u16/math.simf +++ b/simf/lib/u16/math.simf @@ -9,7 +9,7 @@ pub fn checked_add_16(a: u16, b: u16) -> Option { } /// Returns the sum of two u16 values, panics if the result overflows u16 -pub fn safe_add_16(a: u16, b: u16) -> u16 { +pub fn add_16(a: u16, b: u16) -> u16 { unwrap(checked_add_16(a, b)) } @@ -24,7 +24,7 @@ pub fn checked_sub_16(a: u16, b: u16) -> Option { } /// Returns the difference of two u16 values, panics if the result overflows u16 -pub fn safe_sub_16(a: u16, b: u16) -> u16 { +pub fn sub_16(a: u16, b: u16) -> u16 { unwrap(checked_sub_16(a, b)) } @@ -41,7 +41,7 @@ pub fn checked_mul_16(a: u16, b: u16) -> Option { } /// Returns the product of two u16 values, panics if the result overflows u16 -pub fn safe_mul_16(a: u16, b: u16) -> u16 { +pub fn mul_16(a: u16, b: u16) -> u16 { unwrap(checked_mul_16(a, b)) } @@ -54,7 +54,7 @@ pub fn checked_div_16(a: u16, b: u16) -> Option { } /// Returns the quotient of two u16 values, panics if the result overflows u16 -pub fn safe_div_16(a: u16, b: u16) -> u16 { +pub fn div_16(a: u16, b: u16) -> u16 { unwrap(checked_div_16(a, b)) } diff --git a/simf/lib/u256/math.simf b/simf/lib/u256/math.simf index 8654bf9..8750b2f 100644 --- a/simf/lib/u256/math.simf +++ b/simf/lib/u256/math.simf @@ -2,13 +2,13 @@ use crate::lib::binary::{not, and, or}; use crate::lib::u64::convert::u64_to_u128; use crate::lib::u128::math::{ - add_128, + add_128_carry, full_add_128, - sub_128, + sub_128_borrow, full_sub_128, + mul_128_wide, + mul_128_64_wide, mul_128, - mul_128_64, - safe_mul_128, calculate_normalizer_base_64, estimate_quotient_digit_base_64, div_mod_128, @@ -24,11 +24,11 @@ use crate::lib::u256::convert::{split_u256_into_u64, split_u256_into_u128}; /// Arithmetic /// Adds two integers and returns the carry -pub fn add_256(a: u256, b: u256) -> (bool, u256) { +pub fn add_256_carry(a: u256, b: u256) -> (bool, u256) { let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); let (b_high, b_low): (u128, u128) = split_u256_into_u128(b); - let (carry_low, sum_low): (bool, u128) = add_128(a_low, b_low); + let (carry_low, sum_low): (bool, u128) = add_128_carry(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)); @@ -36,10 +36,10 @@ pub fn add_256(a: u256, b: u256) -> (bool, u256) { } /// 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) { +pub fn add_256_128_carry(a: u256, b: u128) -> (bool, u256) { let b: u256 = u128_to_u256(b); - add_256(a, b) + add_256_carry(a, b) } /// Adds two integers. Takes a carry-in and returns a carry-out @@ -56,7 +56,7 @@ pub fn full_add_256(carry_in: bool, a: u256, b: u256) -> (bool, u256) { /// 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); + let (carry, sum): (bool, u256) = add_256_carry(a, b); match carry { true => None, @@ -65,16 +65,16 @@ pub fn checked_add_256(a: u256, b: u256) -> Option { } /// Returns the sum of two u256 values, panics if the result overflows u256 -pub fn safe_add_256(a: u256, b: u256) -> u256 { +pub fn add_256(a: u256, b: u256) -> u256 { unwrap(checked_add_256(a, b)) } /// Subtracts the second integer from the first integer, and returns the borrow bit -pub fn sub_256(a: u256, b: u256) -> (bool, u256) { +pub fn sub_256_borrow(a: u256, b: u256) -> (bool, u256) { let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); let (b_high, b_low): (u128, u128) = split_u256_into_u128(b); - let (borrow_low, diff_low): (bool, u128) = sub_128(a_low, b_low); + let (borrow_low, diff_low): (bool, u128) = sub_128_borrow(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)); @@ -83,7 +83,7 @@ pub fn sub_256(a: u256, b: u256) -> (bool, u256) { /// 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); + let (borrow, diff): (bool, u256) = sub_256_borrow(a, b); match borrow { true => None, @@ -92,7 +92,7 @@ pub fn checked_sub_256(a: u256, b: u256) -> Option { } /// Returns the difference of two u256 values, panics if the result overflows u256 -pub fn safe_sub_256(a: u256, b: u256) -> u256 { +pub fn sub_256(a: u256, b: u256) -> u256 { unwrap(checked_sub_256(a, b)) } @@ -101,31 +101,31 @@ pub fn safe_sub_256(a: u256, b: u256) -> u256 { /// 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) { +pub fn mul_256_wide(a: u256, b: u256) -> (u256, u256) { let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); let (b_high, b_low): (u128, u128) = split_u256_into_u128(b); - let highest: u256 = mul_128(a_high, b_high); - let lowest: u256 = mul_128(a_low, b_low); + let highest: u256 = mul_128_wide(a_high, b_high); + let lowest: u256 = mul_128_wide(a_low, b_low); let (word_1, word_0): (u128, u128) = split_u256_into_u128(lowest); let (word_3, word_2): (u128, u128) = split_u256_into_u128(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 product_1: u256 = mul_128_wide(a_high, b_low); + let product_2: u256 = mul_128_wide(b_high, a_low); + let (carry_3a, middle): (bool, u256) = add_256_carry(product_1, product_2); let (middle_2, middle_1): (u128, u128) = split_u256_into_u128(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_2, res_1): (bool, u128) = add_128_carry(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 + // is used instead of `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); @@ -140,18 +140,18 @@ pub fn mul_256(a: u256, b: u256) -> (u256, u256) { /// 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) { +pub fn mul_256_64_wide(a: u256, b: u64) -> (u64, u256) { let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); - let lowest: u256 = mul_128_64(a_low, b); - let highest: u256 = mul_128_64(a_high, b); + let lowest: u256 = mul_128_64_wide(a_low, b); + let highest: u256 = mul_128_64_wide(a_high, b); let (word_1, word_0): (u128, u128) = split_u256_into_u128(lowest); let (word_3, word_2): (u128, u128) = split_u256_into_u128(highest); let (_, word_3): (u64, u64) = split_u128_into_u64(word_3); - let (carry_2, res_1): (bool, u128) = add_128(word_1, word_2); + let (carry_2, res_1): (bool, u128) = add_128_carry(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); @@ -164,16 +164,16 @@ pub fn mul_256_64(a: u256, b: u64) -> (u64, u256) { /// 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) { +pub fn mul_256_128_wide(a: u256, b: u128) -> (u128, u256) { let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); - let lowest: u256 = mul_128(a_low, b); - let highest: u256 = mul_128(a_high, b); + let lowest: u256 = mul_128_wide(a_low, b); + let highest: u256 = mul_128_wide(a_high, b); let (word_1, word_0): (u128, u128) = split_u256_into_u128(lowest); let (word_3, word_2): (u128, u128) = split_u256_into_u128(highest); - let (carry_2, res_1): (bool, u128) = add_128(word_1, word_2); + let (carry_2, res_1): (bool, u128) = add_128_carry(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); @@ -186,11 +186,11 @@ pub fn mul_256_128(a: u256, b: u128) -> (u128, u256) { /// The idea is that 512-bit `a` divides into 256-bit `a_high` and `a_low`, /// so a = a_high * 2^256 + a_low, /// and a * b = 2^256 * a_high * b + a_low * b -pub fn mul_512_128(a_high: u256, a_low: u256, b: u128) -> (u128, u256, u256) { - let (word_1, word_0): (u128, u256) = mul_256_128(a_low, b); - let (word_3, word_2): (u128, u256) = mul_256_128(a_high, b); +pub fn mul_512_128_wide(a_high: u256, a_low: u256, b: u128) -> (u128, u256, u256) { + let (word_1, word_0): (u128, u256) = mul_256_128_wide(a_low, b); + let (word_3, word_2): (u128, u256) = mul_256_128_wide(a_high, b); - let (carry, res_1): (bool, u256) = add_256_128(word_2, word_1); + let (carry, res_1): (bool, u256) = add_256_128_carry(word_2, word_1); // a * b fits into u640, so addition below can not overflow and // `full_add_128` is used to avoid the unnecessary overflow check @@ -201,7 +201,7 @@ pub fn mul_512_128(a_high: u256, a_low: u256, b: u128) -> (u128, u256, u256) { /// Returns the product of two u256 values wrapped in Some, or None if the result overflows u256 pub fn checked_mul_256(a: u256, b: u256) -> Option { - let (result_high, result_low): (u256, u256) = mul_256(a, b); + let (result_high, result_low): (u256, u256) = mul_256_wide(a, b); match is_zero_256(result_high) { true => Some(result_low), @@ -210,13 +210,13 @@ pub fn checked_mul_256(a: u256, b: u256) -> Option { } /// Returns the product of two u256 values, panics if the result overflows u256 -pub fn safe_mul_256(a: u256, b: u256) -> u256 { +pub fn 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 -pub fn safe_mul_256_128(a: u256, b: u128) -> u256 { - let (result_high, result_low): (u128, u256) = mul_256_128(a, b); +pub fn mul_256_128(a: u256, b: u128) -> u256 { + let (result_high, result_low): (u128, u256) = mul_256_128_wide(a, b); assert!(is_zero_128(result_high)); result_low @@ -242,7 +242,7 @@ pub fn calculate_normalizer_base_128(b: u256) -> u128 { let ( _, norm - ): (bool, u128) = add_128( + ): (bool, u128) = add_128_carry( norm, 1 ); // norm <= 2^127, so norm + 1 can not overflow @@ -267,8 +267,8 @@ fn normalize_to_threshold_256_63(a: u256, b: u128, is_b_u128: bool) -> (u64, u25 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); + let (high, low): (u64, u256) = mul_256_64_wide(a, norm); + let b_norm: u128 = mul_128(b, norm_128); (high, low, b_norm, norm) }, @@ -287,9 +287,9 @@ fn normalize_to_threshold_256_127(a: u256, b: u256) -> (u128, u256, u256) { match eq_128(norm, 1) { true => (0, a, b), false => { - let (high, low): (u128, u256) = mul_256_128(a, norm); + let (high, low): (u128, u256) = mul_256_128_wide(a, norm); - (high, low, safe_mul_256_128(b, norm)) + (high, low, mul_256_128(b, norm)) }, } } @@ -334,9 +334,9 @@ pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { fn mul_and_sub_64(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 q_v: u256 = mul_128_64_wide(v, q); - let u_updated: u256 = safe_sub_256(u, q_v); + let u_updated: u256 = sub_256(u, q_v); let (_, _, u1, u0): (u64, u64, u64, u64) = split_u256_into_u64(u_updated); (u1, u0) @@ -407,25 +407,25 @@ fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { let (_, q_hat): (u128, u128) = split_u256_into_u128(q_hat); let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); - let u_hat: u256 = mul_128(q_hat, v0); + let u_hat: u256 = mul_128_wide(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); + let (_, q_hat): (bool, u128) = sub_128_borrow(q_hat, 1); + let (carry, r_hat): (bool, u128) = add_128_carry(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_to_u256(v0)); + let (_, u_hat): (bool, u256) = sub_256_borrow(u_hat, u128_to_u256(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); + let (_, q_hat): (bool, u128) = sub_128_borrow(q_hat, 1); q_hat }, @@ -437,7 +437,7 @@ fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { false => q_hat, }; - let remainder: u256 = safe_sub_256(dividend, safe_mul_256_128(divisor, q)); + let remainder: u256 = sub_256(dividend, mul_256_128(divisor, q)); (q, remainder) } @@ -464,7 +464,7 @@ pub fn div_mod_256(a: u256, b: u256) -> (u256, u256) { true => { // safe: !lt_256(a, b) and a_high == b_high, so a_low >= b_low, // and the subtraction can not overflow - let (_, diff): (bool, u128) = sub_128(a_low, b_low); + let (_, diff): (bool, u128) = sub_128_borrow(a_low, b_low); (1, u128_to_u256(diff)) }, false => match is_zero_128(b_high) { diff --git a/simf/lib/u256/mul_div.simf b/simf/lib/u256/mul_div.simf index 9aa765b..f4f9878 100644 --- a/simf/lib/u256/mul_div.simf +++ b/simf/lib/u256/mul_div.simf @@ -1,18 +1,18 @@ use crate::lib::binary::not; -use crate::lib::u128::math::{add_128, sub_128, full_sub_128, mul_128}; +use crate::lib::u128::math::{add_128_carry, sub_128_borrow, full_sub_128, mul_128_wide}; use crate::lib::u128::bit::eq_128; use crate::lib::u128::comparison::{is_zero_128, lt_128}; use crate::lib::u256::math::{ algorithm_d_256_128, - mul_256_128, + mul_256_128_wide, + sub_256_borrow, sub_256, - safe_sub_256, calculate_normalizer_base_128, - mul_512_128, - safe_mul_256_128, - mul_256, + mul_512_128_wide, + mul_256_128, + mul_256_wide, div_mod_256_128, div_256 }; @@ -46,7 +46,7 @@ fn estimate_quotient_digit_base_128(u2: u128, u1: u128, u0: u128, v1: u128, v0: // This means u2 == v1, q_hat = 2^128, and r_hat = u1. // Therefore, we need to decrement q and add v1 to r_hat. // r_hat = u1 + v1 may overflow u128, which means that the estimate is exact - let (carry, r_hat): (bool, u128) = add_128(u1, v1); + let (carry, r_hat): (bool, u128) = add_128_carry(u1, v1); let u128_max: u128 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; @@ -60,21 +60,21 @@ fn estimate_quotient_digit_base_128(u2: u128, u1: u128, u0: u128, v1: u128, v0: let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); // correcting estimation: q_hat is off by at most 2. - match lt_256(r_hat_u0, mul_128(q_hat, v0)) { + match lt_256(r_hat_u0, mul_128_wide(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, u128) = sub_128(q_hat, 1); - let (carry, r_hat): (bool, u128) = add_128(r_hat, v1); + let (_, q_hat): (bool, u128) = sub_128_borrow(q_hat, 1); + let (carry, r_hat): (bool, u128) = add_128_carry(r_hat, v1); match carry { true => q_hat, false => { let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); - match lt_256(r_hat_u0, mul_128(q_hat, v0)) { + match lt_256(r_hat_u0, mul_128_wide(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, u128) = sub_128(q_hat, 1); + let (_, q_hat): (bool, u128) = sub_128_borrow(q_hat, 1); q_hat }, @@ -101,9 +101,9 @@ fn normalize_to_threshold_512_127(a_high: u256, a_low: u256, b: u256) -> (u128, match eq_128(norm, 1) { true => (0, a_high, a_low, b), false => { - let (a_2, a_1, a_0): (u128, u256, u256) = mul_512_128(a_high, a_low, norm); + let (a_2, a_1, a_0): (u128, u256, u256) = mul_512_128_wide(a_high, a_low, norm); - (a_2, a_1, a_0, safe_mul_256_128(b, norm)) + (a_2, a_1, a_0, mul_256_128(b, norm)) }, } } @@ -114,9 +114,9 @@ fn normalize_to_threshold_512_127(a_high: u256, a_low: u256, b: u256) -> (u128, fn mul_and_sub_128(q: u128, u2: u128, u1: u128, u0: u128, v: u256) -> (u128, u128) { let u1_0: u256 = <(u128, u128)>::into((u1, u0)); - let (q_v_1, q_v_0): (u128, u256) = mul_256_128(v, q); + let (q_v_1, q_v_0): (u128, u256) = mul_256_128_wide(v, q); - let (borrow_low, u_updated_1_0): (bool, u256) = sub_256(u1_0, q_v_0); + let (borrow_low, u_updated_1_0): (bool, u256) = sub_256_borrow(u1_0, q_v_0); let (borrow, u_updated_2): (bool, u128) = full_sub_128(borrow_low, u2, q_v_1); assert!(not(borrow)); @@ -156,14 +156,13 @@ fn algorithm_d_512_256(dividend_high: u256, dividend_low: u256, divisor: u256) - pub fn mul_div_256(a: u256, b: u256, denominator: u256) -> u256 { assert!(not(is_zero_256(denominator))); - let (result_high, result_low): (u256, u256) = mul_256(a, b); + let (result_high, result_low): (u256, u256) = mul_256_wide(a, b); match is_zero_256(result_high) { true => div_256(result_low, denominator), false => { // if result_high >= denominator, result will overflow u256 assert!(lt_256(result_high, denominator)); - let (denom_high, denom_low): (u128, u128) = split_u256_into_u128(denominator); match is_zero_128(denom_high) { diff --git a/simf/lib/u32/math.simf b/simf/lib/u32/math.simf index cfce540..0253b5e 100644 --- a/simf/lib/u32/math.simf +++ b/simf/lib/u32/math.simf @@ -9,7 +9,7 @@ pub fn checked_add_32(a: u32, b: u32) -> Option { } /// Returns the sum of two u32 values, panics if the result overflows u32 -pub fn safe_add_32(a: u32, b: u32) -> u32 { +pub fn add_32(a: u32, b: u32) -> u32 { unwrap(checked_add_32(a, b)) } @@ -24,7 +24,7 @@ pub fn checked_sub_32(a: u32, b: u32) -> Option { } /// Returns the difference of two u32 values, panics if the result overflows u32 -pub fn safe_sub_32(a: u32, b: u32) -> u32 { +pub fn sub_32(a: u32, b: u32) -> u32 { unwrap(checked_sub_32(a, b)) } @@ -41,7 +41,7 @@ pub fn checked_mul_32(a: u32, b: u32) -> Option { } /// Returns the product of two u32 values, panics if the result overflows u32 -pub fn safe_mul_32(a: u32, b: u32) -> u32 { +pub fn mul_32(a: u32, b: u32) -> u32 { unwrap(checked_mul_32(a, b)) } @@ -54,7 +54,7 @@ pub fn checked_div_32(a: u32, b: u32) -> Option { } /// Returns the quotient of two u32 values, panics if the result overflows u32 -pub fn safe_div_32(a: u32, b: u32) -> u32 { +pub fn div_32(a: u32, b: u32) -> u32 { unwrap(checked_div_32(a, b)) } diff --git a/simf/lib/u64/math.simf b/simf/lib/u64/math.simf index eebddc8..05a8498 100644 --- a/simf/lib/u64/math.simf +++ b/simf/lib/u64/math.simf @@ -9,7 +9,7 @@ pub fn checked_add_64(a: u64, b: u64) -> Option { } /// Returns the sum of two u64 values, panics if the result overflows u64 -pub fn safe_add_64(a: u64, b: u64) -> u64 { +pub fn add_64(a: u64, b: u64) -> u64 { unwrap(checked_add_64(a, b)) } @@ -24,7 +24,7 @@ pub fn checked_sub_64(a: u64, b: u64) -> Option { } /// Returns the difference of two u64 values, panics if the result overflows u64 -pub fn safe_sub_64(a: u64, b: u64) -> u64 { +pub fn sub_64(a: u64, b: u64) -> u64 { unwrap(checked_sub_64(a, b)) } @@ -41,7 +41,7 @@ pub fn checked_mul_64(a: u64, b: u64) -> Option { } /// Returns the product of two u64 values, panics if the result overflows u64 -pub fn safe_mul_64(a: u64, b: u64) -> u64 { +pub fn mul_64(a: u64, b: u64) -> u64 { unwrap(checked_mul_64(a, b)) } @@ -54,7 +54,7 @@ pub fn checked_div_64(a: u64, b: u64) -> Option { } /// Returns the quotient of two u64 values, panics if the result overflows u64 -pub fn safe_div_64(a: u64, b: u64) -> u64 { +pub fn div_64(a: u64, b: u64) -> u64 { unwrap(checked_div_64(a, b)) } diff --git a/simf/lib/u8/math.simf b/simf/lib/u8/math.simf index e2e9e88..1e56ee6 100644 --- a/simf/lib/u8/math.simf +++ b/simf/lib/u8/math.simf @@ -9,7 +9,7 @@ pub fn checked_add_8(a: u8, b: u8) -> Option { } /// Returns the sum of two u8 values, panics if the result overflows u8 -pub fn safe_add_8(a: u8, b: u8) -> u8 { +pub fn add_8(a: u8, b: u8) -> u8 { unwrap(checked_add_8(a, b)) } @@ -24,7 +24,7 @@ pub fn checked_sub_8(a: u8, b: u8) -> Option { } /// Returns the difference of two u8 values, panics if the result overflows u8 -pub fn safe_sub_8(a: u8, b: u8) -> u8 { +pub fn sub_8(a: u8, b: u8) -> u8 { unwrap(checked_sub_8(a, b)) } @@ -41,7 +41,7 @@ pub fn checked_mul_8(a: u8, b: u8) -> Option { } /// Returns the product of two u8 values, panics if the result overflows u8 -pub fn safe_mul_8(a: u8, b: u8) -> u8 { +pub fn mul_8(a: u8, b: u8) -> u8 { unwrap(checked_mul_8(a, b)) } @@ -54,7 +54,7 @@ pub fn checked_div_8(a: u8, b: u8) -> Option { } /// Returns the quotient of two u8 values, panics if the result overflows u8 -pub fn safe_div_8(a: u8, b: u8) -> u8 { +pub fn div_8(a: u8, b: u8) -> u8 { unwrap(checked_div_8(a, b)) } diff --git a/simf/tests/u128/math/api.simf b/simf/tests/u128/math/api.simf index 4769750..5cf1585 100644 --- a/simf/tests/u128/math/api.simf +++ b/simf/tests/u128/math/api.simf @@ -1,10 +1,10 @@ use crate::lib::u128::math::{ checked_add_128, - safe_add_128, + add_128, checked_sub_128, - safe_sub_128, + sub_128, checked_mul_128, - safe_mul_128, + mul_128, checked_div_128, div_128 }; @@ -32,7 +32,7 @@ fn main() { }; match is_selected(1, fn_idx) { true => { - assert_eq_128(safe_add_128(a, b), unwrap(expected)); + assert_eq_128(add_128(a, b), unwrap(expected)); }, false => (), }; @@ -46,7 +46,7 @@ fn main() { }; match is_selected(3, fn_idx) { true => { - assert_eq_128(safe_sub_128(a, b), unwrap(expected)); + assert_eq_128(sub_128(a, b), unwrap(expected)); }, false => (), }; @@ -60,7 +60,7 @@ fn main() { }; match is_selected(5, fn_idx) { true => { - assert_eq_128(safe_mul_128(a, b), unwrap(expected)); + assert_eq_128(mul_128(a, b), unwrap(expected)); }, false => (), }; diff --git a/simf/tests/u128/math/primitives.simf b/simf/tests/u128/math/primitives.simf index c8a6aa0..e8edb36 100644 --- a/simf/tests/u128/math/primitives.simf +++ b/simf/tests/u128/math/primitives.simf @@ -1,11 +1,11 @@ use crate::lib::u128::math::{ - add_128, - add_128_64, + add_128_carry, + add_128_64_carry, full_add_128, - sub_128, + sub_128_borrow, full_sub_128, - mul_128, - mul_128_64, + mul_128_wide, + mul_128_64_wide, calculate_normalizer_base_64, estimate_quotient_digit_base_64, div_mod_128_64, @@ -30,14 +30,14 @@ fn main() { match is_selected(0, fn_idx) { true => { - assert_eq_uint_bool_128(add_128(a, b), unwrap(expected), expected_bool); + assert_eq_uint_bool_128(add_128_carry(a, b), unwrap(expected), expected_bool); }, false => (), }; match is_selected(1, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); - assert_eq_uint_bool_128(add_128_64(a, b), unwrap(expected), expected_bool); + assert_eq_uint_bool_128(add_128_64_carry(a, b), unwrap(expected), expected_bool); }, false => (), }; @@ -54,7 +54,7 @@ fn main() { match is_selected(3, fn_idx) { true => { - assert_eq_uint_bool_128(sub_128(a, b), unwrap(expected), expected_bool); + assert_eq_uint_bool_128(sub_128_borrow(a, b), unwrap(expected), expected_bool); }, false => (), }; @@ -71,7 +71,7 @@ fn main() { match is_selected(5, fn_idx) { true => { - let result: u256 = mul_128(a, b); + let result: u256 = mul_128_wide(a, b); let (result_high, result_low): (u128, u128) = ::into(result); @@ -84,7 +84,7 @@ fn main() { match is_selected(6, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); - let result: u256 = mul_128_64(a, b); + let result: u256 = mul_128_64_wide(a, b); let (result_high, result_low): (u128, u128) = ::into(result); diff --git a/simf/tests/u16/math.simf b/simf/tests/u16/math.simf index 6357266..4736d91 100644 --- a/simf/tests/u16/math.simf +++ b/simf/tests/u16/math.simf @@ -1,12 +1,12 @@ use crate::lib::u16::math::{ checked_add_16, - safe_add_16, + add_16, checked_sub_16, - safe_sub_16, + sub_16, checked_mul_16, - safe_mul_16, + mul_16, checked_div_16, - safe_div_16, + div_16, gt_16, ge_16 }; @@ -30,7 +30,7 @@ fn main() { }; match is_selected(1, fn_idx) { true => { - assert!(jet::eq_16(safe_add_16(a, b), unwrap(expected))); + assert!(jet::eq_16(add_16(a, b), unwrap(expected))); }, false => (), }; @@ -44,7 +44,7 @@ fn main() { }; match is_selected(3, fn_idx) { true => { - assert!(jet::eq_16(safe_sub_16(a, b), unwrap(expected))); + assert!(jet::eq_16(sub_16(a, b), unwrap(expected))); }, false => (), }; @@ -58,7 +58,7 @@ fn main() { }; match is_selected(5, fn_idx) { true => { - assert!(jet::eq_16(safe_mul_16(a, b), unwrap(expected))); + assert!(jet::eq_16(mul_16(a, b), unwrap(expected))); }, false => (), }; @@ -72,7 +72,7 @@ fn main() { }; match is_selected(7, fn_idx) { true => { - assert!(jet::eq_16(safe_div_16(a, b), unwrap(expected))); + assert!(jet::eq_16(div_16(a, b), unwrap(expected))); }, false => (), }; diff --git a/simf/tests/u256/math/add.simf b/simf/tests/u256/math/add.simf index 398c4c5..7ba03e8 100644 --- a/simf/tests/u256/math/add.simf +++ b/simf/tests/u256/math/add.simf @@ -1,4 +1,4 @@ -use crate::lib::u256::math::{add_256, add_256_128, full_add_256}; +use crate::lib::u256::math::{add_256_carry, add_256_128_carry, full_add_256}; use crate::tests::support::expect::assert_eq_uint_bool_256; use crate::tests::support::dispatch::is_selected; @@ -16,7 +16,7 @@ fn main() { match is_selected(0, fn_idx) { true => { - assert_eq_uint_bool_256(add_256(a, b), unwrap(expected), expected_bool); + assert_eq_uint_bool_256(add_256_carry(a, b), unwrap(expected), expected_bool); }, false => (), }; @@ -24,7 +24,7 @@ fn main() { match is_selected(1, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); - assert_eq_uint_bool_256(add_256_128(a, b), unwrap(expected), expected_bool); + assert_eq_uint_bool_256(add_256_128_carry(a, b), unwrap(expected), expected_bool); }, false => (), }; diff --git a/simf/tests/u256/math/api.simf b/simf/tests/u256/math/api.simf index 8527fe0..7de1362 100644 --- a/simf/tests/u256/math/api.simf +++ b/simf/tests/u256/math/api.simf @@ -1,10 +1,10 @@ use crate::lib::u256::math::{ checked_add_256, - safe_add_256, + add_256, checked_sub_256, - safe_sub_256, + sub_256, checked_mul_256, - safe_mul_256, + mul_256, checked_div_256, div_256 }; @@ -32,7 +32,7 @@ fn main() { }; match is_selected(1, fn_idx) { true => { - assert_eq_256(safe_add_256(a, b), unwrap(expected)); + assert_eq_256(add_256(a, b), unwrap(expected)); }, false => (), }; @@ -46,7 +46,7 @@ fn main() { }; match is_selected(3, fn_idx) { true => { - assert_eq_256(safe_sub_256(a, b), unwrap(expected)); + assert_eq_256(sub_256(a, b), unwrap(expected)); }, false => (), }; @@ -60,7 +60,7 @@ fn main() { }; match is_selected(5, fn_idx) { true => { - assert_eq_256(safe_mul_256(a, b), unwrap(expected)); + assert_eq_256(mul_256(a, b), unwrap(expected)); }, false => (), }; diff --git a/simf/tests/u256/math/sub_mul.simf b/simf/tests/u256/math/sub_mul.simf index 4b525a0..2d47842 100644 --- a/simf/tests/u256/math/sub_mul.simf +++ b/simf/tests/u256/math/sub_mul.simf @@ -1,10 +1,10 @@ use crate::lib::u256::math::{ - sub_256, - mul_256, - mul_256_64, - mul_256_128, - mul_512_128, - safe_mul_256_128 + sub_256_borrow, + mul_256_wide, + mul_256_64_wide, + mul_256_128_wide, + mul_512_128_wide, + 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}; @@ -28,14 +28,14 @@ fn main() { match is_selected(0, fn_idx) { true => { - assert_eq_uint_bool_256(sub_256(a, b), unwrap(expected), expected_bool); + assert_eq_uint_bool_256(sub_256_borrow(a, b), unwrap(expected), expected_bool); }, false => (), }; match is_selected(1, fn_idx) { true => { - let (result_high, result_low): (u256, u256) = mul_256(a, b); + let (result_high, result_low): (u256, u256) = mul_256_wide(a, b); assert_eq_256(result_high, unwrap(expected)); assert_eq_256(result_low, second_expected); @@ -48,7 +48,7 @@ fn main() { 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); + let (result_high, result_low): (u64, u256) = mul_256_64_wide(a, b); assert_eq_64(result_high, expected); assert_eq_256(result_low, second_expected); @@ -61,7 +61,7 @@ fn main() { 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); + let (result_high, result_low): (u128, u256) = mul_256_128_wide(a, b); assert_eq_128(result_high, expected); assert_eq_256(result_low, second_expected); @@ -71,7 +71,7 @@ fn main() { match is_selected(4, fn_idx) { true => { - let (result_2, result_1, result_0): (u128, u256, u256) = mul_512_128(a, b, c); + let (result_2, result_1, result_0): (u128, u256, u256) = mul_512_128_wide(a, b, c); let (_, expected): (u128, u128) = ::into(unwrap(expected)); @@ -85,7 +85,7 @@ fn main() { match is_selected(5, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); - let result: u256 = safe_mul_256_128(a, b); + let result: u256 = mul_256_128(a, b); assert_eq_256(result, unwrap(expected)); }, diff --git a/simf/tests/u32/math.simf b/simf/tests/u32/math.simf index cf35bfd..103b12a 100644 --- a/simf/tests/u32/math.simf +++ b/simf/tests/u32/math.simf @@ -1,12 +1,12 @@ use crate::lib::u32::math::{ checked_add_32, - safe_add_32, + add_32, checked_sub_32, - safe_sub_32, + sub_32, checked_mul_32, - safe_mul_32, + mul_32, checked_div_32, - safe_div_32, + div_32, gt_32, ge_32 }; @@ -30,7 +30,7 @@ fn main() { }; match is_selected(1, fn_idx) { true => { - assert!(jet::eq_32(safe_add_32(a, b), unwrap(expected))); + assert!(jet::eq_32(add_32(a, b), unwrap(expected))); }, false => (), }; @@ -44,7 +44,7 @@ fn main() { }; match is_selected(3, fn_idx) { true => { - assert!(jet::eq_32(safe_sub_32(a, b), unwrap(expected))); + assert!(jet::eq_32(sub_32(a, b), unwrap(expected))); }, false => (), }; @@ -58,7 +58,7 @@ fn main() { }; match is_selected(5, fn_idx) { true => { - assert!(jet::eq_32(safe_mul_32(a, b), unwrap(expected))); + assert!(jet::eq_32(mul_32(a, b), unwrap(expected))); }, false => (), }; @@ -72,7 +72,7 @@ fn main() { }; match is_selected(7, fn_idx) { true => { - assert!(jet::eq_32(safe_div_32(a, b), unwrap(expected))); + assert!(jet::eq_32(div_32(a, b), unwrap(expected))); }, false => (), }; diff --git a/simf/tests/u64/math.simf b/simf/tests/u64/math.simf index f47bd9b..6f5f510 100644 --- a/simf/tests/u64/math.simf +++ b/simf/tests/u64/math.simf @@ -1,12 +1,12 @@ use crate::lib::u64::math::{ checked_add_64, - safe_add_64, + add_64, checked_sub_64, - safe_sub_64, + sub_64, checked_mul_64, - safe_mul_64, + mul_64, checked_div_64, - safe_div_64, + div_64, gt_64, ge_64 }; @@ -30,7 +30,7 @@ fn main() { }; match is_selected(1, fn_idx) { true => { - assert!(jet::eq_64(safe_add_64(a, b), unwrap(expected))); + assert!(jet::eq_64(add_64(a, b), unwrap(expected))); }, false => (), }; @@ -44,7 +44,7 @@ fn main() { }; match is_selected(3, fn_idx) { true => { - assert!(jet::eq_64(safe_sub_64(a, b), unwrap(expected))); + assert!(jet::eq_64(sub_64(a, b), unwrap(expected))); }, false => (), }; @@ -58,7 +58,7 @@ fn main() { }; match is_selected(5, fn_idx) { true => { - assert!(jet::eq_64(safe_mul_64(a, b), unwrap(expected))); + assert!(jet::eq_64(mul_64(a, b), unwrap(expected))); }, false => (), }; @@ -72,7 +72,7 @@ fn main() { }; match is_selected(7, fn_idx) { true => { - assert!(jet::eq_64(safe_div_64(a, b), unwrap(expected))); + assert!(jet::eq_64(div_64(a, b), unwrap(expected))); }, false => (), }; diff --git a/simf/tests/u8/math.simf b/simf/tests/u8/math.simf index f600e63..9e3eace 100644 --- a/simf/tests/u8/math.simf +++ b/simf/tests/u8/math.simf @@ -1,12 +1,12 @@ use crate::lib::u8::math::{ checked_add_8, - safe_add_8, + add_8, checked_sub_8, - safe_sub_8, + sub_8, checked_mul_8, - safe_mul_8, + mul_8, checked_div_8, - safe_div_8, + div_8, gt_8, ge_8 }; @@ -30,7 +30,7 @@ fn main() { }; match is_selected(1, fn_idx) { true => { - assert!(jet::eq_8(safe_add_8(a, b), unwrap(expected))); + assert!(jet::eq_8(add_8(a, b), unwrap(expected))); }, false => (), }; @@ -44,7 +44,7 @@ fn main() { }; match is_selected(3, fn_idx) { true => { - assert!(jet::eq_8(safe_sub_8(a, b), unwrap(expected))); + assert!(jet::eq_8(sub_8(a, b), unwrap(expected))); }, false => (), }; @@ -58,7 +58,7 @@ fn main() { }; match is_selected(5, fn_idx) { true => { - assert!(jet::eq_8(safe_mul_8(a, b), unwrap(expected))); + assert!(jet::eq_8(mul_8(a, b), unwrap(expected))); }, false => (), }; @@ -72,7 +72,7 @@ fn main() { }; match is_selected(7, fn_idx) { true => { - assert!(jet::eq_8(safe_div_8(a, b), unwrap(expected))); + assert!(jet::eq_8(div_8(a, b), unwrap(expected))); }, false => (), }; diff --git a/tests/stdlib/common/core.rs b/tests/stdlib/common/core.rs index ada5e42..e11226b 100644 --- a/tests/stdlib/common/core.rs +++ b/tests/stdlib/common/core.rs @@ -14,7 +14,7 @@ pub enum Expect { Ok, /// A failed `assert!` in the contract. AssertFailed, - /// Execution reached a pruned branch (e.g. `unwrap(None)`, a `safe_*` overflow). + /// Execution reached a pruned branch (e.g. `unwrap(None)`). PrunedBranch, } diff --git a/tests/stdlib/common/uint.rs b/tests/stdlib/common/uint.rs index f271a4b..1169c33 100644 --- a/tests/stdlib/common/uint.rs +++ b/tests/stdlib/common/uint.rs @@ -19,13 +19,13 @@ use super::core::{Expect, run}; /// program under `simf/tests`. pub enum CommonOp { CheckedAdd, - SafeAdd, + Add, CheckedSub, - SafeSub, + Sub, CheckedMul, - SafeMul, + Mul, CheckedDiv, - SafeDiv, + Div, Gt, Ge, } @@ -90,25 +90,25 @@ pub fn checked_add_overflow(context: simplex::TestContext) -> anyho ) } -pub fn safe_add_fitting(context: simplex::TestContext) -> anyhow::Result<()> { +pub fn add_fitting(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(T::ZERO..=T::HALF_MAX); let b = rand::thread_rng().gen_range(T::ZERO..=T::HALF_MAX); run( &context, T::program(), - T::witness(op(CommonOp::SafeAdd), a, b, Some(a + b)), + T::witness(op(CommonOp::Add), a, b, Some(a + b)), Expect::Ok, ) } -pub fn safe_add_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +pub fn add_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let b = rand::thread_rng().gen_range(T::ONE..=T::MAX); run( &context, T::program(), - T::witness(op(CommonOp::SafeAdd), T::MAX, b, None), + T::witness(op(CommonOp::Add), T::MAX, b, None), Expect::PrunedBranch, ) } @@ -138,26 +138,26 @@ pub fn checked_sub_overflow(context: simplex::TestContext) -> anyho ) } -pub fn safe_sub_fitting(context: simplex::TestContext) -> anyhow::Result<()> { +pub fn sub_fitting(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(T::ZERO..=T::MAX); let b = rand::thread_rng().gen_range(T::ZERO..=a); run( &context, T::program(), - T::witness(op(CommonOp::SafeSub), a, b, Some(a - b)), + T::witness(op(CommonOp::Sub), a, b, Some(a - b)), Expect::Ok, ) } -pub fn safe_sub_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +pub fn sub_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(T::ZERO..=T::MAX - T::ONE); let b = rand::thread_rng().gen_range(a + T::ONE..=T::MAX); run( &context, T::program(), - T::witness(op(CommonOp::SafeSub), a, b, None), + T::witness(op(CommonOp::Sub), a, b, None), Expect::PrunedBranch, ) } @@ -187,26 +187,26 @@ pub fn checked_mul_overflow(context: simplex::TestContext) -> anyho ) } -pub fn safe_mul_fitting(context: simplex::TestContext) -> anyhow::Result<()> { +pub fn mul_fitting(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(T::ZERO..T::MUL_BOUND); let b = rand::thread_rng().gen_range(T::ZERO..T::MUL_BOUND); run( &context, T::program(), - T::witness(op(CommonOp::SafeMul), a, b, Some(a * b)), + T::witness(op(CommonOp::Mul), a, b, Some(a * b)), Expect::Ok, ) } -pub fn safe_mul_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +pub fn mul_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let two = T::ONE + T::ONE; let b = rand::thread_rng().gen_range(two..=T::MAX); run( &context, T::program(), - T::witness(op(CommonOp::SafeMul), T::MAX, b, None), + T::witness(op(CommonOp::Mul), T::MAX, b, None), Expect::PrunedBranch, ) } @@ -235,21 +235,19 @@ pub fn checked_div_by_zero(context: simplex::TestContext) -> anyhow ) } -pub fn safe_div_fitting(context: simplex::TestContext) -> anyhow::Result<()> { +pub fn div_fitting(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(T::ZERO..=T::MAX); let b = rand::thread_rng().gen_range(T::ONE..=T::MAX); run( &context, T::program(), - T::witness(op(CommonOp::SafeDiv), a, b, Some(a / b)), + T::witness(op(CommonOp::Div), a, b, Some(a / b)), Expect::Ok, ) } -pub fn safe_div_by_zero( - context: simplex::TestContext, -) -> anyhow::Result<()> { +pub fn div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(T::ZERO..=T::MAX); let expect = if TypeId::of::() == TypeId::of::() @@ -263,7 +261,7 @@ pub fn safe_div_by_zero( run( &context, T::program(), - T::witness(op(CommonOp::SafeDiv), a, T::ZERO, None), + T::witness(op(CommonOp::Div), a, T::ZERO, None), expect, ) } @@ -372,13 +370,13 @@ macro_rules! uint_tests { ($t:ty) => { $crate::uint_tests!(@stub $t; checked_add_fitting checked_add_overflow - safe_add_fitting safe_add_overflow + add_fitting add_overflow checked_sub_fitting checked_sub_overflow - safe_sub_fitting safe_sub_overflow + sub_fitting sub_overflow checked_mul_fitting checked_mul_overflow - safe_mul_fitting safe_mul_overflow + mul_fitting mul_overflow checked_div_fitting checked_div_by_zero - safe_div_fitting safe_div_by_zero + div_fitting div_by_zero gt_greater gt_equal gt_less ge_greater ge_equal ge_less diff --git a/tests/stdlib/u128/math/primitives.rs b/tests/stdlib/u128/math/primitives.rs index f415f31..3d39661 100644 --- a/tests/stdlib/u128/math/primitives.rs +++ b/tests/stdlib/u128/math/primitives.rs @@ -12,19 +12,18 @@ use simplicityhl_std::artifacts::tests::u128::math::primitives::derived_primitiv use FunctionToTest::*; enum FunctionToTest { - Add128, - Add128_64, + Add128Carry, + Add128_64Carry, FullAdd128, - Sub128, + Sub128Borrow, FullSub128, - Mul128, - Mul128_64, + Mul128Wide, + Mul128_64Wide, CalculateNormalizerBase64, EstimateQuotientDigitBase64, DivMod128_64, DivMod128, } -// div_128 is already tested through safe_div_fitting and safe_div_by_zero in api.rs fn program() -> U128BasicMathTestProgram { U128BasicMathTestProgram::new(&U128BasicMathTestArguments {}) @@ -95,21 +94,21 @@ fn split_helper(a: U256) -> (u128, u128) { } #[simplex::test] -fn add_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn add_128_carry_not_overflow(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; - case(Add128).args(a, b).expect(result).run(&context) + case(Add128Carry).args(a, b).expect(result).run(&context) } #[simplex::test] -fn add_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn add_128_carry_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let a = u128::MAX; let b = rand::thread_rng().gen_range(1..=u128::MAX); let result = b - 1; - case(Add128) + case(Add128Carry) .args(a, b) .expect(result) .flag(true) @@ -117,21 +116,21 @@ fn add_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn add_128_64_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn add_128_64_carry_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u128::MAX / 2); let b = rand::thread_rng().gen_range(0..=u64::MAX) as u128; let result = a + b; - case(Add128_64).args(a, b).expect(result).run(&context) + case(Add128_64Carry).args(a, b).expect(result).run(&context) } #[simplex::test] -fn add_128_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn add_128_64_carry_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let a = u128::MAX; let b = rand::thread_rng().gen_range(1..=u64::MAX) as u128; let result = b - 1; - case(Add128_64) + case(Add128_64Carry) .args(a, b) .expect(result) .flag(true) @@ -203,23 +202,23 @@ fn full_add_128_overflow_carry_low_true(context: simplex::TestContext) -> anyhow } #[simplex::test] -fn sub_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_128_borrow_not_overflow(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; - case(Sub128).args(a, b).expect(result).run(&context) + case(Sub128Borrow).args(a, b).expect(result).run(&context) } #[simplex::test] -fn sub_128_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_128_borrow_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u128::MAX); - case(Sub128).args(a, a).expect(0).run(&context) + case(Sub128Borrow).args(a, a).expect(0).run(&context) } #[simplex::test] -fn sub_128_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_128_borrow_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u128::MAX); let b_high = rand::thread_rng().gen_range(0..=u64::MAX); @@ -229,7 +228,7 @@ fn sub_128_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { let carry = a < b; let result = a.wrapping_sub(b); - case(Sub128) + case(Sub128Borrow) .args(a, b) .expect(result) .flag(carry) @@ -237,7 +236,7 @@ fn sub_128_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn sub_128_diff_is_u64_max(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_128_borrow_diff_is_u64_max(context: simplex::TestContext) -> anyhow::Result<()> { let a_low: u64 = u64::MAX; let a_high = rand::thread_rng().gen_range(0..=u64::MAX); @@ -249,7 +248,7 @@ fn sub_128_diff_is_u64_max(context: simplex::TestContext) -> anyhow::Result<()> let carry = a < b; let result = a.wrapping_sub(b); - case(Sub128) + case(Sub128Borrow) .args(a, b) .expect(result) .flag(carry) @@ -257,20 +256,20 @@ fn sub_128_diff_is_u64_max(context: simplex::TestContext) -> anyhow::Result<()> } #[simplex::test] -fn sub_128_diff_is_u128_max(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_128_borrow_diff_is_u128_max(context: simplex::TestContext) -> anyhow::Result<()> { let a = u128::MAX; let b = 0; - case(Sub128).args(a, b).expect(a).run(&context) + case(Sub128Borrow).args(a, b).expect(a).run(&context) } #[simplex::test] -fn sub_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_128_borrow_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..u128::MAX); let b = u128::MAX; let result = a + 1; - case(Sub128) + case(Sub128Borrow) .args(a, b) .expect(result) .flag(true) @@ -342,14 +341,14 @@ fn full_sub_128_overflow_borrow_low_true(context: simplex::TestContext) -> anyho } #[simplex::test] -fn mul_128(context: simplex::TestContext) -> anyhow::Result<()> { +fn mul_128_wide(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); let result = U256::from(a) * U256::from(b); let (result_high, result_low) = split_helper(result); - case(Mul128) + case(Mul128Wide) .args(a, b) .expect(result_high) .second(result_low) @@ -357,14 +356,14 @@ fn mul_128(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn mul_128_64(context: simplex::TestContext) -> anyhow::Result<()> { +fn mul_128_64_wide(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); - case(Mul128_64) + case(Mul128_64Wide) .args(a, b as u128) .expect(result_high) .second(result_low) diff --git a/tests/stdlib/u256/math/add.rs b/tests/stdlib/u256/math/add.rs index bbd813c..5af5660 100644 --- a/tests/stdlib/u256/math/add.rs +++ b/tests/stdlib/u256/math/add.rs @@ -11,8 +11,8 @@ use simplicityhl_std::artifacts::tests::u256::math::add::derived_add::{ use FunctionToTest::*; enum FunctionToTest { - Add256, - Add256_128, + Add256Carry, + Add256_128Carry, FullAdd256, } @@ -77,24 +77,24 @@ impl Case { } #[simplex::test] -fn add_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn add_256_carry_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(); - case(Add256) + case(Add256Carry) .args(a.to_big_endian(), b.to_big_endian()) .expect(result) .run(&context) } #[simplex::test] -fn add_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn add_256_carry_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(); - case(Add256) + case(Add256Carry) .args(a.to_big_endian(), b.to_big_endian()) .expect(result) .flag(true) @@ -102,24 +102,24 @@ fn add_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn add_256_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn add_256_128_carry_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(); - case(Add256_128) + case(Add256_128Carry) .args(a.to_big_endian(), b.to_big_endian()) .expect(result) .run(&context) } #[simplex::test] -fn add_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn add_256_128_carry_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(); - case(Add256_128) + case(Add256_128Carry) .args(a.to_big_endian(), b.to_big_endian()) .expect(result) .flag(true) diff --git a/tests/stdlib/u256/math/div.rs b/tests/stdlib/u256/math/div.rs index a35f63f..7b40598 100644 --- a/tests/stdlib/u256/math/div.rs +++ b/tests/stdlib/u256/math/div.rs @@ -17,7 +17,6 @@ enum FunctionToTest { DivMod256_128, DivMod256, } -// div_256 is already tested through safe_div_fitting and safe_div_by_zero in api.rs fn program() -> U256TestDivProgram { U256TestDivProgram::new(&U256TestDivArguments {}) diff --git a/tests/stdlib/u256/math/sub_mul.rs b/tests/stdlib/u256/math/sub_mul.rs index 2d5a64d..3773232 100644 --- a/tests/stdlib/u256/math/sub_mul.rs +++ b/tests/stdlib/u256/math/sub_mul.rs @@ -13,12 +13,12 @@ use simplicityhl_std::artifacts::tests::u256::math::sub_mul::derived_sub_mul::{ use FunctionToTest::*; enum FunctionToTest { - Sub256, - Mul256, - Mul256_64, + Sub256Borrow, + Mul256Wide, + Mul256_64Wide, + Mul256_128Wide, + Mul512_128Wide, Mul256_128, - Mul512_128, - SafeMul256_128, } fn program() -> U256TestSubMulProgram { @@ -103,26 +103,26 @@ fn split_u512(a: [u8; 64]) -> ([u8; 32], [u8; 32]) { } #[simplex::test] -fn sub_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_256_borrow_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(); - case(Sub256) + case(Sub256Borrow) .args(a.to_big_endian(), b.to_big_endian()) .expect(result) .run(&context) } #[simplex::test] -fn sub_256_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_256_borrow_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); - case(Sub256).args(a, a).expect([0; 32]).run(&context) + case(Sub256Borrow).args(a, a).expect([0; 32]).run(&context) } #[simplex::test] -fn sub_256_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_256_borrow_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); @@ -131,7 +131,7 @@ fn sub_256_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { let (result, carry) = a.overflowing_sub(b); - case(Sub256) + case(Sub256Borrow) .args(a.to_big_endian(), b.to_big_endian()) .expect(result.to_big_endian()) .flag(carry) @@ -139,7 +139,7 @@ fn sub_256_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn sub_256_diff_is_u128_max(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_256_borrow_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); @@ -150,7 +150,7 @@ fn sub_256_diff_is_u128_max(context: simplex::TestContext) -> anyhow::Result<()> let (result, carry) = a.overflowing_sub(b); - case(Sub256) + case(Sub256Borrow) .args(a.to_big_endian(), b.to_big_endian()) .expect(result.to_big_endian()) .flag(carry) @@ -158,23 +158,23 @@ fn sub_256_diff_is_u128_max(context: simplex::TestContext) -> anyhow::Result<()> } #[simplex::test] -fn sub_256_diff_is_u256_max(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_256_borrow_diff_is_u256_max(context: simplex::TestContext) -> anyhow::Result<()> { let a = U256::MAX.to_big_endian(); let b = U256::zero(); - case(Sub256) + case(Sub256Borrow) .args(a, b.to_big_endian()) .expect(a) .run(&context) } #[simplex::test] -fn sub_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn sub_256_borrow_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::one(), U256::MAX - 1); let b = U256::MAX; let result = a + 1; - case(Sub256) + case(Sub256Borrow) .args(a.to_big_endian(), b.to_big_endian()) .expect(result.to_big_endian()) .flag(true) @@ -182,14 +182,14 @@ fn sub_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn mul_256(context: simplex::TestContext) -> anyhow::Result<()> { +fn mul_256_wide(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); - case(Mul256) + case(Mul256Wide) .args(a.to_big_endian(), b.to_big_endian()) .expect(result_high) .second(result_low) @@ -197,14 +197,14 @@ fn mul_256(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn mul_256_64(context: simplex::TestContext) -> anyhow::Result<()> { +fn mul_256_64_wide(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); - case(Mul256_64) + case(Mul256_64Wide) .args(a.to_big_endian(), b.to_big_endian()) .expect(result_high) .second(result_low) @@ -212,14 +212,14 @@ fn mul_256_64(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn mul_256_128(context: simplex::TestContext) -> anyhow::Result<()> { +fn mul_256_128_wide(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); - case(Mul256_128) + case(Mul256_128Wide) .args(a.to_big_endian(), b.to_big_endian()) .expect(result_high) .second(result_low) @@ -227,7 +227,7 @@ fn mul_256_128(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn mul_512_128(context: simplex::TestContext) -> anyhow::Result<()> { +fn mul_512_128_wide(context: simplex::TestContext) -> anyhow::Result<()> { let a_1 = generate_u256(U256::one(), U256::MAX); let a_0 = generate_u256(U256::one(), U256::MAX); let b = rand::thread_rng().gen_range(1..=u128::MAX); @@ -244,7 +244,7 @@ fn mul_512_128(context: simplex::TestContext) -> anyhow::Result<()> { let res_3_final = (U256::from_big_endian(&res_3_1) + U256::from_big_endian(&res_3)).to_big_endian(); - case(Mul512_128) + case(Mul512_128Wide) .args(a_1.to_big_endian(), a_0.to_big_endian()) .third_arg(b) .expect(res_3_final) @@ -254,22 +254,22 @@ fn mul_512_128(context: simplex::TestContext) -> anyhow::Result<()> { } #[simplex::test] -fn safe_mul_256_128_fitting(context: simplex::TestContext) -> anyhow::Result<()> { +fn mul_256_128_fitting(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::from(u128::MAX)); let b = generate_u256(U256::zero(), U256::from(u128::MAX)); - case(SafeMul256_128) + case(Mul256_128) .args(a.to_big_endian(), b.to_big_endian()) .expect((a * b).to_big_endian()) .run(&context) } #[simplex::test] -fn safe_mul_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { +fn mul_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); let b = U256::from(u128::MAX); - case(SafeMul256_128) + case(Mul256_128) .args(a.to_big_endian(), b.to_big_endian()) .expect([0; 32]) .expecting(&context, Expect::AssertFailed) From ba3523c9b8e688a2a6ac0a003232fdf7380f0354 Mon Sep 17 00:00:00 2001 From: aritkulova Date: Tue, 6 Oct 2026 18:12:23 +0300 Subject: [PATCH 06/11] updated changelog --- CHANGELOG.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a12e66..8957b4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,17 @@ ## [0.0.1] -The initial release with checked arithmetic operations for `u8`, `u16`, `u32`, `u64`, and `u128`; -`OP_RETURN` detection utilities; -implementation of `and`, `or`, `not`, and `xor` binary operators; -basic numeric assertions; -and equality and conversion operators for `secp256k1` points. +The initial release! +- Сhecked arithmetic operations for `u8`, `u16`, `u32`, `u64`, and `u128`; +- `OP_RETURN` detection utilities. +- Implementation of `and`, `or`, `not`, and `xor` binary operators. +- Basic numeric assertions. +- Equality and conversion operators for `secp256k1` points. + +## [Unreleased] + +- Added `mul_div` functions. +- Organized code into modules. +- Refactored test structure. +- Removed `safe_` prefixes from math functions, so the default versions are overflow-checked. +- Added functions for retrieving and asserting `AssetId` and `Amount`. From 4e6319182fb253014dfb8f158a1657306226be04 Mon Sep 17 00:00:00 2001 From: Yuliia Aritkulova <94910987+aritkulova@users.noreply.github.com> Date: Wed, 7 Oct 2026 19:27:33 +0300 Subject: [PATCH 07/11] Functions for retrieving AssetId and Amount (#61) * added functions for retrieving the AssetId and Amount * renamed asset_id_amount directory to utxo * updated tests to have different amounts for input and output of the same index * optimised getters * restructured doc comment; renamed get_current to get_explicit and get_confidential for consistency * test refactoring: added negative tests; added witness for parity bit; utxo order in a tx is now deterministic; small cleanups * refactoring and typo fixes * test refactoring: added `send_issued` and `create_utxos_with_new_asset` to send some other asset_id; added randomization for amounts; added function to check for pass and failure cases on same utxo. * refactored doc comments * excess doc commets --- docs/stdlib.json | 168 +++++++ simf/lib/utxo/getters/amount.simf | 80 +++ simf/lib/utxo/getters/asset_id.simf | 69 +++ .../utxo/getters/asset_id_with_amount.simf | 78 +++ simf/tests/utxo/getters/amount.simf | 104 ++++ simf/tests/utxo/getters/asset_id.simf | 104 ++++ .../utxo/getters/asset_id_with_amount.simf | 162 ++++++ tests/stdlib/common/core.rs | 187 ++++++- tests/stdlib/common/mod.rs | 1 + tests/stdlib/common/utxo_helper.rs | 165 ++++++ tests/stdlib/main.rs | 1 + tests/stdlib/utxo/getters/amount.rs | 462 +++++++++++++++++ tests/stdlib/utxo/getters/asset_id.rs | 355 +++++++++++++ .../utxo/getters/asset_id_with_amount.rs | 475 ++++++++++++++++++ tests/stdlib/utxo/getters/mod.rs | 3 + tests/stdlib/utxo/mod.rs | 1 + 16 files changed, 2406 insertions(+), 9 deletions(-) create mode 100644 simf/lib/utxo/getters/amount.simf create mode 100644 simf/lib/utxo/getters/asset_id.simf create mode 100644 simf/lib/utxo/getters/asset_id_with_amount.simf create mode 100644 simf/tests/utxo/getters/amount.simf create mode 100644 simf/tests/utxo/getters/asset_id.simf create mode 100644 simf/tests/utxo/getters/asset_id_with_amount.simf create mode 100644 tests/stdlib/common/utxo_helper.rs create mode 100644 tests/stdlib/utxo/getters/amount.rs create mode 100644 tests/stdlib/utxo/getters/asset_id.rs create mode 100644 tests/stdlib/utxo/getters/asset_id_with_amount.rs create mode 100644 tests/stdlib/utxo/getters/mod.rs create mode 100644 tests/stdlib/utxo/mod.rs diff --git a/docs/stdlib.json b/docs/stdlib.json index 93503bf..7a88ddc 100644 --- a/docs/stdlib.json +++ b/docs/stdlib.json @@ -104,6 +104,174 @@ "output_type": "()", "description": "Assert that two `bool` values are equal.\n\n## Panics\nThe assertion fails." }, + { + "simplicityhl_name": "get_explicit_amount", + "section": "Amount getters", + "input_type": "u32, bool", + "output_type": "u64", + "description": "Returns the explicit amount at the specified input or output index.\n\n## Panics\nPanics if the specified input or output does not exist or if its amount is confidential." + }, + { + "simplicityhl_name": "get_confidential_amount", + "section": "Amount getters", + "input_type": "u32, bool", + "output_type": "Confidential1", + "description": "Returns the confidential amount at the specified input or output index.\n\n## Panics\nPanics if the specified input or output does not exist or if its amount is explicit." + }, + { + "simplicityhl_name": "get_explicit_input_amount", + "section": "Amount getters", + "input_type": "u32", + "output_type": "u64", + "description": "Returns the explicit amount at the specified input index.\n\n## Panics\nPanics if the specified input does not exist or if its amount is confidential." + }, + { + "simplicityhl_name": "get_confidential_input_amount", + "section": "Amount getters", + "input_type": "u32", + "output_type": "Confidential1", + "description": "Returns the confidential amount at the specified input index.\n\n## Panics\nPanics if the specified input does not exist or if its amount is explicit." + }, + { + "simplicityhl_name": "get_explicit_output_amount", + "section": "Amount getters", + "input_type": "u32", + "output_type": "u64", + "description": "Returns the explicit amount at the specified output index.\n\n## Panics\nPanics if the specified output does not exist or if its amount is confidential." + }, + { + "simplicityhl_name": "get_confidential_output_amount", + "section": "Amount getters", + "input_type": "u32", + "output_type": "Confidential1", + "description": "Returns the confidential amount at the specified output index.\n\n## Panics\nPanics if the specified output does not exist or if its amount is explicit." + }, + { + "simplicityhl_name": "get_explicit_current_amount", + "section": "Amount getters", + "input_type": "()", + "output_type": "u64", + "description": "Returns the explicit input amount at the current_index.\n\n## Panics\nPanics if amount is confidential." + }, + { + "simplicityhl_name": "get_confidential_current_amount", + "section": "Amount getters", + "input_type": "()", + "output_type": "Confidential1", + "description": "Returns the confidential input amount at the current_index.\n\n## Panics\nPanics if amount is explicit." + }, + { + "simplicityhl_name": "get_explicit_asset_id_with_amount", + "section": "AssetId and Amount getters", + "input_type": "u32, bool", + "output_type": "(u256, u64)", + "description": "Returns explicit asset_id and amount at the specified input or output index.\n\n## Panics\nPanics if the specified input or output does not exist or if its asset_id and amount are confidential." + }, + { + "simplicityhl_name": "get_confidential_asset_id_with_amount", + "section": "AssetId and Amount getters", + "input_type": "u32, bool", + "output_type": "(Confidential1, Confidential1)", + "description": "Returns confidential asset_id and amount at the specified input or output index.\n\n## Panics\nPanics if the specified input or output does not exist or if its asset_id and amount are explicit." + }, + { + "simplicityhl_name": "get_explicit_input_asset_id_with_amount", + "section": "AssetId and Amount getters", + "input_type": "u32", + "output_type": "(u256, u64)", + "description": "Returns explicit asset_id and amount at the specified input index.\n\n## Panics\nPanics if the specified input does not exist or if its asset_id and amount are confidential." + }, + { + "simplicityhl_name": "get_confidential_input_asset_id_with_amount", + "section": "AssetId and Amount getters", + "input_type": "u32", + "output_type": "(Confidential1, Confidential1)", + "description": "Returns confidential asset_id and amount at the specified input index.\n\n## Panics\nPanics if the specified input does not exist or if its asset_id and amount are explicit." + }, + { + "simplicityhl_name": "get_explicit_output_asset_id_with_amount", + "section": "AssetId and Amount getters", + "input_type": "u32", + "output_type": "(u256, u64)", + "description": "Returns explicit asset_id and amount at the specified output index.\n\n## Panics\nPanics if the specified output does not exist or if its asset_id and amount are confidential." + }, + { + "simplicityhl_name": "get_confidential_output_asset_id_with_amount", + "section": "AssetId and Amount getters", + "input_type": "u32", + "output_type": "(Confidential1, Confidential1)", + "description": "Returns confidential asset_id and amount at the specified output index.\n\n## Panics\nPanics if the specified output does not exist or if its asset_id and amount are explicit." + }, + { + "simplicityhl_name": "get_explicit_current_asset_id_with_amount", + "section": "AssetId and Amount getters", + "input_type": "()", + "output_type": "(u256, u64)", + "description": "Returns explicit input asset_id and amount at the current_index.\n\n## Panics\nPanics if asset_id and amount are confidential." + }, + { + "simplicityhl_name": "get_confidential_current_asset_id_with_amount", + "section": "AssetId and Amount getters", + "input_type": "()", + "output_type": "(Confidential1, Confidential1)", + "description": "Returns confidential input asset_id and amount at the current_index.\n\n## Panics\nPanics if asset_id and amount are explicit." + }, + { + "simplicityhl_name": "get_explicit_asset_id", + "section": "AssetId getters", + "input_type": "u32, bool", + "output_type": "u256", + "description": "Returns the explicit asset_id at the specified input or output index.\n\n## Panics\nPanics if the specified input or output does not exist or if its asset_id is confidential." + }, + { + "simplicityhl_name": "get_confidential_asset_id", + "section": "AssetId getters", + "input_type": "u32, bool", + "output_type": "Confidential1", + "description": "Returns the confidential asset_id at the specified input or output index.\n\n## Panics\nPanics if the specified input or output does not exist or if its asset_id is explicit." + }, + { + "simplicityhl_name": "get_explicit_input_asset_id", + "section": "AssetId getters", + "input_type": "u32", + "output_type": "u256", + "description": "Returns the explicit asset_id at the specified input index.\n\n## Panics\nPanics if the specified input does not exist or if its asset_id is confidential." + }, + { + "simplicityhl_name": "get_confidential_input_asset_id", + "section": "AssetId getters", + "input_type": "u32", + "output_type": "Confidential1", + "description": "Returns the confidential asset_id at the specified input index.\n\n## Panics\nPanics if the specified input does not exist or if its asset_id is explicit." + }, + { + "simplicityhl_name": "get_explicit_output_asset_id", + "section": "AssetId getters", + "input_type": "u32", + "output_type": "u256", + "description": "Returns the explicit asset_id at the specified output index.\n\n## Panics\nPanics if the specified output does not exist or if its asset_id is confidential." + }, + { + "simplicityhl_name": "get_confidential_output_asset_id", + "section": "AssetId getters", + "input_type": "u32", + "output_type": "Confidential1", + "description": "Returns the confidential asset_id at the specified output index.\n\n## Panics\nPanics if the specified output does not exist or if its asset_id is explicit." + }, + { + "simplicityhl_name": "get_explicit_current_asset_id", + "section": "AssetId getters", + "input_type": "()", + "output_type": "u256", + "description": "Returns the explicit input asset_id at the current_index.\n\n## Panics\nPanics if asset_id is confidential." + }, + { + "simplicityhl_name": "get_confidential_current_asset_id", + "section": "AssetId getters", + "input_type": "()", + "output_type": "Confidential1", + "description": "Returns the confidential input asset_id at the current_index.\n\n## Panics\nPanics if asset_id is explicit." + }, { "simplicityhl_name": "not", "section": "Binary logic", diff --git a/simf/lib/utxo/getters/amount.simf b/simf/lib/utxo/getters/amount.simf new file mode 100644 index 0000000..55c2f75 --- /dev/null +++ b/simf/lib/utxo/getters/amount.simf @@ -0,0 +1,80 @@ +/// Functions for retrieving Amount +/// +/// If you need both `Amount` and `AssetId`, it is recommended to use the corresponding +/// function from the `asset_id_with_amount` file, because all `get_amount` functions +/// also return the `AssetId`. Using `get_amount` and `get_asset_id` together results +/// in unnecessary overhead. + +/// Returns the explicit amount at the specified input or output. +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn get_explicit_amount(index: u32, is_input_index: bool) -> u64 { + let ( + _, + amount + ): (Asset1, Amount1) = match is_input_index { + true => unwrap(jet::input_amount(index)), + false => unwrap(jet::output_amount(index)), + }; + + unwrap_right::<(u1, u256)>(amount) +} + +/// Returns the confidential amount at the specified input or output +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn get_confidential_amount(index: u32, is_input_index: bool) -> Confidential1 { + let ( + _, + amount + ): (Asset1, Amount1) = match is_input_index { + true => unwrap(jet::input_amount(index)), + false => unwrap(jet::output_amount(index)), + }; + + unwrap_left::(amount) +} + +/// Returns the explicit amount at the specified input +pub fn get_explicit_input_amount(index: u32) -> u64 { + let (_, amount): (Asset1, Amount1) = unwrap(jet::input_amount(index)); + + unwrap_right::<(u1, u256)>(amount) +} + +/// Returns the confidential amount at the specified input +pub fn get_confidential_input_amount(index: u32) -> Confidential1 { + let (_, amount): (Asset1, Amount1) = unwrap(jet::input_amount(index)); + + unwrap_left::(amount) +} + +/// Returns the explicit amount at the specified output +pub fn get_explicit_output_amount(index: u32) -> u64 { + let (_, amount): (Asset1, Amount1) = unwrap(jet::output_amount(index)); + + unwrap_right::<(u1, u256)>(amount) +} + +/// Returns the confidential amount at the specified output +pub fn get_confidential_output_amount(index: u32) -> Confidential1 { + let (_, amount): (Asset1, Amount1) = unwrap(jet::output_amount(index)); + + unwrap_left::(amount) +} + +/// Returns the explicit input amount at the current_index +pub fn get_explicit_current_amount() -> u64 { + let (_, amount): (Asset1, Amount1) = jet::current_amount(); + + unwrap_right::<(u1, u256)>(amount) +} + +/// Returns the confidential input amount at the current_index +pub fn get_confidential_current_amount() -> Confidential1 { + let (_, amount): (Asset1, Amount1) = jet::current_amount(); + + unwrap_left::(amount) +} diff --git a/simf/lib/utxo/getters/asset_id.simf b/simf/lib/utxo/getters/asset_id.simf new file mode 100644 index 0000000..3e2b8f1 --- /dev/null +++ b/simf/lib/utxo/getters/asset_id.simf @@ -0,0 +1,69 @@ +/// Functions for retrieving AssetId + +/// Returns the explicit asset_id at the specified input or output +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn get_explicit_asset_id(index: u32, is_input_index: bool) -> u256 { + let asset: Asset1 = match is_input_index { + true => unwrap(jet::input_asset(index)), + false => unwrap(jet::output_asset(index)), + }; + + unwrap_right::<(u1, u256)>(asset) +} + +/// Returns the confidential asset_id at the specified input or output +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn get_confidential_asset_id(index: u32, is_input_index: bool) -> Confidential1 { + let asset: Asset1 = match is_input_index { + true => unwrap(jet::input_asset(index)), + false => unwrap(jet::output_asset(index)), + }; + + unwrap_left::(asset) +} + +/// Returns the explicit asset_id at the specified input +pub fn get_explicit_input_asset_id(index: u32) -> u256 { + let asset: Asset1 = unwrap(jet::input_asset(index)); + + unwrap_right::<(u1, u256)>(asset) +} + +/// Returns the confidential asset_id at the specified input +pub fn get_confidential_input_asset_id(index: u32) -> Confidential1 { + let asset: Asset1 = unwrap(jet::input_asset(index)); + + unwrap_left::(asset) +} + +/// Returns the explicit asset_id at the specified output +pub fn get_explicit_output_asset_id(index: u32) -> u256 { + let asset: Asset1 = unwrap(jet::output_asset(index)); + + unwrap_right::<(u1, u256)>(asset) +} + +/// Returns the confidential asset_id at the specified output +pub fn get_confidential_output_asset_id(index: u32) -> Confidential1 { + let asset: Asset1 = unwrap(jet::output_asset(index)); + + unwrap_left::(asset) +} + +/// Returns the explicit input asset_id at the current_index +pub fn get_explicit_current_asset_id() -> u256 { + let asset: Asset1 = jet::current_asset(); + + unwrap_right::<(u1, u256)>(asset) +} + +/// Returns the confidential input asset_id at the current_index +pub fn get_confidential_current_asset_id() -> Confidential1 { + let asset: Asset1 = jet::current_asset(); + + unwrap_left::(asset) +} diff --git a/simf/lib/utxo/getters/asset_id_with_amount.simf b/simf/lib/utxo/getters/asset_id_with_amount.simf new file mode 100644 index 0000000..d555e6d --- /dev/null +++ b/simf/lib/utxo/getters/asset_id_with_amount.simf @@ -0,0 +1,78 @@ +/// Functions for retrieving an `AssetId` along with an `Amount` + +/// Returns explicit asset_id and amount at the specified input or output. +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn get_explicit_asset_id_with_amount(index: u32, is_input_index: bool) -> (u256, u64) { + let ( + asset, + amount + ): (Asset1, Amount1) = match is_input_index { + true => unwrap(jet::input_amount(index)), + false => unwrap(jet::output_amount(index)), + }; + + (unwrap_right::<(u1, u256)>(asset), unwrap_right::<(u1, u256)>(amount)) +} + +/// Returns confidential asset_id and amount at the specified input or output +/// +/// Warning: using this incurs the overhead, and should be used only when the input/output selection +/// needs to be determined at runtime; otherwise, use the input- or output- specific getter directly. +pub fn get_confidential_asset_id_with_amount( + index: u32, + is_input_index: bool +) -> (Confidential1, Confidential1) { + let ( + asset, + amount + ): (Asset1, Amount1) = match is_input_index { + true => unwrap(jet::input_amount(index)), + false => unwrap(jet::output_amount(index)), + }; + + (unwrap_left::(asset), unwrap_left::(amount)) +} + +/// Returns explicit asset_id and amount at the specified input +pub fn get_explicit_input_asset_id_with_amount(index: u32) -> (u256, u64) { + let (asset, amount): (Asset1, Amount1) = unwrap(jet::input_amount(index)); + + (unwrap_right::<(u1, u256)>(asset), unwrap_right::<(u1, u256)>(amount)) +} + +/// Returns confidential asset_id and amount at the specified input +pub fn get_confidential_input_asset_id_with_amount(index: u32) -> (Confidential1, Confidential1) { + let (asset, amount): (Asset1, Amount1) = unwrap(jet::input_amount(index)); + + (unwrap_left::(asset), unwrap_left::(amount)) +} + +/// Returns explicit asset_id and amount at the specified output +pub fn get_explicit_output_asset_id_with_amount(index: u32) -> (u256, u64) { + let (asset, amount): (Asset1, Amount1) = unwrap(jet::output_amount(index)); + + (unwrap_right::<(u1, u256)>(asset), unwrap_right::<(u1, u256)>(amount)) +} + +/// Returns confidential asset_id and amount at the specified output +pub fn get_confidential_output_asset_id_with_amount(index: u32) -> (Confidential1, Confidential1) { + let (asset, amount): (Asset1, Amount1) = unwrap(jet::output_amount(index)); + + (unwrap_left::(asset), unwrap_left::(amount)) +} + +/// Returns explicit input asset_id and amount at the current_index +pub fn get_explicit_current_asset_id_with_amount() -> (u256, u64) { + let (asset, amount): (Asset1, Amount1) = jet::current_amount(); + + (unwrap_right::<(u1, u256)>(asset), unwrap_right::<(u1, u256)>(amount)) +} + +/// Returns confidential input asset_id and amount at the current_index +pub fn get_confidential_current_asset_id_with_amount() -> (Confidential1, Confidential1) { + let (asset, amount): (Asset1, Amount1) = jet::current_amount(); + + (unwrap_left::(asset), unwrap_left::(amount)) +} diff --git a/simf/tests/utxo/getters/amount.simf b/simf/tests/utxo/getters/amount.simf new file mode 100644 index 0000000..08b6b0a --- /dev/null +++ b/simf/tests/utxo/getters/amount.simf @@ -0,0 +1,104 @@ +use crate::lib::utxo::getters::amount::{ + get_explicit_amount, + get_confidential_amount, + get_explicit_input_amount, + get_confidential_input_amount, + get_explicit_output_amount, + get_confidential_output_amount, + get_explicit_current_amount, + get_confidential_current_amount +}; +use crate::lib::asserts::{assert_eq_1, assert_eq_64, assert_eq_256}; + +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let index: u32 = witness::INDEX; + let is_input_index: bool = witness::IS_INPUT_INDEX; + + let expected_amount: u64 = witness::EXPECTED; + let expected_parity: u1 = witness::EXPECTED_PARITY; + let expected_confidential_amount: u256 = witness::EXPECTED_CONF; + + match is_selected(0, fn_idx) { + true => { + assert_eq_64(get_explicit_amount(index, is_input_index), expected_amount); + }, + false => (), + }; + + match is_selected(1, fn_idx) { + true => { + let ( + parity_bit, + confidential_amount + ): Confidential1 = get_confidential_amount(index, is_input_index); + + assert_eq_1(parity_bit,expected_parity); + assert_eq_256(confidential_amount, expected_confidential_amount); + }, + false => (), + }; + + match is_selected(2, fn_idx) { + true => { + assert_eq_64(get_explicit_input_amount(index), expected_amount); + }, + false => (), + }; + + match is_selected(3, fn_idx) { + true => { + let ( + parity_bit, + confidential_amount + ): Confidential1 = get_confidential_input_amount(index); + + assert_eq_1(parity_bit,expected_parity); + assert_eq_256(confidential_amount, expected_confidential_amount); + }, + false => (), + }; + + match is_selected(4, fn_idx) { + true => { + assert_eq_64(get_explicit_output_amount(index), expected_amount); + }, + false => (), + }; + + match is_selected(5, fn_idx) { + true => { + let ( + parity_bit, + confidential_amount + ): Confidential1 = get_confidential_output_amount(index); + + assert_eq_1(parity_bit,expected_parity); + assert_eq_256(confidential_amount, expected_confidential_amount); + }, + false => (), + }; + + match is_selected(6, fn_idx) { + true => { + assert_eq_64(get_explicit_current_amount(), expected_amount); + }, + false => (), + }; + + match is_selected(7, fn_idx) { + true => { + let ( + parity_bit, + confidential_amount + ): Confidential1 = get_confidential_current_amount(); + + assert_eq_1(parity_bit,expected_parity); + assert_eq_256(confidential_amount, expected_confidential_amount); + }, + false => (), + }; +} diff --git a/simf/tests/utxo/getters/asset_id.simf b/simf/tests/utxo/getters/asset_id.simf new file mode 100644 index 0000000..1655758 --- /dev/null +++ b/simf/tests/utxo/getters/asset_id.simf @@ -0,0 +1,104 @@ +use crate::lib::utxo::getters::asset_id::{ + get_explicit_asset_id, + get_confidential_asset_id, + get_explicit_input_asset_id, + get_confidential_input_asset_id, + get_explicit_output_asset_id, + get_confidential_output_asset_id, + get_explicit_current_asset_id, + get_confidential_current_asset_id +}; +use crate::lib::asserts::{assert_eq_1, assert_eq_256}; + +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let index: u32 = witness::INDEX; + let is_input_index: bool = witness::IS_INPUT_INDEX; + + let expected_asset_id: u256 = witness::EXPECTED; + let expected_parity: u1 = witness::EXPECTED_PARITY; + let expected_confidential_asset_id: u256 = witness::EXPECTED_CONF; + + match is_selected(0, fn_idx) { + true => { + assert_eq_256(get_explicit_asset_id(index, is_input_index), expected_asset_id); + }, + false => (), + }; + + match is_selected(1, fn_idx) { + true => { + let ( + parity_bit, + confidential_asset_id + ): Confidential1 = get_confidential_asset_id(index, is_input_index); + + assert_eq_1(parity_bit, expected_parity); + assert_eq_256(confidential_asset_id, expected_confidential_asset_id); + }, + false => (), + }; + + match is_selected(2, fn_idx) { + true => { + assert_eq_256(get_explicit_input_asset_id(index), expected_asset_id); + }, + false => (), + }; + + match is_selected(3, fn_idx) { + true => { + let ( + parity_bit, + confidential_asset_id + ): Confidential1 = get_confidential_input_asset_id(index); + + assert_eq_1(parity_bit, expected_parity); + assert_eq_256(confidential_asset_id, expected_confidential_asset_id); + }, + false => (), + }; + + match is_selected(4, fn_idx) { + true => { + assert_eq_256(get_explicit_output_asset_id(index), expected_asset_id); + }, + false => (), + }; + + match is_selected(5, fn_idx) { + true => { + let ( + parity_bit, + confidential_asset_id + ): Confidential1 = get_confidential_output_asset_id(index); + + assert_eq_1(parity_bit, expected_parity); + assert_eq_256(confidential_asset_id, expected_confidential_asset_id); + }, + false => (), + }; + + match is_selected(6, fn_idx) { + true => { + assert_eq_256(get_explicit_current_asset_id(), expected_asset_id); + }, + false => (), + }; + + match is_selected(7, fn_idx) { + true => { + let ( + parity_bit, + confidential_asset_id + ): Confidential1 = get_confidential_current_asset_id(); + + assert_eq_1(parity_bit, expected_parity); + assert_eq_256(confidential_asset_id, expected_confidential_asset_id); + }, + false => (), + }; +} diff --git a/simf/tests/utxo/getters/asset_id_with_amount.simf b/simf/tests/utxo/getters/asset_id_with_amount.simf new file mode 100644 index 0000000..298b265 --- /dev/null +++ b/simf/tests/utxo/getters/asset_id_with_amount.simf @@ -0,0 +1,162 @@ +use crate::lib::utxo::getters::asset_id_with_amount::{ + get_explicit_asset_id_with_amount, + get_confidential_asset_id_with_amount, + get_explicit_input_asset_id_with_amount, + get_confidential_input_asset_id_with_amount, + get_explicit_output_asset_id_with_amount, + get_confidential_output_asset_id_with_amount, + get_explicit_current_asset_id_with_amount, + get_confidential_current_asset_id_with_amount +}; +use crate::lib::asserts::{assert_eq_1, assert_eq_64, assert_eq_256}; + +use crate::tests::support::dispatch::is_selected; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let index: u32 = witness::INDEX; + let is_input_index: bool = witness::IS_INPUT_INDEX; + + let expected_asset_id: u256 = witness::EXPECTED_ASSET; + let expected_asset_parity: u1 = witness::EXPECTED_ASSET_PARITY; + let expected_confidential_asset_id: u256 = witness::EXPECTED_CONF_ASSET; + + let expected_amount: u64 = witness::EXPECTED_AMOUNT; + let expected_amount_parity: u1 = witness::EXPECTED_AMOUNT_PARITY; + let expected_confidential_amount: u256 = witness::EXPECTED_CONF_AMOUNT; + + match is_selected(0, fn_idx) { + true => { + let ( + asset_id, + amount + ): (u256, u64) = get_explicit_asset_id_with_amount(index, is_input_index); + + assert_eq_256(asset_id, expected_asset_id); + assert_eq_64(amount, expected_amount); + }, + false => (), + }; + + match is_selected(1, fn_idx) { + true => { + let ( + ( + parity_bit_asset_id, + confidential_asset_id + ), + ( + parity_bit_amount, + confidential_amount + ) + ): (Confidential1, Confidential1) = get_confidential_asset_id_with_amount( + index, + is_input_index + ); + + assert_eq_1(parity_bit_asset_id, expected_asset_parity); + assert_eq_256(confidential_asset_id, expected_confidential_asset_id); + + assert_eq_1(parity_bit_amount, expected_amount_parity); + assert_eq_256(confidential_amount, expected_confidential_amount); + }, + false => (), + }; + + match is_selected(2, fn_idx) { + true => { + let (asset_id, amount): (u256, u64) = get_explicit_input_asset_id_with_amount(index); + + assert_eq_256(asset_id, expected_asset_id); + assert_eq_64(amount, expected_amount); + }, + false => (), + }; + + match is_selected(3, fn_idx) { + true => { + let ( + ( + parity_bit_asset_id, + confidential_asset_id + ), + ( + parity_bit_amount, + confidential_amount + ) + ): (Confidential1, Confidential1) = get_confidential_input_asset_id_with_amount(index); + + assert_eq_1(parity_bit_asset_id, expected_asset_parity); + assert_eq_256(confidential_asset_id, expected_confidential_asset_id); + + assert_eq_1(parity_bit_amount, expected_amount_parity); + assert_eq_256(confidential_amount, expected_confidential_amount); + }, + false => (), + }; + + match is_selected(4, fn_idx) { + true => { + let (asset_id, amount): (u256, u64) = get_explicit_output_asset_id_with_amount(index); + + assert_eq_256(asset_id, expected_asset_id); + assert_eq_64(amount, expected_amount); + }, + false => (), + }; + + match is_selected(5, fn_idx) { + true => { + let ( + ( + parity_bit_asset_id, + confidential_asset_id + ), + ( + parity_bit_amount, + confidential_amount + ) + ): (Confidential1, Confidential1) = get_confidential_output_asset_id_with_amount(index); + + assert_eq_1(parity_bit_asset_id, expected_asset_parity); + assert_eq_256(confidential_asset_id, expected_confidential_asset_id); + + assert_eq_1(parity_bit_amount, expected_amount_parity); + assert_eq_256(confidential_amount, expected_confidential_amount); + }, + false => (), + }; + + match is_selected(6, fn_idx) { + true => { + let (asset_id, amount): (u256, u64) = get_explicit_current_asset_id_with_amount(); + + assert_eq_256(asset_id, expected_asset_id); + assert_eq_64(amount, expected_amount); + }, + false => (), + }; + + match is_selected(7, fn_idx) { + true => { + let ( + ( + parity_bit_asset_id, + confidential_asset_id + ), + ( + parity_bit_amount, + confidential_amount + ) + ): (Confidential1, Confidential1) = get_confidential_current_asset_id_with_amount(); + + assert_eq_1(parity_bit_asset_id, expected_asset_parity); + assert_eq_256(confidential_asset_id, expected_confidential_asset_id); + + assert_eq_1(parity_bit_amount, expected_amount_parity); + assert_eq_256(confidential_amount, expected_confidential_amount); + }, + false => (), + }; +} diff --git a/tests/stdlib/common/core.rs b/tests/stdlib/common/core.rs index e11226b..054768a 100644 --- a/tests/stdlib/common/core.rs +++ b/tests/stdlib/common/core.rs @@ -2,12 +2,17 @@ // part of it, so per-crate dead-code analysis would warn about the rest. #![allow(dead_code)] +use secp256k1_zkp::Secp256k1; use simplex::program::{Program, WitnessTrait}; -use simplex::simplicityhl::elements::Script; +use simplex::signer::Signer; +use simplex::simplicityhl::elements::{AssetId, Script, Txid}; +use simplex::transaction::partial_input::IssuanceInput; use simplex::transaction::{ - FinalTransaction, PartialInput, PartialOutput, ProgramInput, RequiredSignature, + FinalTransaction, PartialInput, PartialOutput, ProgramInput, RequiredSignature, UTXO, }; +use crate::common::utxo_helper::{DEFAULT_SEND_AMOUNT, search_utxo_by_txid}; + #[derive(Clone, Copy)] pub enum Expect { /// The spend succeeds. @@ -35,14 +40,66 @@ impl Expect { pub fn fund( context: &simplex::TestContext, program: &impl AsRef, + amount_to_send: u64, ) -> anyhow::Result