diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 209790a..87b55d5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -46,6 +46,8 @@ jobs: - name: Install simplex-cli uses: ./.github/actions/setup-smplx + with: + commit-sha: "c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" - name: Generate contract artifacts shell: bash diff --git a/Cargo.lock b/Cargo.lock index e9dcc4c..e8bf726 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -69,15 +69,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "ar_archive_writer" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4087686b4b0a3427190bae57a1d9a478dbb2d40c5dc1bd6e2b6d797913bdd348" +checksum = "73cd58deff2140a0a8eae87e417bd01db68a33e148aa93d1e8cd837e55e312b6" dependencies = [ "object", ] @@ -96,9 +96,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.17.1" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" +checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" dependencies = [ "aws-lc-sys", "zeroize", @@ -106,9 +106,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.42.0" +version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" +checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" dependencies = [ "cc", "cmake", @@ -177,9 +177,9 @@ dependencies = [ [[package]] name = "bitcoin-consensus-encoding" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "207311705279250ba465076a1bac4b1ac982855fff73fc5f67e22158ac58cdc9" +checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" dependencies = [ "bitcoin-internals", "hex-conservative 1.2.0", @@ -300,9 +300,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ "memchr", "serde_core", @@ -328,9 +328,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.2.67" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" +checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -360,18 +360,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.2" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd059f9da4f5c36b3787f65d38ccaab1cc315f07b01f89abc8359ee6a8205011" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.6.2" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -496,9 +496,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" dependencies = [ "serde", ] @@ -571,15 +571,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "fixed-hash" @@ -613,21 +613,21 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", "futures-task", @@ -677,20 +677,20 @@ checksum = "d8449d342b1c67f49169e92e71deb7b9b27f30062301a16dbc27a4cc8d2351b7" [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "globset" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.16", + "regex-automata 0.4.18", "regex-syntax 0.8.11", ] @@ -772,15 +772,15 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.30" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b009b6744c1445efd7244084e25e498636412effb6760b55067553baa925cc7" +checksum = "00b69833ed729dc5aa7d19541d96d6cf8e9137194207a04916d658e43168402f" dependencies = [ "crossbeam-deque", "globset", "log", "memchr", - "regex-automata 0.4.16", + "regex-automata 0.4.18", "same-file", "walkdir", "winapi-util", @@ -803,7 +803,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -849,9 +849,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -887,9 +887,9 @@ checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "linux-raw-sys" @@ -983,9 +983,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" dependencies = [ "num-traits", ] @@ -1001,9 +1001,9 @@ dependencies = [ [[package]] name = "object" -version = "0.37.3" +version = "0.39.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +checksum = "2e5a6c098c7a3b6547378093f5cc30bc54fd361ce711e05293a5cc589562739b" dependencies = [ "memchr", ] @@ -1045,7 +1045,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] @@ -1068,9 +1068,9 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "ppv-lite86" @@ -1088,7 +1088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn", + "syn 2.0.119", ] [[package]] @@ -1113,18 +1113,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] [[package]] name = "psm" -version = "0.1.31" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +checksum = "4dcd034599e63b970727f70d79e02d62390a4a84f7c6b827c27c46d5ac3fa622" dependencies = [ "ar_archive_writer", "cc", @@ -1132,9 +1132,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -1194,9 +1194,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -1263,9 +1263,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.42" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "aws-lc-rs", "log", @@ -1278,18 +1278,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" dependencies = [ "aws-lc-rs", "ring", @@ -1358,9 +1358,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -1368,29 +1368,29 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -1491,9 +1491,8 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smplx-build" -version = "0.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55a9a38dc0d803acdeed6217cdf9c1b3ded9220796315a458f47572269abaf2f" +version = "0.0.9" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "glob", "globwalk", @@ -1502,28 +1501,28 @@ dependencies = [ "proc-macro2", "quote", "serde", + "serde_json", "simplicityhl", - "syn", + "syn 2.0.119", "thiserror", "toml", + "toml_edit", ] [[package]] name = "smplx-macros" -version = "0.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e1091132a1f5157111920504e403fc8a4eb8a7814a376e4bd4c55a7fb6edff" +version = "0.0.9" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "smplx-build", "smplx-test", - "syn", + "syn 2.0.119", ] [[package]] name = "smplx-regtest" -version = "0.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ccf7e3eade934e890bcee3558af658de46177dbd6a5dd0302bb1a64b7701bbe" +version = "0.0.9" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "electrsd", "hex", @@ -1537,9 +1536,8 @@ dependencies = [ [[package]] name = "smplx-sdk" -version = "0.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d06536b493296672867c1a1ea8d67538c233af72624c18694ecb50691fcb74" +version = "0.0.9" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "bip39", "bitcoin_hashes", @@ -1557,9 +1555,8 @@ dependencies = [ [[package]] name = "smplx-std" -version = "0.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "834fd437c9394610ece857755d5967124a0b97c6837bf1170ae8deed38ba4d32" +version = "0.0.9" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "either", "serde", @@ -1571,9 +1568,8 @@ dependencies = [ [[package]] name = "smplx-test" -version = "0.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82bf119d89ca2b2ac992377912efe7dd1915e0aa6a193ca5383fb8a3da86594c" +version = "0.0.9" +source = "git+https://github.com/BlockstreamResearch/smplx.git?rev=c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b#c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" dependencies = [ "electrsd", "proc-macro2", @@ -1582,16 +1578,16 @@ dependencies = [ "simplicityhl", "smplx-regtest", "smplx-sdk", - "syn", + "syn 2.0.119", "thiserror", "toml", ] [[package]] name = "stacker" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" +checksum = "707f49d46706bacf8a2b00d51dace3f9de527c13eec3778f570c411f89e69967" dependencies = [ "cc", "cfg-if", @@ -1629,6 +1625,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tap" version = "1.0.1" @@ -1650,22 +1657,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 3.0.3", ] [[package]] @@ -1725,14 +1732,15 @@ dependencies = [ "indexmap", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", + "toml_writer", "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ "winnow 1.0.4", ] @@ -1751,9 +1759,9 @@ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uint" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909988d098b2f738727b161a106cfc7cab00c539c2687a8836f8e565976fb53e" +checksum = "6f9227a75a5a540a464c832ad4a4195dbdbecd8787610a56262721fde6f04f90" dependencies = [ "byteorder", "crunchy", @@ -1824,9 +1832,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -1837,9 +1845,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1847,31 +1855,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] [[package]] name = "webpki-roots" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -2020,22 +2028,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.54" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.119", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index d636ad8..bb8698d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["simplicity", "liquid", "elements", "smart-contracts"] categories = ["cryptography::cryptocurrencies"] [dependencies] -smplx-std = "0.0.8" +smplx-std = { git = "https://github.com/BlockstreamResearch/smplx.git", rev = "c9cae5da3ace3e502fc19bda9cf5bd3fb909e49b" } [dev-dependencies] anyhow = { version = "1.0.101" } diff --git a/README.md b/README.md index e607392..1f1bfe7 100644 --- a/README.md +++ b/README.md @@ -10,25 +10,58 @@ The standard library implementation for [SimplicityHL](https://github.com/Blocks ```md simf/lib ├── secp256k1 -│ └── operations.simf +│ └── operations.simf │ ├── Conversions between `Ge`, `Gej`, and compressed `Point`. │ ├── Subtraction for `Fe`, `Scalar`, `Gej`. │ ├── Equality predicates and their `assert_*` counterparts. │ └── Safe Jacobian-to-affine normalization. +├── u1 +│ └── convert.simf +│ └── Conversions from `u1` to other uint types and `bool`. +├── u8 +│ ├── convert.simf +│ │ └── Conversions between `u8` and other uint types. +│ └── math.simf +│ └── Overflow-checked arithmetic and `gt` / `ge`. +├── u16 +│ ├── convert.simf +│ │ └── Conversions between `u16` and other uint types. +│ └── math.simf +│ └── Overflow-checked arithmetic and `gt` / `ge`. +├── u32 +│ ├── convert.simf +│ │ └── Conversions between `u32` and other uint types. +│ └── math.simf +│ └── Overflow-checked arithmetic and `gt` / `ge`. +├── u64 +│ ├── convert.simf +│ │ └── Conversions between `u64` and other uint types. +│ └── math.simf +│ └── Overflow-checked arithmetic and `gt` / `ge`. +├── u128 +│ ├── bit.simf +│ │ └── Basic bit operations that are available as jets for `u8`–`u64` but are missing for `u128`. +│ ├── comparison.simf +│ │ └── Basic comparison operations that are available as jets for `u8`–`u64` but are missing for `u128`. +│ ├── convert.simf +│ │ └── Conversions between `u128` and other uint types. +│ └── math.simf +│ └── Carry/borrow arithmetic, multiplication, division, and overflow-checked wrappers. +├── u256 +│ ├── bit.simf +│ │ └── Basic bit operations that are available as jets for `u8`–`u64` but are missing for `u256`. +│ ├── comparison.simf +│ │ └── Basic comparison operations that are available as jets for `u8`–`u64` but are missing for `u256`. +│ ├── convert.simf +│ │ └── Conversions between `u256` and other uint types. +│ └── math.simf +│ └── Carry/borrow arithmetic, multiplication, division, and overflow-checked wrappers. ├── asserts.simf -│ └── Generic assertion helpers with equality checks between uint values. +│ └── Assertion helpers: `assert_eq_*` for uints and `bool`, plus `assert_none_*` for `Option`. ├── binary.simf │ └── Basic binary logic operations: `and`, `or`, `not`, `xor`. ├── op_return.simf -│ └── Utilities for detecting and enforcing `OP_RETURN` (null data) outputs. -├── u8.simf -├── u16.simf -├── u32.simf -├── u64.simf -└── u128.simf - ├── Overflow-checked arithmetic operations. - ├── Comparison helpers. - └── Basic operations that are available as jets for `u8`-`u64` but are missing for `u128`. + └── Utilities for detecting and enforcing `OP_RETURN` (null data) outputs. ``` ## Installation @@ -49,6 +82,19 @@ simplex install https://github.com/BlockstreamResearch/simplicityhl-std > [!NOTE] > The library works with Simplex version 0.0.7 or higher. +## Usage + +After installing the library, import modules from `std::lib`: + +```simf +use std::lib::u32::math::checked_add_32; +use std::lib::asserts::assert_eq_32; + +fn main() { + assert_eq_32(unwrap(checked_add_32(1, 2)), 3); +} +``` + ## Contributing We are open to any mind-blowing ideas! Please take a look at our [contributing guidelines](CONTRIBUTING.md) to get involved. diff --git a/simf/asserts_test.simf b/simf/asserts_test.simf index fafd2f1..36e1708 100644 --- a/simf/asserts_test.simf +++ b/simf/asserts_test.simf @@ -1,6 +1,19 @@ use crate::lib::asserts::{ - assert_eq_1, assert_eq_8, assert_eq_16, assert_eq_32, assert_eq_64,assert_eq_128, assert_eq_256, - assert_none_1, assert_none_8, assert_none_16, assert_none_32, assert_none_64, assert_none_128, assert_none_256 + assert_eq_1, + assert_eq_8, + assert_eq_16, + assert_eq_32, + assert_eq_64, + assert_eq_128, + assert_eq_256, + assert_eq_bool, + assert_none_1, + assert_none_8, + assert_none_16, + assert_none_32, + assert_none_64, + assert_none_128, + assert_none_256 }; use crate::helper::if_test_this_function; @@ -36,13 +49,14 @@ fn main() { match if_test_this_function(4, fn_idx) { true => { assert_eq_64(unwrap(a_u64), unwrap(b_u64)); }, false => (), }; match if_test_this_function(5, fn_idx) { true => { assert_eq_128(unwrap(a_u128), unwrap(b_u128)); }, false => (), }; match if_test_this_function(6, fn_idx) { true => { assert_eq_256(unwrap(a_u256), unwrap(b_u256)); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => { assert_eq_bool(::into(unwrap(a_u1)), ::into(unwrap(b_u1))); }, false => (), }; // Assert None - match if_test_this_function(7, fn_idx) { true => { assert_none_1(a_u1); }, false => (), }; - match if_test_this_function(8, fn_idx) { true => { assert_none_8(a_u8); }, false => (), }; - match if_test_this_function(9, fn_idx) { true => { assert_none_16(a_u16); }, false => (), }; - match if_test_this_function(10, fn_idx) { true => { assert_none_32(a_u32); }, false => (), }; - match if_test_this_function(11, fn_idx) { true => { assert_none_64(a_u64); }, false => (), }; - match if_test_this_function(12, fn_idx) { true => { assert_none_128(a_u128); }, false => (), }; - match if_test_this_function(13, fn_idx) { true => { assert_none_256(a_u256); }, false => (), }; + match if_test_this_function(8, fn_idx) { true => { assert_none_1(a_u1); }, false => (), }; + match if_test_this_function(9, fn_idx) { true => { assert_none_8(a_u8); }, false => (), }; + match if_test_this_function(10, fn_idx) { true => { assert_none_16(a_u16); }, false => (), }; + match if_test_this_function(11, fn_idx) { true => { assert_none_32(a_u32); }, false => (), }; + match if_test_this_function(12, fn_idx) { true => { assert_none_64(a_u64); }, false => (), }; + match if_test_this_function(13, fn_idx) { true => { assert_none_128(a_u128); }, false => (), }; + match if_test_this_function(14, fn_idx) { true => { assert_none_256(a_u256); }, false => (), }; } diff --git a/simf/lib/asserts.simf b/simf/lib/asserts.simf index 60c577c..1c6ad58 100644 --- a/simf/lib/asserts.simf +++ b/simf/lib/asserts.simf @@ -1,4 +1,4 @@ -use crate::lib::u128::eq_128; +use crate::lib::u128::bit::eq_128; /// Asserts that two `u1` are equal pub fn assert_eq_1(a: u1, b: u1) { @@ -35,6 +35,11 @@ pub fn assert_eq_256(a: u256, b: u256) { assert!(jet::eq_256(a, b)); } +/// Asserts that two bool values are equal +pub fn assert_eq_bool(a: bool, b: bool) { + assert_eq_1(::into(a), ::into(b)); +} + /// Asserts that provided `Option` value is a `None` pub fn assert_none_1(val: Option) { assert!(is_none::(val)); diff --git a/simf/lib/u1/convert.simf b/simf/lib/u1/convert.simf new file mode 100644 index 0000000..f01b02e --- /dev/null +++ b/simf/lib/u1/convert.simf @@ -0,0 +1,42 @@ +/// Widening uint conversions + +/// Converts u1 to u8 +pub fn u1_to_u8(a: u1) -> u8 { + jet::left_pad_low_1_8(a) +} + +/// Converts u1 to u16 +pub fn u1_to_u16(a: u1) -> u16 { + jet::left_pad_low_1_16(a) +} + +/// Converts u1 to u32 +pub fn u1_to_u32(a: u1) -> u32 { + jet::left_pad_low_1_32(a) +} + +/// Converts u1 to u64 +pub fn u1_to_u64(a: u1) -> u64 { + jet::left_pad_low_1_64(a) +} + +/// Converts u1 to u128 +pub fn u1_to_u128(a: u1) -> u128 { + let a_u64: u64 = u1_to_u64(a); + + <(u64, u64)>::into((0, a_u64)) +} + +/// Converts u1 to u256 +pub fn u1_to_u256(a: u1) -> u256 { + let a_u128: u128 = u1_to_u128(a); + + <(u128, u128)>::into((0, a_u128)) +} + +/// Type conversions + +/// Converts u1 to bool +pub fn u1_to_bool(a: u1) -> bool { + ::into(a) +} diff --git a/simf/lib/u128/bit.simf b/simf/lib/u128/bit.simf new file mode 100644 index 0000000..1e4f0e9 --- /dev/null +++ b/simf/lib/u128/bit.simf @@ -0,0 +1,79 @@ +use crate::lib::binary::and; + +/// Bit logic + +/// Bitwise AND of two 128-bit values +pub fn and_128(a: u128, b: u128) -> u128 { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + <(u64, u64)>::into((jet::and_64(a_high, b_high), jet::and_64(a_low, b_low))) +} + +/// Bitwise OR of two 128-bit values +pub fn or_128(a: u128, b: u128) -> u128 { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + <(u64, u64)>::into((jet::or_64(a_high, b_high), jet::or_64(a_low, b_low))) +} + +/// Checks if two 128-bit values are equal +pub fn eq_128(a: u128, b: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + and(jet::eq_64(a_high, b_high), jet::eq_64(a_low, b_low)) +} + +/// Left-shift a 128-bit value by the given amount. Bits are filled with zeroes +pub fn left_shift_128(shift: u8, a: u128) -> u128 { + match jet::is_zero_8(shift) { + true => a, + false => { + let (a_high, a_low): (u64, u64) = ::into(a); + + match jet::lt_8(shift, 64) { + true => { + let (_, low_to_high_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 + let shifted_bits: u64 = jet::right_shift_64(low_to_high_amount, a_low); + + let res_high: u64 = jet::or_64(jet::left_shift_64(shift, a_high), shifted_bits); + + <(u64, u64)>::into((res_high, jet::left_shift_64(shift, a_low))) + }, + false => { + let (_, shift): (bool, u8) = jet::subtract_8(shift, 64); // shift >= 64 + + <(u64, u64)>::into((jet::left_shift_64(shift, a_low), 0)) + } + } + } + } +} + +/// Right-shift a 128-bit value by the given amount. Bits are filled with zeroes +pub fn right_shift_128(shift: u8, a: u128) -> u128 { + match jet::is_zero_8(shift) { + true => a, + false => { + let (a_high, a_low): (u64, u64) = ::into(a); + + match jet::lt_8(shift, 64) { + true => { + let (_, high_to_low_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 + let shifted_bits: u64 = jet::left_shift_64(high_to_low_amount, a_high); + + let res_low: u64 = jet::or_64(jet::right_shift_64(shift, a_low), shifted_bits); + + <(u64, u64)>::into((jet::right_shift_64(shift, a_high), res_low)) + }, + false => { + let (_, shift): (bool, u8) = jet::subtract_8(shift, 64); // shift >= 64 + + <(u64, u64)>::into((0, jet::right_shift_64(shift, a_high))) + } + } + } + } +} diff --git a/simf/lib/u128/comparison.simf b/simf/lib/u128/comparison.simf new file mode 100644 index 0000000..25c0b58 --- /dev/null +++ b/simf/lib/u128/comparison.simf @@ -0,0 +1,48 @@ +use crate::lib::binary::and; + +/// Comparison operations + +/// Checks if an integer is zero +pub fn is_zero_128(a: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + + and(jet::is_zero_64(a_high), jet::is_zero_64(a_low)) +} + +/// Checks if an integer is less than another integer +pub fn lt_128(a: u128, b: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + match jet::lt_64(a_high, b_high) { + true => true, + false => match jet::eq_64(a_high, b_high) { + true => jet::lt_64(a_low, b_low), + false => false, + }, + } +} + +/// Checks if an integer is less than or equal to another integer +pub fn le_128(a: u128, b: u128) -> bool { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + match jet::lt_64(a_high, b_high) { + true => true, + false => match jet::eq_64(a_high, b_high) { + true => jet::le_64(a_low, b_low), + false => false, + }, + } +} + +/// Check if an integer is greater than another integer +pub fn gt_128(a: u128, b: u128) -> bool { + lt_128(b, a) +} + +/// Check if an integer is greater than or equal to another integer +pub fn ge_128(a: u128, b: u128) -> bool { + le_128(b, a) +} diff --git a/simf/lib/u128/convert.simf b/simf/lib/u128/convert.simf new file mode 100644 index 0000000..4b6aea9 --- /dev/null +++ b/simf/lib/u128/convert.simf @@ -0,0 +1,94 @@ +use crate::lib::u1::convert::u1_to_u128; +use crate::lib::u8::convert::u8_to_u128; +use crate::lib::u16::convert::u16_to_u128; +use crate::lib::u32::convert::u32_to_u128; +use crate::lib::u64::convert::u64_to_u128; +use crate::lib::u128::comparison::le_128; + +/// Widening uint conversions + +/// Converts u128 to u256 +pub fn u128_to_u256(a: u128) -> u256 { + <(u128, u128)>::into((0, a)) +} + +/// Splitting uint conversions + +/// Splits u128 into sixteen u8 +pub fn split_u128_into_u8( + a: u128 +) -> (u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8, u8) { + ::into(a) +} + +/// Splits u128 into eight u16 +pub fn split_u128_into_u16(a: u128) -> (u16, u16, u16, u16, u16, u16, u16, u16) { + ::into(a) +} + +/// Splits u128 into four u32 +pub fn split_u128_into_u32(a: u128) -> (u32, u32, u32, u32) { + ::into(a) +} + +/// Splits u128 into two u64 +pub fn split_u128_into_u64(a: u128) -> (u64, u64) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u128 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u128_to_u1(a: u128) -> u1 { + let u1_max: u128 = u1_to_u128(jet::high_1()); + let (_, a_64): (u64, u64) = split_u128_into_u64(a); + + assert!(le_128(a, u1_max)); + + jet::rightmost_64_1(a_64) +} + +/// Converts u128 into u8. +/// Panics if the value does not fit in u8 +pub fn safe_u128_to_u8(a: u128) -> u8 { + let u8_max: u128 = u8_to_u128(jet::high_8()); + let (_, a_64): (u64, u64) = split_u128_into_u64(a); + + assert!(le_128(a, u8_max)); + + jet::rightmost_64_8(a_64) +} + +/// Converts u128 into u16. +/// Panics if the value does not fit in u16 +pub fn safe_u128_to_u16(a: u128) -> u16 { + let u16_max: u128 = u16_to_u128(jet::high_16()); + let (_, a_64): (u64, u64) = split_u128_into_u64(a); + + assert!(le_128(a, u16_max)); + + jet::rightmost_64_16(a_64) +} + +/// Converts u128 into u32. +/// Panics if the value does not fit in u32 +pub fn safe_u128_to_u32(a: u128) -> u32 { + let u32_max: u128 = u32_to_u128(jet::high_32()); + let (_, a_64): (u64, u64) = split_u128_into_u64(a); + + assert!(le_128(a, u32_max)); + + jet::rightmost_64_32(a_64) +} + +/// Converts u128 into u64. +/// Panics if the value does not fit in u64 +pub fn safe_u128_to_u64(a: u128) -> u64 { + let u64_max: u128 = u64_to_u128(jet::high_64()); + let (_, a_64): (u64, u64) = split_u128_into_u64(a); + + assert!(le_128(a, u64_max)); + + a_64 +} diff --git a/simf/lib/u128.simf b/simf/lib/u128/math.simf similarity index 60% rename from simf/lib/u128.simf rename to simf/lib/u128/math.simf index 5695081..1909850 100644 --- a/simf/lib/u128.simf +++ b/simf/lib/u128/math.simf @@ -1,134 +1,8 @@ use crate::lib::binary::{not, or, and}; - -/// Bit logic - -/// Bitwise AND of two 128-bit values -pub fn and_128(a: u128, b: u128) -> u128 { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - <(u64, u64)>::into((jet::and_64(a_high, b_high), jet::and_64(a_low, b_low))) -} - -/// Bitwise OR of two 128-bit values -pub fn or_128(a: u128, b: u128) -> u128 { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - <(u64, u64)>::into((jet::or_64(a_high, b_high), jet::or_64(a_low, b_low))) -} - -/// Checks if two 128-bit values are equal -pub fn eq_128(a: u128, b: u128) -> bool { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - and(jet::eq_64(a_high, b_high), jet::eq_64(a_low, b_low)) -} - -/// Left-shift a 128-bit value by the given amount. Bits are filled with zeroes -pub fn left_shift_128(shift: u8, a: u128) -> u128 { - match jet::is_zero_8(shift) { - true => a, - false => { - let (a_high, a_low): (u64, u64) = ::into(a); - - match jet::lt_8(shift, 64) { - true => { - let (_, low_to_high_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 - let shifted_bits: u64 = jet::right_shift_64(low_to_high_amount, a_low); - - let res_high: u64 = jet::or_64(jet::left_shift_64(shift, a_high), shifted_bits); - - <(u64, u64)>::into((res_high, jet::left_shift_64(shift, a_low))) - }, - false => { - let (_, shift): (bool, u8) = jet::subtract_8(shift, 64); // shift >= 64 - - <(u64, u64)>::into((jet::left_shift_64(shift, a_low), 0)) - } - } - } - } -} - -/// Right-shift a 128-bit value by the given amount. Bits are filled with zeroes -pub fn right_shift_128(shift: u8, a: u128) -> u128 { - match jet::is_zero_8(shift) { - true => a, - false => { - let (a_high, a_low): (u64, u64) = ::into(a); - - match jet::lt_8(shift, 64) { - true => { - let (_, high_to_low_amount): (bool, u8) = jet::subtract_8(64, shift); // shift < 64 - let shifted_bits: u64 = jet::left_shift_64(high_to_low_amount, a_high); - - let res_low: u64 = jet::or_64(jet::right_shift_64(shift, a_low), shifted_bits); - - <(u64, u64)>::into((jet::right_shift_64(shift, a_high), res_low)) - }, - false => { - let (_, shift): (bool, u8) = jet::subtract_8(shift, 64); // shift >= 64 - - <(u64, u64)>::into((0, jet::right_shift_64(shift, a_high))) - } - } - } - } -} +use crate::lib::u128::comparison::{is_zero_128, lt_128}; /// Arithmetic -/// Checks if an integer is zero -pub fn is_zero_128(a: u128) -> bool { - let (a_high, a_low): (u64, u64) = ::into(a); - - and(jet::is_zero_64(a_high), jet::is_zero_64(a_low)) -} - -/// Checks if an integer is less than another integer -pub fn lt_128(a: u128, b: u128) -> bool { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - match jet::lt_64(a_high, b_high) { - true => true, - false => { - match jet::eq_64(a_high, b_high) { - true => jet::lt_64(a_low, b_low), - false => false, - } - } - } -} - -/// Checks if an integer is less than or equal to another integer -pub fn le_128(a: u128, b: u128) -> bool { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - match jet::lt_64(a_high, b_high) { - true => true, - false => { - match jet::eq_64(a_high, b_high) { - true => jet::le_64(a_low, b_low), - false => false, - } - } - } -} - -/// Check if an integer is greater than another integer -pub fn gt_128(a: u128, b: u128) -> bool { - lt_128(b, a) -} - -/// Check if an integer is greater than or equal to another integer -pub fn ge_128(a: u128, b: u128) -> bool { - le_128(b, a) -} - /// Adds two integers and returns the carry pub fn add_128(a: u128, b: u128) -> (bool, u128) { let (a_high, a_low): (u64, u64) = ::into(a); @@ -141,14 +15,27 @@ pub fn add_128(a: u128, b: u128) -> (bool, u128) { (carry_high, res) } -/// Adds the 128-bit integer with the 64-bit integer. Returns a tuple of the sum and the carry +/// Adds the 128-bit integer with the 64-bit integer and returns the carry pub fn add_128_64(a: u128, b: u64) -> (bool, u128) { let (a_high, a_low): (u64, u64) = ::into(a); let (carry_low, res_low): (bool, u64) = jet::add_64(a_low, b); let (carry_high, res_high): (bool, u64) = jet::full_add_64(carry_low, a_high, 0); - (carry_high, <(u64, u64)>::into((res_high, res_low))) + let res: u128 = <(u64, u64)>::into((res_high, res_low)); + (carry_high, res) +} + +/// Adds two integers. Takes a carry-in and returns a carry-out +pub fn full_add_128(carry_in: bool, a: u128, b: u128) -> (bool, u128) { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + let (carry_low, sum_low): (bool, u64) = jet::full_add_64(carry_in, a_low, b_low); + let (carry_out, sum_high): (bool, u64) = jet::full_add_64(carry_low, a_high, b_high); + + let res: u128 = <(u64, u64)>::into((sum_high, sum_low)); + (carry_out, res) } /// Returns the sum of two u128 values wrapped in Some, or None if the result overflows u128 @@ -178,6 +65,18 @@ pub fn sub_128(a: u128, b: u128) -> (bool, u128) { (borrow_high, res) } +/// Subtracts the second integer from the first integer, takes a borrow-in and returns a borrow-out +pub fn full_sub_128(borrow_in: bool, a: u128, b: u128) -> (bool, u128) { + let (a_high, a_low): (u64, u64) = ::into(a); + let (b_high, b_low): (u64, u64) = ::into(b); + + let (borrow_low, diff_low): (bool, u64) = jet::full_subtract_64(borrow_in, a_low, b_low); + let (borrow_out, diff_high): (bool, u64) = jet::full_subtract_64(borrow_low, a_high, b_high); + + let res: u128 = <(u64, u64)>::into((diff_high, diff_low)); + (borrow_out, res) +} + /// Returns the difference of two u128 values wrapped in Some, or None if the result overflows u128 pub fn checked_sub_128(a: u128, b: u128) -> Option { let (borrow, diff): (bool, u128) = sub_128(a, b); @@ -193,7 +92,7 @@ pub fn safe_sub_128(a: u128, b: u128) -> u128 { unwrap(checked_sub_128(a, b)) } -/// Multiply two integers. The output is a 256-bit integer +/// Multiplies two integers. The output is a 256-bit integer. /// The idea is that u128-bit `a` divides into 64-bit `a_high` and `a_low`, /// so a = a_high * 2^64 + a_low. /// In the same way, b = b_high * 2^64 + b_low. @@ -221,7 +120,7 @@ pub fn mul_128(a: u128, b: u128) -> u256 { // `word_3` is the upper half of a_high * b_high. It is at most `u64::MAX - 1` when // either factor is `u64::MAX`, and even in the extreme case where // a == b == u128::MAX, the total product still fits into u256. - // Therefore, word_3 + carry_3a + carry_3b can not overflow, and `add_64` + // Therefore, word_3 + carry_3a + carry_3b can not overflow, and `full_add_64` // is used instead of `safe_add_64` to avoid the unnecessary overflow check let (_, res_3a): (bool, u64) = jet::full_add_64(carry_3a, word_3, 0); let (_, res_3): (bool, u64) = jet::full_add_64(carry_3b, res_3a, 0); @@ -232,6 +131,27 @@ pub fn mul_128(a: u128, b: u128) -> u256 { <(u128, u128)>::into((res_3_2, res_1_0)) } +/// Multiplies two integers. The output is a 256-bit integer. +/// The idea is that u128-bit `a` divides into 64-bit `a_high` and `a_low`, +/// so a = a_high * 2^64 + a_low. +/// Therefore, a * b = 2^64 * a_high * b + a_low * b. +pub fn mul_128_64(a: u128, b: u64) -> u256 { + let (a_high, a_low): (u64, u64) = ::into(a); + + let highest: u128 = jet::multiply_64(a_high, b); + let lowest: u128 = jet::multiply_64(a_low, b); + + let (word_1, word_0): (u64, u64) = ::into(lowest); + let (word_3, word_2): (u64, u64) = ::into(highest); + + let (carry_2, res_1): (bool, u64) = jet::add_64(word_1, word_2); + // a * b fits into u192, so addition below can not overflow and `full_add_64` + // is used instead of `safe_add_64` to avoid the unnecessary overflow check + let (_, res_2): (bool, u64) = jet::full_add_64(carry_2, word_3, 0); + + <(u64, u64, u64, u64)>::into((0, res_2, res_1, word_0)) +} + /// Returns the product of two u128 values wrapped in Some, or None if the result overflows u128 pub fn checked_mul_128(a: u128, b: u128) -> Option { let result: u256 = mul_128(a, b); @@ -248,26 +168,12 @@ pub fn safe_mul_128(a: u128, b: u128) -> u128 { unwrap(checked_mul_128(a, b)) } -/// Splits the u256 integer into four u64 integers -// TODO: Move to u256 once that module is added. -pub fn split_256_into_64(a: u256) -> ((u64, u64), (u64, u64)) { - let (high, low): (u128, u128) = ::into(a); - - (::into(high), ::into(low)) -} - -/// Helper function, can be used with jet::div_mod_128_64. -/// Normalizes two u128 values by multiplying both by the same factor, -/// ensuring that the most significant non-zero word of `b` is at least 2^63. -/// -/// If `is_b_u128` is true, expects the upper half of `b` to be non-zero. -/// If `is_b_u128` is false, expects `b` to fit into u64. -/// -/// Division algorithms operate in base 2^64, so the normalization threshold is 2^63. -pub fn normalize_to_threshold(a: u128, b: u128, is_b_u128: bool) -> (u256, u128) { - // Compile-time constant: 2^63. Avoids a runtime jet::left_shift_64 call. +/// Helper function that can be used with jet::div_mod_128_64 or Algorithm D. +/// Returns the normalization factor by which `b` should be multiplied so that +/// its most significant non-zero word is greater than or equal to 2^63 +pub fn calculate_normalizer_base_64(b: u128, is_b_u128: bool) -> u64 { + // Compile-time constant: 2^63. Avoids a runtime jet::left_shift_64 call let threshold: u64 = 0x8000000000000000; - let (b_high, b_low): (u64, u64) = ::into(b); let b_highest_word: u64 = match is_b_u128 { @@ -281,18 +187,84 @@ pub fn normalize_to_threshold(a: u128, b: u128, is_b_u128: bool) -> (u256, u128) let (norm, remainder): (u64, u64) = jet::div_mod_64(threshold, b_highest_word); - let norm: u64 = match jet::is_zero_64(remainder) { + match jet::is_zero_64(remainder) { true => norm, false => { let (_, norm): (bool, u64) = jet::add_64(norm, 1); // norm <= 2^63, so norm + 1 can not overflow norm } + } +} + +/// Helper function, can be used with jet::div_mod_128_64 or Algorithm D. +/// Normalizes two u128 values by multiplying both by the same factor, +/// ensuring that the most significant non-zero word of `b` is at least 2^63. +/// +/// If `is_b_u128` is true, expects the upper half of `b` to be non-zero. +/// If `is_b_u128` is false, expects `b` to fit into u64. +/// +/// Division algorithms operate in base 2^64, so the normalization threshold is 2^63 +fn normalize_to_threshold_128_63(a: u128, b: u128, is_b_u128: bool) -> (u256, u128, u64) { + let norm: u64 = calculate_normalizer_base_64(b, is_b_u128); + let norm_128: u128 = <(u64, u64)>::into((0, norm)); + + match jet::eq_64(norm, 1) { + true => (<(u128, u128)>::into((0, a)), b, norm), + false => (mul_128(a, norm_128), safe_mul_128(b, norm_128), norm), + } +} + +/// Estimates and corrects the next quotient digit (q_hat) for Algorithm D. +/// Returns the quotient digit to use in the subsequent multiply-and-subtract step. +/// Expects result to fit into u64 +pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u64) -> u64 { + let (q_hat, r_hat, carry): (u64, u64, bool) = match jet::lt_64(u2, v1) { + true => { + let (q_hat, r_hat) : (u64, u64) =jet::div_mod_128_64(<(u64, u64)>::into((u2, u1)), v1); + (q_hat, r_hat, false) + }, + false => { + // This means u2 == v1, q_hat = 2^64, and r_hat = u1. + // Therefore, we need to decrement q and add v1 to r_hat. + // r_hat = u1 + v1 may overflow u64, which means that the estimate is exact. + let (carry, r_hat): (bool, u64) = jet::add_64(u1, v1); + + (jet::high_64(), r_hat, carry) + } }; - let norm: u128 = <(u64, u64)>::into((0, norm)); - match jet::lt_64(b_highest_word, threshold) { - true => (mul_128(a, norm), safe_mul_128(b, norm)), - false => (<(u128, u128)>::into((0, a)), b), + match carry { + true => q_hat, + false => { + let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); + + // correcting estimation: q_hat is off by at most 2. + match lt_128(r_hat_u0, jet::multiply_64(q_hat, v0)) { + true => { + // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 + let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); + let (carry, r_hat): (bool, u64) = jet::add_64(r_hat, v1); + + match carry { + true => q_hat, + false => { + let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); + + match lt_128(r_hat_u0, jet::multiply_64(q_hat, v0)) { + true => { + // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 + let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); + + q_hat + } + false => q_hat, + } + } + } + }, + false => q_hat, + } + } } } @@ -300,44 +272,14 @@ pub fn normalize_to_threshold(a: u128, b: u128, is_b_u128: bool) -> (u256, u128) /// returns the u64 quotient and the u128 remainder. /// Implements Algorithm D by Donald Knuth. /// Requires the upper half of the divisor to be non-zero. -pub fn algorithm_d(dividend: u128, divisor: u128) -> (u64, u128) { - let (norm_dividend, norm_divisor): (u256, u128) = normalize_to_threshold(dividend, divisor, true); +fn algorithm_d_128_128(dividend: u128, divisor: u128) -> (u64, u128) { + let (norm_dividend, norm_divisor, _): (u256, u128, u64) = normalize_to_threshold_128_63(dividend, divisor, true); // normalized dividend fits into 192 bits - let ((_, u2), (u1, u0)): ((u64, u64), (u64, u64)) = split_256_into_64(norm_dividend); + let (_, u2, u1, u0): (u64, u64, u64, u64) = ::into(norm_dividend); let (v1, v0): (u64, u64) = ::into(norm_divisor); - let (q_hat, r_hat): (u64, u64) = jet::div_mod_128_64(<(u64, u64)>::into((u2, u1)), v1); - - let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); - - // correcting estimation: q_hat is off by at most 2. - let q: u64 = match lt_128(r_hat_u0, jet::multiply_64(q_hat, v0)) { - true => { - // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 - let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); - let (carry, r_hat): (bool, u64) = jet::add_64(r_hat, v1); - - match carry { - true => q_hat, - false => { - let r_hat_u0: u128 = <(u64, u64)>::into((r_hat, u0)); - - match lt_128(r_hat_u0, jet::multiply_64(q_hat, v0)) { - true => { - // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 - let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); - - q_hat - } - false => q_hat, - } - } - - } - }, - false => q_hat, - }; + let q: u64 = estimate_quotient_digit_base_64(u2, u1, u0, v1, v0); let remainder: u128 = safe_sub_128(dividend, safe_mul_128(divisor, <(u64, u64)>::into((0, q)))); (q, remainder) @@ -355,16 +297,15 @@ pub fn div_mod_128_64(a: u128, b: u64) -> (u128, u64) { let a_prime: u128 = <(u64, u64)>::into((remainder, a_low)); // we need to normalize here, because jet::div_mod_128_64 only accepts b >= 2^63 - let (a_normalized, b_normalized): (u256, u128) = normalize_to_threshold(a_prime, <(u64, u64)>::into((0, b)), false); + let (a_normalized, b_normalized, norm): (u256, u128, u64) = normalize_to_threshold_128_63(a_prime, <(u64, u64)>::into((0, b)), false); // a_normalized fits into u128, because remainder < b and b_normalized fits into u64 let (_, a_normalized): (u128, u128) = ::into(a_normalized); let (_, b_normalized): (u64, u64) = ::into(b_normalized); // remainder < b, so (remainder * 2^64 + a_low) / b fits into u64 - let (q_low, _): (u64, u64) = jet::div_mod_128_64(a_normalized, b_normalized); // remainder is not valid here due to normalizing - - let (_, remainder): (u64, u64) = ::into(safe_sub_128(a_prime, jet::multiply_64(q_low, b))); + let (q_low, r_normalized): (u64, u64) = jet::div_mod_128_64(a_normalized, b_normalized); + let remainder: u64 = jet::divide_64(r_normalized, norm); (<(u64, u64)>::into((q_high, q_low)), remainder) } @@ -399,7 +340,7 @@ pub fn div_mod_128(a: u128, b: u128) -> (u128, u128) { (q, <(u64, u64)>::into((0, r))) }, false => { - let (q, r): (u64, u128) = algorithm_d(a, b); + let (q, r): (u64, u128) = algorithm_d_128_128(a, b); (<(u64, u64)>::into((0, q)), r) } } diff --git a/simf/lib/u16/convert.simf b/simf/lib/u16/convert.simf new file mode 100644 index 0000000..34b4b15 --- /dev/null +++ b/simf/lib/u16/convert.simf @@ -0,0 +1,54 @@ +/// Widening uint conversions + +/// Converts u16 to u32 +pub fn u16_to_u32(a: u16) -> u32 { + jet::left_pad_low_16_32(a) +} + +/// Converts u16 to u64 +pub fn u16_to_u64(a: u16) -> u64 { + jet::left_pad_low_16_64(a) +} + +/// Converts u16 to u128 +pub fn u16_to_u128(a: u16) -> u128 { + let a_u64: u64 = u16_to_u64(a); + + <(u64, u64)>::into((0, a_u64)) +} + +/// Converts u16 to u256 +pub fn u16_to_u256(a: u16) -> u256 { + let a_u128: u128 = u16_to_u128(a); + + <(u128, u128)>::into((0, a_u128)) +} + +/// Splitting uint conversions + +/// Splits u16 into two u8 +pub fn split_u16_into_u8(a: u16) -> (u8, u8) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u16 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u16_to_u1(a: u16) -> u1 { + let u1_max: u16 = jet::left_pad_low_1_16(jet::high_1()); + + assert!(jet::le_16(a, u1_max)); + + jet::rightmost_16_1(a) +} + +/// Converts u16 into u8. +/// Panics if the value does not fit in u8 +pub fn safe_u16_to_u8(a: u16) -> u8 { + let u8_max: u16 = jet::left_pad_low_8_16(jet::high_8()); + + assert!(jet::le_16(a, u8_max)); + + jet::rightmost_16_8(a) +} diff --git a/simf/lib/u16.simf b/simf/lib/u16/math.simf similarity index 100% rename from simf/lib/u16.simf rename to simf/lib/u16/math.simf diff --git a/simf/lib/u256/bit.simf b/simf/lib/u256/bit.simf new file mode 100644 index 0000000..2df63a7 --- /dev/null +++ b/simf/lib/u256/bit.simf @@ -0,0 +1,71 @@ +use crate::lib::u128::bit::{and_128, or_128, left_shift_128, right_shift_128}; + +/// Bit logic + +/// Bitwise AND of two 256-bit values +pub fn and_256(a: u256, b: u256) -> u256 { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + <(u128, u128)>::into((and_128(a_high, b_high), and_128(a_low, b_low))) +} + +/// Bitwise OR of two 256-bit values +pub fn or_256(a: u256, b: u256) -> u256 { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + <(u128, u128)>::into((or_128(a_high, b_high), or_128(a_low, b_low))) +} + +/// Left-shift a 256-bit value by the given amount. Bits are filled with zeroes +pub fn left_shift_256(shift: u8, a: u256) -> u256 { + match jet::is_zero_8(shift) { + true => a, + false => { + let (a_high, a_low): (u128, u128) = ::into(a); + + match jet::lt_8(shift, 128) { + true => { + let (_, low_to_high_amount): (bool, u8) = jet::subtract_8(128, shift); // shift < 128 + let shifted_bits: u128 = right_shift_128(low_to_high_amount, a_low); + + let res_high: u128 = or_128(left_shift_128(shift, a_high), shifted_bits); + + <(u128, u128)>::into((res_high, left_shift_128(shift, a_low))) + }, + false => { + let (_, shift): (bool, u8) = jet::subtract_8(shift, 128); // shift >= 128 + + <(u128, u128)>::into((left_shift_128(shift, a_low), 0)) + } + } + } + } +} + +/// Right-shift a 256-bit value by the given amount. Bits are filled with zeroes +pub fn right_shift_256(shift: u8, a: u256) -> u256 { + match jet::is_zero_8(shift) { + true => a, + false => { + let (a_high, a_low): (u128, u128) = ::into(a); + + match jet::lt_8(shift, 128) { + true => { + let (_, high_to_low_amount): (bool, u8) = jet::subtract_8(128, shift); // shift < 128 + let shifted_bits: u128 = left_shift_128(high_to_low_amount, a_high); + + let res_low: u128 = or_128(right_shift_128(shift, a_low), shifted_bits); + + <(u128, u128)>::into((right_shift_128(shift, a_high), res_low)) + }, + false => { + let (_, shift): (bool, u8) = jet::subtract_8(shift, 128); // shift >= 128 + + <(u128, u128)>::into((0, right_shift_128(shift, a_high))) + } + } + } + } +} diff --git a/simf/lib/u256/comparison.simf b/simf/lib/u256/comparison.simf new file mode 100644 index 0000000..9a5fbe0 --- /dev/null +++ b/simf/lib/u256/comparison.simf @@ -0,0 +1,50 @@ +use crate::lib::binary::and; +use crate::lib::u128::bit::eq_128; +use crate::lib::u128::comparison::{is_zero_128, lt_128, le_128}; + +/// Comparison operations + +/// Checks if an integer is zero +pub fn is_zero_256(a: u256) -> bool { + let (a_high, a_low): (u128, u128) = ::into(a); + + and(is_zero_128(a_high), is_zero_128(a_low)) +} + +/// Checks if an integer is less than another integer +pub fn lt_256(a: u256, b: u256) -> bool { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + match lt_128(a_high, b_high) { + true => true, + false => match eq_128(a_high, b_high) { + true => lt_128(a_low, b_low), + false => false, + }, + } +} + +/// Checks if an integer is less than or equal to another integer +pub fn le_256(a: u256, b: u256) -> bool { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + match lt_128(a_high, b_high) { + true => true, + false => match eq_128(a_high, b_high) { + true => le_128(a_low, b_low), + false => false, + }, + } +} + +/// Check if an integer is greater than another integer +pub fn gt_256(a: u256, b: u256) -> bool { + lt_256(b, a) +} + +/// Check if an integer is greater than or equal to another integer +pub fn ge_256(a: u256, b: u256) -> bool { + le_256(b, a) +} diff --git a/simf/lib/u256/convert.simf b/simf/lib/u256/convert.simf new file mode 100644 index 0000000..0e12ce1 --- /dev/null +++ b/simf/lib/u256/convert.simf @@ -0,0 +1,138 @@ +use crate::lib::u1::convert::u1_to_u256; +use crate::lib::u8::convert::u8_to_u256; +use crate::lib::u16::convert::u16_to_u256; +use crate::lib::u32::convert::u32_to_u256; +use crate::lib::u64::convert::u64_to_u256; +use crate::lib::u256::comparison::le_256; + +/// Splitting uint conversions + +/// Splits u256 into thirty two u8 +pub fn split_u256_into_u8( + a: u256 +) -> ( + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8 +) { + ::into(a) +} + +/// Splits u256 into sixteen u16 +pub fn split_u256_into_u16( + a: u256 +) -> (u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16, u16) { + ::into(a) +} + +/// Splits u256 into eight u32 +pub fn split_u256_into_u32(a: u256) -> (u32, u32, u32, u32, u32, u32, u32, u32) { + ::into(a) +} + +/// Splits u256 into four u64 +pub fn split_u256_into_u64(a: u256) -> (u64, u64, u64, u64) { + ::into(a) +} + +/// Splits u256 into two u128 +pub fn split_u256_into_u128(a: u256) -> (u128, u128) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u256 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u256_to_u1(a: u256) -> u1 { + let u1_max: u256 = u1_to_u256(jet::high_1()); + let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a); + + assert!(le_256(a, u1_max)); + + jet::rightmost_64_1(a_64) +} + +/// Converts u256 into u8. +/// Panics if the value does not fit in u8 +pub fn safe_u256_to_u8(a: u256) -> u8 { + let u8_max: u256 = u8_to_u256(jet::high_8()); + let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a); + + assert!(le_256(a, u8_max)); + + jet::rightmost_64_8(a_64) +} + +/// Converts u256 into u16. +/// Panics if the value does not fit in u16 +pub fn safe_u256_to_u16(a: u256) -> u16 { + let u16_max: u256 = u16_to_u256(jet::high_16()); + let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a); + + assert!(le_256(a, u16_max)); + + jet::rightmost_64_16(a_64) +} + +/// Converts u256 into u32. +/// Panics if the value does not fit in u32 +pub fn safe_u256_to_u32(a: u256) -> u32 { + let u32_max: u256 = u32_to_u256(jet::high_32()); + let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a); + + assert!(le_256(a, u32_max)); + + jet::rightmost_64_32(a_64) +} + +/// Converts u256 into u64. +/// Panics if the value does not fit in u64 +pub fn safe_u256_to_u64(a: u256) -> u64 { + let u64_max: u256 = u64_to_u256(jet::high_64()); + let (_, _, _, a_64): (u64, u64, u64, u64) = split_u256_into_u64(a); + + assert!(le_256(a, u64_max)); + + a_64 +} + +/// Converts u256 into u128. +/// Panics if the value does not fit in u128 +pub fn safe_u256_to_u128(a: u256) -> u128 { + let u128_max: u256 = 0x00000000000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + let (_, a_128): (u128, u128) = split_u256_into_u128(a); + + assert!(le_256(a, u128_max)); + + a_128 +} diff --git a/simf/lib/u256/math.simf b/simf/lib/u256/math.simf new file mode 100644 index 0000000..12a37f7 --- /dev/null +++ b/simf/lib/u256/math.simf @@ -0,0 +1,440 @@ +use crate::lib::binary::{not, and}; +use crate::lib::u128::math::{ + add_128, + full_add_128, + sub_128, + full_sub_128, + mul_128, + mul_128_64, + safe_mul_128, + calculate_normalizer_base_64, + estimate_quotient_digit_base_64, + div_mod_128, + div_mod_128_64 +}; +use crate::lib::u128::bit::eq_128; +use crate::lib::u128::comparison::{is_zero_128, lt_128}; + +use crate::lib::u256::convert::split_u256_into_u64; +use crate::lib::u256::comparison::{is_zero_256, lt_256}; + +/// 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 (carry_low, sum_low): (bool, u128) = add_128(a_low, b_low); + let (carry_high, sum_high): (bool, u128) = full_add_128(carry_low, a_high, b_high); + + let res: u256 = <(u128, u128)>::into((sum_high, sum_low)); + (carry_high, res) +} + +/// Adds the 256-bit integer with the 128-bit integer and returns the carry +pub fn add_256_128(a: u256, b: u128) -> (bool, u256) { + let b: u256 = <(u128, u128)>::into((0, b)); + + add_256(a, b) +} + +/// Returns the sum of two u256 values wrapped in Some, or None if the result overflows u256 +pub fn checked_add_256(a: u256, b: u256) -> Option { + let (carry, sum): (bool, u256) = add_256(a, b); + + match carry { + true => None, + false => Some(sum), + } +} + +/// Returns the sum of two u256 values, panics if the result overflows u256 +pub fn safe_add_256(a: u256, b: u256) -> u256 { + unwrap(checked_add_256(a, b)) +} + +/// Subtracts the second integer from the first integer, and returns the borrow bit +pub fn sub_256(a: u256, b: u256) -> (bool, u256) { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + let (borrow_low, diff_low): (bool, u128) = sub_128(a_low, b_low); + let (borrow_high, diff_high): (bool, u128) = full_sub_128(borrow_low, a_high, b_high); + + let res: u256 = <(u128, u128)>::into((diff_high, diff_low)); + (borrow_high, res) +} + +/// Returns the difference of two u256 values wrapped in Some, or None if the result overflows u256 +pub fn checked_sub_256(a: u256, b: u256) -> Option { + let (borrow, diff): (bool, u256) = sub_256(a, b); + + match borrow { + true => None, + false => Some(diff), + } +} + +/// Returns the difference of two u256 values, panics if the result overflows u256 +pub fn safe_sub_256(a: u256, b: u256) -> u256 { + unwrap(checked_sub_256(a, b)) +} + +/// Multiplies two integers. The output is two 256-bit integers +/// The idea is that u256-bit `a` divides into 128-bit `a_high` and `a_low`, +/// so a = a_high * 2^128 + a_low. +/// In the same way, b = b_high * 2^128 + b_low. +/// Therefore, a * b = 2^256 * a_high * b_high + 2^128(a_high * b_low + a_low * b_high) + a_low * b_low. +pub fn mul_256(a: u256, b: u256) -> (u256, u256) { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + let highest: u256 = mul_128(a_high, b_high); + let lowest: u256 = mul_128(a_low, b_low); + let (word_1, word_0): (u128, u128) = ::into(lowest); + let (word_3, word_2): (u128, u128) = ::into(highest); + + let product_1: u256 = mul_128(a_high, b_low); + let product_2: u256 = mul_128(b_high, a_low); + let (carry_3a, middle): (bool, u256) = add_256(product_1, product_2); + + let (middle_2, middle_1): (u128, u128) = ::into(middle); + + // fold the low-side carry directly into the word_2 addition via full_add_128, + // then propagate any resulting carry into word_3 + let (carry_2, res_1): (bool, u128) = add_128(word_1, middle_1); + let (carry_3b, res_2): (bool, u128) = full_add_128(carry_2, word_2, middle_2); + + // `word_3` is the upper half of a_high * b_high. It is at most `u128::MAX - 1` when + // either factor is `u128::MAX`, and even in the extreme case where + // a == b == u256::MAX, the total product still fits into two u256. + // Therefore, word_3 + carry_3a + carry_3b can not overflow, and `full_add_128` + // is used instead of `safe_add_128` to avoid the unnecessary overflow check + let (_, res_3a): (bool, u128) = full_add_128(carry_3a, word_3, 0); + let (_, res_3): (bool, u128) = full_add_128(carry_3b, res_3a, 0); + + let res_1_0: u256 = <(u128, u128)>::into((res_1, word_0)); + let res_3_2: u256 = <(u128, u128)>::into((res_3, res_2)); + + (res_3_2, res_1_0) +} + +/// Multiplies two integers. +/// The output consists of a 64-bit integer and a 256-bit integer. +/// The idea is that u256-bit `a` divides into 128-bit `a_high` and `a_low`, +/// so a = a_high * 2^128 + a_low, +/// a * b = 2^128 * a_high * b + a_low * b +pub fn mul_256_64(a: u256, b: u64) -> (u64, u256) { + let (a_high, a_low): (u128, u128) = ::into(a); + + let lowest: u256 = mul_128_64(a_low, b); + let highest: u256 = mul_128_64(a_high, b); + + let (word_1, word_0): (u128, u128) = ::into(lowest); + let (word_3, word_2): (u128, u128) = ::into(highest); + + let (_, word_3): (u64, u64) = ::into(word_3); + + let (carry_2, res_1): (bool, u128) = add_128(word_1, word_2); + // a * b fits into u320, so addition below can not overflow and + // `full_add_64` is used to avoid the unnecessary overflow check + let (_, res_2): (bool, u64) = jet::full_add_64(carry_2, word_3, 0); + + (res_2, <(u128, u128)>::into((res_1, word_0))) +} + +/// Multiplies two integers. +/// The output consists of a 128-bit integer and a 256-bit integer. +/// The idea is that u256-bit `a` divides into 128-bit `a_high` and `a_low`, +/// so a = a_high * 2^128 + a_low, +/// and a * b = 2^128 * a_high * b + a_low * b +pub fn mul_256_128(a: u256, b: u128) -> (u128, u256) { + let (a_high, a_low): (u128, u128) = ::into(a); + + let lowest: u256 = mul_128(a_low, b); + let highest: u256 = mul_128(a_high, b); + + let (word_1, word_0): (u128, u128) = ::into(lowest); + let (word_3, word_2): (u128, u128) = ::into(highest); + + let (carry_2, res_1): (bool, u128) = add_128(word_1, word_2); + // a * b fits into u384, so addition below can not overflow and + // `full_add_128` is used to avoid the unnecessary overflow check + let (_, res_2): (bool, u128) = full_add_128(carry_2, word_3, 0); + + (res_2, <(u128, u128)>::into((res_1, word_0))) +} + +/// Returns the product of two u256 values wrapped in Some, or None if the result overflows u256 +pub fn checked_mul_256(a: u256, b: u256) -> Option { + let (result_high, result_low): (u256, u256) = mul_256(a, b); + + match is_zero_256(result_high) { + true => Some(result_low), + false => None, + } +} + +/// Returns the product of two u256 values, panics if the result overflows u256 +pub fn safe_mul_256(a: u256, b: u256) -> u256 { + unwrap(checked_mul_256(a, b)) +} + +/// Returns the product of u256 and u128 values, panics if the result overflows u256 +fn safe_mul_256_128(a: u256, b: u128) -> u256 { + let (result_high, result_low): (u128, u256) = mul_256_128(a, b); + assert!(is_zero_128(result_high)); + + result_low +} + +/// Normalizes the dividend and divisor for Algorithm D by multiplying +/// both u256 and u128 by the same factor, ensuring that +/// the most significant non-zero word of `b` is at least 2^63. +/// +/// Returns the normalized `a` as a `(u64, u256)` pair, +/// the normalized `b` and the normalization factor. +fn normalize_to_threshold_256_63(a: u256, b: u128, is_b_u128: bool) -> (u64, u256, u128, u64) { + let norm: u64 = calculate_normalizer_base_64(b, is_b_u128); + let norm_128: u128 = <(u64, u64)>::into((0, norm)); + + match jet::eq_64(norm, 1) { + true => (0, a, b, norm), + false => { + let (high, low): (u64, u256) = mul_256_64(a, norm); + let b_norm: u128 = safe_mul_128(b, norm_128); + + (high, low, b_norm, norm) + }, + } +} + +/// Normalizes the dividend and divisor for Algorithm D by multiplying +/// both u256 values by the same factor, ensuring that +/// the most significant non-zero word of `b` is at least 2^127. +/// +/// The normalized dividend fits into 384 bits and is returned as a +/// `(u128, u256)` pair. The normalized divisor fits into a `u256`. +fn normalize_to_threshold_256_127(a: u256, b: u256) -> (u128, u256, u256) { + // Compile-time constant: 2^127 + let threshold: u128 = 0x80000000000000000000000000000000; + + let (b_high, _): (u128, u128) = ::into(b); + + assert!(not(is_zero_128(b_high))); + + let (norm, remainder): (u128, u128) = div_mod_128(threshold, b_high); + + let norm: u128 = match is_zero_128(remainder) { + true => norm, + false => { + let (_, norm): (bool, u128) = add_128(norm, 1); // norm <= 2^127, so norm + 1 can not overflow + norm + } + }; + + match lt_128(b_high, threshold) { + true => { + let (high, low): (u128, u256) = mul_256_128(a, norm); + + (high, low, safe_mul_256_128(b, norm)) + }, + false => (0, a, b), + } +} + +/// Divides the 256-bit integer by the 64-bit integer, +/// returns a tuple of the u256 quotient and the u64 remainder +pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { + let (u3, u2, u1, u0): (u64, u64, u64, u64) = split_u256_into_u64(dividend); + + // calculate the upper part of the quotient + let (q3, remainder): (u64, u64) = jet::div_mod_64(u3, divisor); + + let divisor: u128 = <(u64, u64)>::into((0, divisor)); + let dividend: u256 = <(u64, u64, u64, u64)>::into((remainder, u2, u1, u0)); + + // normalized dividend fits into 256 bits because remainder < divisor and + // divisor * norm fits into u64, so dividend * norm also fits + let (_, norm_dividend_low, norm_divisor, norm): (u64, u256, u128, u64) = + normalize_to_threshold_256_63(dividend, divisor, false); + + let (u3, u2, u1, u0): (u64, u64, u64, u64) = split_u256_into_u64(norm_dividend_low); + let (_, v0): (u64, u64) = ::into(norm_divisor); + + let (q2, remainder): (u64, u64) = jet::div_mod_128_64( <(u64, u64)>::into((u3, u2)), v0); + let (q1, remainder): (u64, u64) = jet::div_mod_128_64( <(u64, u64)>::into((remainder, u1)), v0); + let (q0, remainder): (u64, u64) = jet::div_mod_128_64( <(u64, u64)>::into((remainder, u0)), v0); + + let remainder: u64 = jet::divide_64(remainder, norm); + let q: u256 = <(u64, u64, u64, u64)>::into(( q3, q2, q1, q0)); + + (q, remainder) +} + +/// Multiplies the divisor by the quotient digit and subtracts the result from +/// the corresponding dividend limbs. Returns the updated dividend segment, +/// which is used by Algorithm D +fn mul_and_sub(q: u64, u2: u64, u1: u64, u0: u64, v: u128) -> (u64, u64) { + let u: u256 = <(u64, u64, u64, u64)>::into((0, u2, u1, u0)); + + let q_v: u256 = mul_128_64(v, q); + + let u_updated: u256 = safe_sub_256(u, q_v); + let (_, _, u1, u0): (u64, u64, u64, u64) = split_u256_into_u64(u_updated); + + (u1, u0) +} + +/// Divides the 256-bit integer by the 128-bit integer, +/// returns a tuple of the u256 quotient and the u128 remainder. +/// Implements Algorithm D by Donald Knuth. +/// Requires the upper half of the divisor to be non-zero (divisor >= 2^64) +fn algorithm_d_256_128(dividend: u256, divisor: u128) -> (u256, u128) { + let (u4, norm_dividend_low, norm_divisor, norm): (u64, u256, u128, u64) = + normalize_to_threshold_256_63(dividend, divisor, true); + + // normalized dividend fits into 320 bits + let (u3, u2, u1, u0): (u64, u64, u64, u64) = split_u256_into_u64(norm_dividend_low); + let (v1, v0): (u64, u64) = ::into(norm_divisor); + + let q2: u64 = estimate_quotient_digit_base_64(u4, u3, u2, v1, v0); + let (u3, u2): (u64, u64) = mul_and_sub(q2, u4, u3, u2, norm_divisor); + + let q1: u64 = estimate_quotient_digit_base_64(u3, u2, u1, v1, v0); + let (u2, u1): (u64, u64) = mul_and_sub(q1, u3, u2, u1, norm_divisor); + + let q0: u64 = estimate_quotient_digit_base_64(u2, u1, u0, v1, v0); + let (u1, u0): (u64, u64) = mul_and_sub(q0, u2, u1, u0, norm_divisor); + + let q: u256 = <(u64, u64, u64, u64)>::into((0, q2, q1, q0)); + let (remainder, _): (u128, u64) = div_mod_128_64(<(u64, u64)>::into((u1, u0)), norm); + + (q, remainder) +} + +/// Divides the 256-bit integer by the 128-bit integer, +/// returns a tuple of the u256 quotient and the u128 remainder +pub fn div_mod_256_128(dividend: u256, divisor: u128) -> (u256, u128) { + let (divisor_high, divisor_low): (u64, u64) = ::into(divisor); + + match jet::is_zero_64(divisor_high) { + true => { + let (q, r): (u256, u64) = div_mod_256_64(dividend, divisor_low); + + (q, <(u64, u64)>::into((0, r))) + }, + false => algorithm_d_256_128(dividend, divisor), + } +} + +/// Divides the first u256 integer by the second u256 integer, +/// returns the u128 quotient and the u256 remainder. +/// Implements Algorithm D by Donald Knuth. +/// Requires the upper half of the divisor to be non-zero (divisor >= 2^128). +fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { + let (u2, norm_dividend_low, norm_divisor): (u128, u256, u256) = normalize_to_threshold_256_127(dividend, divisor); + + let (u1, u0): (u128, u128) = ::into(norm_dividend_low); + let (v1, v0): (u128, u128) = ::into(norm_divisor); + + let (q_hat, r_hat): (u256, u128) = algorithm_d_256_128(<(u128, u128)>::into((u2, u1)), v1); + let( _, q_hat): (u128, u128) = ::into(q_hat); + + let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); + let u_hat: u256 = mul_128(q_hat, v0); + + // correcting estimation: q_hat is off by at most 2. + let q: u128 = match lt_256(r_hat_u0, u_hat) { + true => { + // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 + let (_, q_hat): (bool, u128) = sub_128(q_hat, 1); + let (carry, r_hat): (bool, u128) = add_128(r_hat, v1); + + match carry { + true => q_hat, + false => { + let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); + let (_, u_hat): (bool, u256) = sub_256(u_hat, <(u128, u128)>::into((0, v0))); + + match lt_256(r_hat_u0, u_hat) { + true => { + // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 + let (_, q_hat): (bool, u128) = sub_128(q_hat, 1); + + q_hat + } + false => q_hat, + } + } + } + }, + false => q_hat, + }; + + let remainder: u256 = safe_sub_256(dividend, safe_mul_256_128(divisor, q)); + (q, remainder) +} + +/// Divides the first integer by the second integer, +/// returns the quotient and the remainder +pub fn div_mod_256(a: u256, b: u256) -> (u256, u256) { + let (a_high, a_low): (u128, u128) = ::into(a); + let (b_high, b_low): (u128, u128) = ::into(b); + + match lt_256(a, b) { + true => (0, a), + false => { + match and(is_zero_128(a_high), is_zero_128(b_high)) { + true => { + // if both a_high and b_high are zero, this narrows down to 128-bit division + let (q, r): (u128, u128) = div_mod_128(a_low, b_low); + (<(u128, u128)>::into((0, q)), <(u128, u128)>::into((0, r))) + }, + false => { + match eq_128(a_high, b_high) { + true => { + // safe: !lt_256(a, b) and a_high == b_high, so a_low >= b_low, + // and the subtraction can not underflow + let (_, diff): (bool, u128) = sub_128(a_low, b_low); + (1, <(u128, u128)>::into((0, diff))) + }, + false => { + match is_zero_128(b_high) { + true => { + let (q, r): (u256, u128) = div_mod_256_128(a, b_low); + (q, <(u128, u128)>::into((0, r))) + }, + false => { + let (q, r): (u128, u256) = algorithm_d_256_256(a, b); + (<(u128, u128)>::into((0, q)), r) + } + } + } + } + } + } + } + } +} + +/// Divide the first integer by the second integer, returns the quotient +pub fn div_256(a: u256, b: u256) -> u256 { + let (q, _): (u256, u256) = div_mod_256(a, b); + + q +} + +/// Returns the quotient of two u256 values wrapped in Some, or None if the result overflows u256 +pub fn checked_div_256(a: u256, b: u256) -> Option { + match is_zero_256(b) { + true => None, + false => Some(div_256(a, b)), + } +} + +/// Returns the quotient of two u256 values, panics if the result overflows u256 +pub fn safe_div_256(a: u256, b: u256) -> u256 { + unwrap(checked_div_256(a, b)) +} diff --git a/simf/lib/u32/convert.simf b/simf/lib/u32/convert.simf new file mode 100644 index 0000000..4145570 --- /dev/null +++ b/simf/lib/u32/convert.simf @@ -0,0 +1,64 @@ +/// Widening uint conversions + +/// Converts u32 to u64 +pub fn u32_to_u64(a: u32) -> u64 { + jet::left_pad_low_32_64(a) +} + +/// Converts u32 to u128 +pub fn u32_to_u128(a: u32) -> u128 { + let a_u64: u64 = u32_to_u64(a); + + <(u64, u64)>::into((0, a_u64)) +} + +/// Converts u32 to u256 +pub fn u32_to_u256(a: u32) -> u256 { + let a_u128: u128 = u32_to_u128(a); + + <(u128, u128)>::into((0, a_u128)) +} + +/// Splitting uint conversions + +/// Splits u32 into four u8 +pub fn split_u32_into_u8(a: u32) -> (u8, u8, u8, u8) { + ::into(a) +} + +/// Splits u32 into two u16 +pub fn split_u32_into_u16(a: u32) -> (u16, u16) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u32 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u32_to_u1(a: u32) -> u1 { + let u1_max: u32 = jet::left_pad_low_1_32(jet::high_1()); + + assert!(jet::le_32(a, u1_max)); + + jet::rightmost_32_1(a) +} + +/// Converts u32 into u8. +/// Panics if the value does not fit in u8 +pub fn safe_u32_to_u8(a: u32) -> u8 { + let u8_max: u32 = jet::left_pad_low_8_32(jet::high_8()); + + assert!(jet::le_32(a, u8_max)); + + jet::rightmost_32_8(a) +} + +/// Converts u32 into u16. +/// Panics if the value does not fit in u16 +pub fn safe_u32_to_u16(a: u32) -> u16 { + let u16_max: u32 = jet::left_pad_low_16_32(jet::high_16()); + + assert!(jet::le_32(a, u16_max)); + + jet::rightmost_32_16(a) +} diff --git a/simf/lib/u32.simf b/simf/lib/u32/math.simf similarity index 100% rename from simf/lib/u32.simf rename to simf/lib/u32/math.simf diff --git a/simf/lib/u64/convert.simf b/simf/lib/u64/convert.simf new file mode 100644 index 0000000..8a7d985 --- /dev/null +++ b/simf/lib/u64/convert.simf @@ -0,0 +1,72 @@ +/// Widening uint conversions + +/// Converts u64 to u128 +pub fn u64_to_u128(a: u64) -> u128 { + <(u64, u64)>::into((0, a)) +} + +/// Converts u64 to u256 +pub fn u64_to_u256(a: u64) -> u256 { + let a_u128: u128 = u64_to_u128(a); + + <(u128, u128)>::into((0, a_u128)) +} + +/// Splitting uint conversions + +/// Splits u64 into eight u8 +pub fn split_u64_into_u8(a: u64) -> (u8, u8, u8, u8, u8, u8, u8, u8) { + ::into(a) +} + +/// Splits u64 into four u16 +pub fn split_u64_into_u16(a: u64) -> (u16, u16, u16, u16) { + ::into(a) +} + +/// Splits u64 into two u32 +pub fn split_u64_into_u32(a: u64) -> (u32, u32) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u64 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u64_to_u1(a: u64) -> u1 { + let u1_max: u64 = jet::left_pad_low_1_64(jet::high_1()); + + assert!(jet::le_64(a, u1_max)); + + jet::rightmost_64_1(a) +} + +/// Converts u64 into u8. +/// Panics if the value does not fit in u8 +pub fn safe_u64_to_u8(a: u64) -> u8 { + let u8_max: u64 = jet::left_pad_low_8_64(jet::high_8()); + + assert!(jet::le_64(a, u8_max)); + + jet::rightmost_64_8(a) +} + +/// Converts u64 into u16. +/// Panics if the value does not fit in u16 +pub fn safe_u64_to_u16(a: u64) -> u16 { + let u16_max: u64 = jet::left_pad_low_16_64(jet::high_16()); + + assert!(jet::le_64(a, u16_max)); + + jet::rightmost_64_16(a) +} + +/// Converts u64 into u32. +/// Panics if the value does not fit in u32 +pub fn safe_u64_to_u32(a: u64) -> u32 { + let u32_max: u64 = jet::left_pad_low_32_64(jet::high_32()); + + assert!(jet::le_64(a, u32_max)); + + jet::rightmost_64_32(a) +} diff --git a/simf/lib/u64.simf b/simf/lib/u64/math.simf similarity index 94% rename from simf/lib/u64.simf rename to simf/lib/u64/math.simf index f3fd3da..77fc4aa 100644 --- a/simf/lib/u64.simf +++ b/simf/lib/u64/math.simf @@ -67,8 +67,3 @@ pub fn gt_64(a: u64, b: u64) -> bool { pub fn ge_64(a: u64, b: u64) -> bool { jet::le_64(b, a) } - -/// Widen `u64` to a `u256` scalar. -pub fn u64_into_u256(val: u64) -> u256 { - <(u64, u64, u64, u64)>::into((0, 0, 0, val)) -} diff --git a/simf/lib/u8/convert.simf b/simf/lib/u8/convert.simf new file mode 100644 index 0000000..bfe9bc4 --- /dev/null +++ b/simf/lib/u8/convert.simf @@ -0,0 +1,49 @@ +/// Widening uint conversions + +/// Converts u8 to u16 +pub fn u8_to_u16(a: u8) -> u16 { + jet::left_pad_low_8_16(a) +} + +/// Converts u8 to u32 +pub fn u8_to_u32(a: u8) -> u32 { + jet::left_pad_low_8_32(a) +} + +/// Converts u8 to u64 +pub fn u8_to_u64(a: u8) -> u64 { + jet::left_pad_low_8_64(a) +} + +/// Converts u8 to u128 +pub fn u8_to_u128(a: u8) -> u128 { + let a_u64: u64 = u8_to_u64(a); + + <(u64, u64)>::into((0, a_u64)) +} + +/// Converts u8 to u256 +pub fn u8_to_u256(a: u8) -> u256 { + let a_u128: u128 = u8_to_u128(a); + + <(u128, u128)>::into((0, a_u128)) +} + +/// Splitting uint conversions + +/// Splits u8 into eight u1 +pub fn split_u8_into_u1(a: u8) -> (u1, u1, u1, u1, u1, u1, u1, u1) { + ::into(a) +} + +/// Narrowing uint conversions + +/// Converts u8 into u1. +/// Panics if the value does not fit in u1 +pub fn safe_u8_to_u1(a: u8) -> u1 { + let u1_max: u8 = jet::left_pad_low_1_8(jet::high_1()); + + assert!(jet::le_8(a, u1_max)); + + jet::rightmost_8_1(a) +} diff --git a/simf/lib/u8.simf b/simf/lib/u8/math.simf similarity index 100% rename from simf/lib/u8.simf rename to simf/lib/u8/math.simf diff --git a/simf/u128_test_arithmetic.simf b/simf/u128_basic_math_test.simf similarity index 54% rename from simf/u128_test_arithmetic.simf rename to simf/u128_basic_math_test.simf index a95ee18..17d5992 100644 --- a/simf/u128_test_arithmetic.simf +++ b/simf/u128_basic_math_test.simf @@ -1,4 +1,18 @@ -use crate::lib::u128::{ eq_128, is_zero_128, lt_128, le_128, add_128, add_128_64, sub_128, mul_128, split_256_into_64, normalize_to_threshold, algorithm_d, div_mod_128_64, div_mod_128, div_128 }; +use crate::lib::u128::math::{ + add_128, + add_128_64, + full_add_128, + sub_128, + full_sub_128, + mul_128, + mul_128_64, + calculate_normalizer_base_64, + estimate_quotient_digit_base_64, + div_mod_128_64, + div_mod_128, + div_128 +}; +use crate::lib::u128::bit::eq_128; use crate::helper::{if_test_this_function, assert_bool}; /// Asserts a result equals expected u128 and bool values. @@ -7,7 +21,7 @@ fn assert_eq_uint_bool(result: (bool, u128), expected: u128, expected_bool: bool let (bool_res, uint_res): (bool, u128) = result; assert_bool(bool_res, expected_bool); - + assert!(eq_128(uint_res, expected)); } @@ -20,72 +34,68 @@ fn main() { let expected_bool: bool = witness::EXPECTED_BOOL; let second_expected: u128 = witness::SECOND_EXPECTED; // for cases where the result is a 256 bit value - let third_expected: u128 = witness::THIRD_EXPECTED; // for cases where the result is 256 bit + 128 bit values /// Arithmetic - match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_128(a), expected_bool); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_128(a, b), expected_bool); }, false => (), }; - match if_test_this_function(2, fn_idx) { true => { assert_bool(le_128(a, b), expected_bool); }, false => (), }; + match if_test_this_function(0, fn_idx) { true => { assert_eq_uint_bool(add_128(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); assert_eq_uint_bool(add_128_64(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(2, fn_idx) { true => { assert_eq_uint_bool(full_add_128(eq_128(second_expected, 1), a, b), unwrap(expected), expected_bool); }, false => (), }; - match if_test_this_function(3, fn_idx) { true => { assert_eq_uint_bool(add_128(a, b), unwrap(expected), expected_bool); }, false => (), }; - match if_test_this_function(4, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); assert_eq_uint_bool(add_128_64(a, b), unwrap(expected), expected_bool); }, false => (), }; - match if_test_this_function(5, fn_idx) { true => { assert_eq_uint_bool(sub_128(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => { assert_eq_uint_bool(sub_128(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(4, fn_idx) { true => { assert_eq_uint_bool(full_sub_128(eq_128(second_expected, 1), a, b), unwrap(expected), expected_bool); }, false => (), }; - match if_test_this_function(6, fn_idx) { + match if_test_this_function(5, fn_idx) { true => { let result: u256 = mul_128(a, b); let (result_high, result_low): (u128, u128) = ::into(result); - + assert!(eq_128(result_high, unwrap(expected))); assert!(eq_128(result_low, second_expected)); }, false => (), }; - match if_test_this_function(7, fn_idx) { + match if_test_this_function(6, fn_idx) { true => { - let input: u256 = <(u128, u128)>::into((a, b)); - let ((res1, res2), (res3, res4)): ((u64, u64), (u64, u64)) = split_256_into_64(input); - - let (expected1, expected2): (u64, u64) = ::into(unwrap(expected)); - let (expected3, expected4): (u64, u64) = ::into(second_expected); - - assert!(jet::eq_64(res1, expected1)); - assert!(jet::eq_64(res2, expected2)); - assert!(jet::eq_64(res3, expected3)); - assert!(jet::eq_64(res4, expected4)); + let (_, b): (u64, u64) = ::into(b); + let result: u256 = mul_128_64(a, b); + + let (result_high, result_low): (u128, u128) = ::into(result); + + assert!(eq_128(result_high, unwrap(expected))); + assert!(eq_128(result_low, second_expected)); }, false => (), }; - match if_test_this_function(8, fn_idx) { + match if_test_this_function(7, fn_idx) { true => { - let (result_a, result_b): (u256, u128) = normalize_to_threshold(a, b, expected_bool); + let norm: u64 = calculate_normalizer_base_64(b, expected_bool); - let (result_a_high, result_a_low): (u128, u128) = ::into(result_a); + let (_, expected_norm): (u64, u64) = ::into(unwrap(expected)); - assert!(eq_128(result_a_high, unwrap(expected))); - assert!(eq_128(result_a_low, second_expected)); - assert!(eq_128(result_b, third_expected)); + assert!(jet::eq_64(norm, expected_norm)); }, false => (), }; - match if_test_this_function(9, fn_idx) { + match if_test_this_function(8, fn_idx) { true => { - let (_, expected_q): (u64, u64) = ::into(unwrap(expected)); + let (_, u2): (u64, u64) = ::into(a); + let (u1, u0): (u64, u64) = ::into(b); + let (v1, v0): (u64, u64) = ::into(second_expected); - let (q, r): (u64, u128) = algorithm_d(a, b); + let q: u64 = estimate_quotient_digit_base_64(u2, u1, u0, v1, v0); + + let (_, expected_q): (u64, u64) = ::into(unwrap(expected)); assert!(jet::eq_64(q, expected_q)); - assert!(eq_128(r, second_expected)); }, false => (), }; - match if_test_this_function(10, fn_idx) { + match if_test_this_function(9, fn_idx) { true => { let (_, b): (u64, u64) = ::into(b); let (_, expected_r): (u64, u64) = ::into(second_expected); @@ -98,7 +108,7 @@ fn main() { false => (), }; - match if_test_this_function(11, fn_idx) { + match if_test_this_function(10, fn_idx) { true => { let (q, r): (u128, u128) = div_mod_128(a, b); @@ -108,5 +118,5 @@ fn main() { false => (), }; - match if_test_this_function(12, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(11, fn_idx) { true => { assert!(eq_128(div_128(a, b), unwrap(expected))); }, false => (), }; } diff --git a/simf/u128_convert_test.simf b/simf/u128_convert_test.simf new file mode 100644 index 0000000..0d70dcf --- /dev/null +++ b/simf/u128_convert_test.simf @@ -0,0 +1,240 @@ +use crate::lib::u128::convert::{ + u128_to_u256, + split_u128_into_u8, + split_u128_into_u16, + split_u128_into_u32, + split_u128_into_u64, + safe_u128_to_u1, + safe_u128_to_u8, + safe_u128_to_u16, + safe_u128_to_u32, + safe_u128_to_u64 +}; +use crate::lib::asserts::{ + assert_eq_1, + assert_eq_8, + assert_eq_16, + assert_eq_32, + assert_eq_64, + assert_eq_256 +}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u128 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_256(u128_to_u256(a), expected); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + let ( + a15, + a14, + a13, + a12, + a11, + a10, + a9, + a8, + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): ( + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8 + ) = split_u128_into_u8(a); + + let (_, _, expected1, expected0): (u64, u64, u64, u64) = ::into(expected); + let (expected3, expected2): (u32, u32) = ::into(expected1); + let (expected1, expected0): (u32, u32) = ::into(expected0); + + let ( + expected15, + expected14, + expected13, + expected12 + ): (u8, u8, u8, u8) = ::into(expected3); + let ( + expected11, + expected10, + expected9, + expected8 + ): (u8, u8, u8, u8) = ::into(expected2); + let ( + expected7, + expected6, + expected5, + expected4 + ): (u8, u8, u8, u8) = ::into(expected1); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u8, u8, u8, u8) = ::into(expected0); + + assert_eq_8(a15, expected15); + assert_eq_8(a14, expected14); + assert_eq_8(a13, expected13); + assert_eq_8(a12, expected12); + assert_eq_8(a11, expected11); + assert_eq_8(a10, expected10); + assert_eq_8(a9, expected9); + assert_eq_8(a8, expected8); + assert_eq_8(a7, expected7); + assert_eq_8(a6, expected6); + assert_eq_8(a5, expected5); + assert_eq_8(a4, expected4); + assert_eq_8(a3, expected3); + assert_eq_8(a2, expected2); + assert_eq_8(a1, expected1); + assert_eq_8(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + let ( + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): (u16, u16, u16, u16, u16, u16, u16, u16) = split_u128_into_u16(a); + + let (_, _, expected1, expected0): (u64, u64, u64, u64) = ::into(expected); + + let ( + expected7, + expected6, + expected5, + expected4 + ): (u16, u16, u16, u16) = ::into(expected1); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u16, u16, u16, u16) = ::into(expected0); + + assert_eq_16(a7, expected7); + assert_eq_16(a6, expected6); + assert_eq_16(a5, expected5); + assert_eq_16(a4, expected4); + assert_eq_16(a3, expected3); + assert_eq_16(a2, expected2); + assert_eq_16(a1, expected1); + assert_eq_16(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (a3, a2, a1, a0): (u32, u32, u32, u32) = split_u128_into_u32(a); + + let (_, _, expected1, expected0): (u64, u64, u64, u64) = ::into(expected); + let (expected3, expected2): (u32, u32) = ::into(expected1); + let (expected1, expected0): (u32, u32) = ::into(expected0); + + assert_eq_32(a3, expected3); + assert_eq_32(a2, expected2); + assert_eq_32(a1, expected1); + assert_eq_32(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (a1, a0): (u64, u64) = split_u128_into_u64(a); + + let (_, _, expected1, expected0): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(a1, expected1); + assert_eq_64(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u128_to_u1(a), expected); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + + assert_eq_8(safe_u128_to_u8(a), expected); + }, + false => {}, + }; + + match if_test_this_function(7, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u16 = jet::rightmost_64_16(expected); + + assert_eq_16(safe_u128_to_u16(a), expected); + }, + false => {}, + }; + + match if_test_this_function(8, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u32 = jet::rightmost_64_32(expected); + + assert_eq_32(safe_u128_to_u32(a), expected); + }, + false => {}, + }; + + match if_test_this_function(9, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(safe_u128_to_u64(a), expected); + }, + false => {}, + }; +} diff --git a/simf/u128_test.simf b/simf/u128_math_test.simf similarity index 71% rename from simf/u128_test.simf rename to simf/u128_math_test.simf index cbaeedf..1c73009 100644 --- a/simf/u128_test.simf +++ b/simf/u128_math_test.simf @@ -1,4 +1,14 @@ -use crate::lib::u128::{ checked_add_128, safe_add_128, checked_sub_128, safe_sub_128, checked_mul_128, safe_mul_128, checked_div_128, safe_div_128, eq_128, gt_128, ge_128 }; +use crate::lib::u128::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; @@ -8,8 +18,8 @@ use crate::helper::if_test_this_function; /// witness value carries both, removing the need for a separate overflow flag. fn assert_eq_opt(result: Option, expected: Option) { match expected { - None => assert_none_128(result), Some(e: u128) => assert_eq_128(unwrap(result), e), + None => assert_none_128(result), } } @@ -33,19 +43,19 @@ fn main() { // add match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_128(a, b), expected); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { assert!(eq_128(safe_add_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => {assert_eq_128(safe_add_128(a, b), unwrap(expected)); }, false => (), }; // sub match if_test_this_function(2, fn_idx) { true => { assert_eq_opt(checked_sub_128(a, b), expected); }, false => (), }; - match if_test_this_function(3, fn_idx) { true => { assert!(eq_128(safe_sub_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(3, fn_idx) { true => {assert_eq_128(safe_sub_128(a, b), unwrap(expected)); }, false => (), }; // mul match if_test_this_function(4, fn_idx) { true => { assert_eq_opt(checked_mul_128(a, b), expected); }, false => (), }; - match if_test_this_function(5, fn_idx) { true => { assert!(eq_128(safe_mul_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => {assert_eq_128(safe_mul_128(a, b), unwrap(expected)); }, false => (), }; // div match if_test_this_function(6, fn_idx) { true => { assert_eq_opt(checked_div_128(a, b), expected); }, false => (), }; - match if_test_this_function(7, fn_idx) { true => { assert!(eq_128(safe_div_128(a, b), unwrap(expected))); }, false => (), }; + match if_test_this_function(7, fn_idx) { true => {assert_eq_128(safe_div_128(a, b), unwrap(expected)); }, false => (), }; // gt, ge match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_128(a, b), expected); }, false => (), }; diff --git a/simf/u128_test_bits.simf b/simf/u128_test_bits.simf index 725cad9..b3ea239 100644 --- a/simf/u128_test_bits.simf +++ b/simf/u128_test_bits.simf @@ -1,4 +1,4 @@ -use crate::lib::u128::{ and_128, or_128, eq_128, left_shift_128, right_shift_128 }; +use crate::lib::u128::bit::{and_128, or_128, eq_128, left_shift_128, right_shift_128}; use crate::helper::{if_test_this_function, assert_bool}; fn main() { @@ -9,7 +9,7 @@ fn main() { let expected: Option = witness::EXPECTED; let expected_bool: bool = witness::EXPECTED_BOOL; - + /// Bit logic match if_test_this_function(0, fn_idx) { true => { assert!(eq_128(and_128(a, b), unwrap(expected))); }, false => (), }; diff --git a/simf/u128_test_compare.simf b/simf/u128_test_compare.simf new file mode 100644 index 0000000..e9badb1 --- /dev/null +++ b/simf/u128_test_compare.simf @@ -0,0 +1,20 @@ +use crate::lib::u128::comparison::{ + is_zero_128, + lt_128, + le_128, +}; +use crate::helper::{if_test_this_function, assert_bool}; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u128 = witness::FIRST_ARG; + let b: u128 = witness::SECOND_ARG; + let expected_bool: bool = witness::EXPECTED_BOOL; + + /// Comparison operations + + match if_test_this_function(0, fn_idx) { true => { assert_bool(is_zero_128(a), expected_bool); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert_bool(lt_128(a, b), expected_bool); }, false => (), }; + match if_test_this_function(2, fn_idx) { true => { assert_bool(le_128(a, b), expected_bool); }, false => (), }; +} diff --git a/simf/u16_convert_test.simf b/simf/u16_convert_test.simf new file mode 100644 index 0000000..847d4f3 --- /dev/null +++ b/simf/u16_convert_test.simf @@ -0,0 +1,96 @@ +use crate::lib::u16::convert::{ + u16_to_u32, + u16_to_u64, + u16_to_u128, + u16_to_u256, + split_u16_into_u8, + 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, + assert_eq_32, + assert_eq_64, + assert_eq_128, + assert_eq_256 +}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u16 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, expected): (u32, u32) = ::into(expected); + + assert_eq_32(u16_to_u32(a), expected); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(u16_to_u64(a), expected); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert_eq_128(u16_to_u128(a), expected); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + assert_eq_256(u16_to_u256(a), expected); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (a1, a0): (u8, u8) = split_u16_into_u8(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u16 = jet::rightmost_64_16(expected); + let (expected1, expected0): (u8, u8) = ::into(expected); + + assert_eq_8(a1, expected1); + assert_eq_8(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u16_to_u1(a), expected); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + + assert_eq_8(safe_u16_to_u8(a), expected); + }, + false => {}, + }; +} diff --git a/simf/u16_test.simf b/simf/u16_math_test.simf similarity index 93% rename from simf/u16_test.simf rename to simf/u16_math_test.simf index 43cb52e..bc7aece 100644 --- a/simf/u16_test.simf +++ b/simf/u16_math_test.simf @@ -1,4 +1,4 @@ -use crate::lib::u16::{checked_add_16, safe_add_16, checked_sub_16, safe_sub_16, checked_mul_16, safe_mul_16, checked_div_16, safe_div_16, gt_16, ge_16}; +use crate::lib::u16::math::{checked_add_16, safe_add_16, checked_sub_16, safe_sub_16, checked_mul_16, safe_mul_16, checked_div_16, safe_div_16, gt_16, ge_16}; use crate::lib::asserts::{assert_none_16, assert_eq_16}; use crate::lib::binary::not; use crate::helper::if_test_this_function; diff --git a/simf/u1_convert_test.simf b/simf/u1_convert_test.simf new file mode 100644 index 0000000..8263efc --- /dev/null +++ b/simf/u1_convert_test.simf @@ -0,0 +1,92 @@ +use crate::lib::u1::convert::{ + u1_to_u8, + u1_to_u16, + u1_to_u32, + u1_to_u64, + u1_to_u128, + u1_to_u256, + u1_to_bool +}; +use crate::lib::u128::bit::eq_128; +use crate::lib::asserts::{ + assert_eq_bool, + assert_eq_8, + assert_eq_16, + assert_eq_32, + assert_eq_64, + assert_eq_128, + assert_eq_256 +}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u1 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); + let (_, expected): (u8, u8) = ::into(expected); + + assert_eq_8(u1_to_u8(a), expected); + }, + false => (), + }; + + match if_test_this_function(1, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); + + assert_eq_16(u1_to_u16(a), expected); + }, + false => (), + }; + + match if_test_this_function(2, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, expected): (u32, u32) = ::into(expected); + + assert_eq_32(u1_to_u32(a), expected); + }, + false => (), + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(u1_to_u64(a), expected); + }, + false => (), + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert_eq_128(u1_to_u128(a), expected); + }, + false => (), + }; + + match if_test_this_function(5, fn_idx) { + true => { + assert_eq_256(u1_to_u256(a), expected); + }, + false => (), + }; + + match if_test_this_function(6, fn_idx) { + true => { + let expected_bool: bool = jet::eq_256(expected, 1); + assert_eq_bool(u1_to_bool(a), expected_bool); + }, + false => (), + }; +} diff --git a/simf/u256_convert_test.simf b/simf/u256_convert_test.simf new file mode 100644 index 0000000..fe659dd --- /dev/null +++ b/simf/u256_convert_test.simf @@ -0,0 +1,406 @@ +use crate::lib::u256::convert::{ + split_u256_into_u8, + split_u256_into_u16, + split_u256_into_u32, + split_u256_into_u64, + split_u256_into_u128, + safe_u256_to_u1, + safe_u256_to_u8, + safe_u256_to_u16, + safe_u256_to_u32, + safe_u256_to_u64, + safe_u256_to_u128 +}; +use crate::lib::asserts::{ + assert_eq_1, + assert_eq_8, + assert_eq_16, + assert_eq_32, + assert_eq_64, + assert_eq_128 +}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let ( + a31, + a30, + a29, + a28, + a27, + a26, + a25, + a24, + a23, + a22, + a21, + a20, + a19, + a18, + a17, + a16, + a15, + a14, + a13, + a12, + a11, + a10, + a9, + a8, + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): ( + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8, + u8 + ) = split_u256_into_u8(a); + + let ( + expected3, + expected2, + expected1, + expected0 + ): (u64, u64, u64, u64) = ::into(expected); + + let ( + expected31, + expected30, + expected29, + expected28, + expected27, + expected26, + expected25, + expected24 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = ::into(expected3); + + let ( + expected23, + expected22, + expected21, + expected20, + expected19, + expected18, + expected17, + expected16 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = ::into(expected2); + + let ( + expected15, + expected14, + expected13, + expected12, + expected11, + expected10, + expected9, + expected8 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = ::into(expected1); + + let ( + expected7, + expected6, + expected5, + expected4, + expected3, + expected2, + expected1, + expected0 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = ::into(expected0); + + assert_eq_8(a31, expected31); + assert_eq_8(a30, expected30); + assert_eq_8(a29, expected29); + assert_eq_8(a28, expected28); + assert_eq_8(a27, expected27); + assert_eq_8(a26, expected26); + assert_eq_8(a25, expected25); + assert_eq_8(a24, expected24); + assert_eq_8(a23, expected23); + assert_eq_8(a22, expected22); + assert_eq_8(a21, expected21); + assert_eq_8(a20, expected20); + assert_eq_8(a19, expected19); + assert_eq_8(a18, expected18); + assert_eq_8(a17, expected17); + assert_eq_8(a16, expected16); + assert_eq_8(a15, expected15); + assert_eq_8(a14, expected14); + assert_eq_8(a13, expected13); + assert_eq_8(a12, expected12); + assert_eq_8(a11, expected11); + assert_eq_8(a10, expected10); + assert_eq_8(a9, expected9); + assert_eq_8(a8, expected8); + assert_eq_8(a7, expected7); + assert_eq_8(a6, expected6); + assert_eq_8(a5, expected5); + assert_eq_8(a4, expected4); + assert_eq_8(a3, expected3); + assert_eq_8(a2, expected2); + assert_eq_8(a1, expected1); + assert_eq_8(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + let ( + a15, + a14, + a13, + a12, + a11, + a10, + a9, + a8, + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): ( + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16, + u16 + ) = split_u256_into_u16(a); + + let ( + expected3, + expected2, + expected1, + expected0 + ): (u64, u64, u64, u64) = ::into(expected); + + let ( + expected15, + expected14, + expected13, + expected12 + ): (u16, u16, u16, u16) = ::into(expected3); + let ( + expected11, + expected10, + expected9, + expected8 + ): (u16, u16, u16, u16) = ::into(expected2); + let ( + expected7, + expected6, + expected5, + expected4 + ): (u16, u16, u16, u16) = ::into(expected1); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u16, u16, u16, u16) = ::into(expected0); + + assert_eq_16(a15, expected15); + assert_eq_16(a14, expected14); + assert_eq_16(a13, expected13); + assert_eq_16(a12, expected12); + assert_eq_16(a11, expected11); + assert_eq_16(a10, expected10); + assert_eq_16(a9, expected9); + assert_eq_16(a8, expected8); + assert_eq_16(a7, expected7); + assert_eq_16(a6, expected6); + assert_eq_16(a5, expected5); + assert_eq_16(a4, expected4); + assert_eq_16(a3, expected3); + assert_eq_16(a2, expected2); + assert_eq_16(a1, expected1); + assert_eq_16(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + let ( + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): (u32, u32, u32, u32, u32, u32, u32, u32) = split_u256_into_u32(a); + + let (expected1, expected0): (u128, u128) = ::into(expected); + + let ( + expected7, + expected6, + expected5, + expected4 + ): (u32, u32, u32, u32) = ::into(expected1); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u32, u32, u32, u32) = ::into(expected0); + + assert_eq_32(a7, expected7); + assert_eq_32(a6, expected6); + assert_eq_32(a5, expected5); + assert_eq_32(a4, expected4); + assert_eq_32(a3, expected3); + assert_eq_32(a2, expected2); + assert_eq_32(a1, expected1); + assert_eq_32(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (a3, a2, a1, a0): (u64, u64, u64, u64) = split_u256_into_u64(a); + + let ( + expected3, + expected2, + expected1, + expected0 + ): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(a3, expected3); + assert_eq_64(a2, expected2); + assert_eq_64(a1, expected1); + assert_eq_64(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (a1, a0): (u128, u128) = split_u256_into_u128(a); + + let (expected1, expected0): (u128, u128) = ::into(expected); + + assert_eq_128(a1, expected1); + assert_eq_128(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u256_to_u1(a), expected); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + + assert_eq_8(safe_u256_to_u8(a), expected); + }, + false => {}, + }; + + match if_test_this_function(7, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u16 = jet::rightmost_64_16(expected); + + assert_eq_16(safe_u256_to_u16(a), expected); + }, + false => {}, + }; + + match if_test_this_function(8, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u32 = jet::rightmost_64_32(expected); + + assert_eq_32(safe_u256_to_u32(a), expected); + }, + false => {}, + }; + + match if_test_this_function(9, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(safe_u256_to_u64(a), expected); + }, + false => {}, + }; + + match if_test_this_function(10, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert_eq_128(safe_u256_to_u128(a), expected); + }, + false => {}, + }; +} diff --git a/simf/u256_math_test.simf b/simf/u256_math_test.simf new file mode 100644 index 0000000..9bc8930 --- /dev/null +++ b/simf/u256_math_test.simf @@ -0,0 +1,63 @@ +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 new file mode 100644 index 0000000..1c06f46 --- /dev/null +++ b/simf/u256_test_add.simf @@ -0,0 +1,27 @@ +use crate::lib::u256::math::{add_256, add_256_128}; +use crate::lib::asserts::assert_eq_256; +use crate::helper::{if_test_this_function, assert_bool}; + +/// Asserts a result equals expected u256 and bool values. +/// Used for functions that return carry or borrow bool value. +fn assert_eq_uint_bool(result: (bool, u256), expected: u256, expected_bool: bool) { + let (bool_res, uint_res): (bool, u256) = result; + + assert_bool(bool_res, expected_bool); + + assert_eq_256(uint_res, expected); +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + let expected_bool: bool = witness::EXPECTED_BOOL; + + /// Arithmetic + + match if_test_this_function(0, fn_idx) { true => { assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); assert_eq_uint_bool(add_256_128(a, b), unwrap(expected), expected_bool); }, false => (), }; +} diff --git a/simf/u256_test_bits.simf b/simf/u256_test_bits.simf new file mode 100644 index 0000000..797c0e8 --- /dev/null +++ b/simf/u256_test_bits.simf @@ -0,0 +1,35 @@ +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; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + /// Bit logic + + match if_test_this_function(0, fn_idx) { true => { assert_eq_256(and_256(a, b), unwrap(expected)); }, false => (), }; + match if_test_this_function(1, fn_idx) { true => { assert_eq_256(or_256(a, b), unwrap(expected)); }, false => (), }; + + match if_test_this_function(2, fn_idx) { + true => { + let (_, _, _, a): (u64, u64, u64, u64) = split_u256_into_u64(a); + let shift: u8 = jet::rightmost_64_8(a); + + assert_eq_256(left_shift_256(shift, b), unwrap(expected)); + }, false => (), + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (_, _, _, a): (u64, u64, u64, u64) = split_u256_into_u64(a); + let shift: u8 = jet::rightmost_64_8(a); + + assert_eq_256(right_shift_256(shift, b), unwrap(expected)); + }, false => (), + }; +} diff --git a/simf/u256_test_compare.simf b/simf/u256_test_compare.simf new file mode 100644 index 0000000..5192369 --- /dev/null +++ b/simf/u256_test_compare.simf @@ -0,0 +1,16 @@ +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/u256_test_div.simf b/simf/u256_test_div.simf new file mode 100644 index 0000000..7c895b2 --- /dev/null +++ b/simf/u256_test_div.simf @@ -0,0 +1,54 @@ +use crate::lib::u256::math::{div_mod_256_64, div_mod_256_128, div_mod_256, div_256}; +use crate::lib::u256::convert::split_u256_into_u64; +use crate::lib::asserts::{assert_eq_128, assert_eq_256}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + let second_expected: u256 = witness::SECOND_EXPECTED; + + /// Arithmetic + + match if_test_this_function(0, fn_idx) { + true => { + let (_, _, _, b): (u64, u64, u64, u64) = split_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)); + }, + false => (), + }; + + match if_test_this_function(1, fn_idx) { + true => { + let (_, b): (u128, u128) = ::into(b); + let (_, expected_r): (u128, u128) = ::into(second_expected); + + let (q, r): (u256, u128) = div_mod_256_128(a, b); + + assert_eq_256(q, unwrap(expected)); + assert_eq_128(r, expected_r); + }, + false => (), + }; + + match if_test_this_function(2, fn_idx) { + true => { + let (q, r): (u256, u256) = div_mod_256(a, b); + + assert_eq_256(q, unwrap(expected)); + assert_eq_256(r, second_expected); + }, + false => (), + }; + + match if_test_this_function(3, fn_idx) { true => { assert_eq_256(div_256(a, b), unwrap(expected)); }, false => (), }; +} diff --git a/simf/u256_test_sub_mul.simf b/simf/u256_test_sub_mul.simf new file mode 100644 index 0000000..aa6b673 --- /dev/null +++ b/simf/u256_test_sub_mul.simf @@ -0,0 +1,65 @@ +use crate::lib::u256::math::{sub_256, mul_256, mul_256_64, mul_256_128}; +use crate::lib::u256::convert::split_u256_into_u64; +use crate::lib::asserts::{assert_eq_64, assert_eq_128, assert_eq_256}; +use crate::helper::{if_test_this_function, assert_bool}; + +/// Asserts a result equals expected u256 and bool values. +/// Used for functions that return carry or borrow bool value. +fn assert_eq_uint_bool(result: (bool, u256), expected: u256, expected_bool: bool) { + let (bool_res, uint_res): (bool, u256) = result; + + assert_bool(bool_res, expected_bool); + + assert_eq_256(uint_res, expected); +} + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let expected: Option = witness::EXPECTED; + + let expected_bool: bool = witness::EXPECTED_BOOL; + let second_expected: u256 = witness::SECOND_EXPECTED; + + /// Arithmetic + + match if_test_this_function(0, fn_idx) { true => { assert_eq_uint_bool(sub_256(a, b), unwrap(expected), expected_bool); }, false => (), }; + + match if_test_this_function(1, fn_idx) { + true => { + let (result_high, result_low): (u256, u256) = mul_256(a, b); + + assert_eq_256(result_high, unwrap(expected)); + assert_eq_256(result_low, second_expected); + }, + false => (), + }; + + match if_test_this_function(2, fn_idx) { + true => { + let (_, _, _, b): (u64, u64, u64, u64) = split_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); + + assert_eq_64(result_high, expected); + assert_eq_256(result_low, second_expected); + }, + false => (), + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (_, b): (u128, u128) = ::into(b); + let (_, expected): (u128, u128) = ::into(unwrap(expected)); + + let (result_high, result_low): (u128, u256) = mul_256_128(a, b); + + assert_eq_128(result_high, expected); + assert_eq_256(result_low, second_expected); + }, + false => (), + }; +} diff --git a/simf/u32_convert_test.simf b/simf/u32_convert_test.simf new file mode 100644 index 0000000..658cc58 --- /dev/null +++ b/simf/u32_convert_test.simf @@ -0,0 +1,118 @@ +use crate::lib::u32::convert::{ + u32_to_u64, + u32_to_u128, + u32_to_u256, + split_u32_into_u8, + split_u32_into_u16, + safe_u32_to_u1, + 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, + assert_eq_16, + assert_eq_64, + assert_eq_128, + assert_eq_256 +}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u32 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(u32_to_u64(a), expected); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert_eq_128(u32_to_u128(a), expected); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + assert_eq_256(u32_to_u256(a), expected); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (a3, a2, a1, a0): (u8, u8, u8, u8) = split_u32_into_u8(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u32 = jet::rightmost_64_32(expected); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u8, u8, u8, u8) = ::into(expected); + + assert_eq_8(a3, expected3); + assert_eq_8(a2, expected2); + assert_eq_8(a1, expected1); + assert_eq_8(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (a1, a0): (u16, u16) = split_u32_into_u16(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u32 = jet::rightmost_64_32(expected); + let (expected1, expected0): (u16, u16) = ::into(expected); + + assert_eq_16(a1, expected1); + assert_eq_16(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u32_to_u1(a), expected); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + + assert_eq_8(safe_u32_to_u8(a), expected); + }, + false => {}, + }; + + match if_test_this_function(7, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u16 = jet::rightmost_64_16(expected); + + assert_eq_16(safe_u32_to_u16(a), expected); + }, + false => {}, + }; +} diff --git a/simf/u32_test.simf b/simf/u32_math_test.simf similarity index 93% rename from simf/u32_test.simf rename to simf/u32_math_test.simf index 8636003..0d8274e 100644 --- a/simf/u32_test.simf +++ b/simf/u32_math_test.simf @@ -1,4 +1,4 @@ -use crate::lib::u32::{checked_add_32, safe_add_32, checked_sub_32, safe_sub_32, checked_mul_32, safe_mul_32, checked_div_32, safe_div_32, gt_32, ge_32}; +use crate::lib::u32::math::{checked_add_32, safe_add_32, checked_sub_32, safe_sub_32, checked_mul_32, safe_mul_32, checked_div_32, safe_div_32, gt_32, ge_32}; use crate::lib::asserts::{assert_none_32, assert_eq_32}; use crate::lib::binary::not; use crate::helper::if_test_this_function; diff --git a/simf/u64_convert_test.simf b/simf/u64_convert_test.simf new file mode 100644 index 0000000..00d651e --- /dev/null +++ b/simf/u64_convert_test.simf @@ -0,0 +1,154 @@ +use crate::lib::u64::convert::{ + u64_to_u128, + u64_to_u256, + split_u64_into_u8, + split_u64_into_u16, + split_u64_into_u32, + safe_u64_to_u1, + safe_u64_to_u8, + safe_u64_to_u16, + safe_u64_to_u32 +}; +use crate::lib::asserts::{ + assert_eq_1, + assert_eq_8, + assert_eq_16, + assert_eq_32, + assert_eq_128, + assert_eq_256 +}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u64 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert_eq_128(u64_to_u128(a), expected); + }, + false => {}, + }; + + match if_test_this_function(1, fn_idx) { + true => { + assert_eq_256(u64_to_u256(a), expected); + }, + false => {}, + }; + + match if_test_this_function(2, fn_idx) { + true => { + let ( + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = split_u64_into_u8(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let ( + expected7, + expected6, + expected5, + expected4, + expected3, + expected2, + expected1, + expected0 + ): (u8, u8, u8, u8, u8, u8, u8, u8) = ::into(expected); + + assert_eq_8(a7, expected7); + assert_eq_8(a6, expected6); + assert_eq_8(a5, expected5); + assert_eq_8(a4, expected4); + assert_eq_8(a3, expected3); + assert_eq_8(a2, expected2); + assert_eq_8(a1, expected1); + assert_eq_8(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (a3, a2, a1, a0): (u16, u16, u16, u16) = split_u64_into_u16(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let ( + expected3, + expected2, + expected1, + expected0 + ): (u16, u16, u16, u16) = ::into(expected); + + assert_eq_16(a3, expected3); + assert_eq_16(a2, expected2); + assert_eq_16(a1, expected1); + assert_eq_16(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(4, fn_idx) { + true => { + let (a1, a0): (u32, u32) = split_u64_into_u32(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (expected1, expected0): (u32, u32) = ::into(expected); + + assert_eq_32(a1, expected1); + assert_eq_32(a0, expected0); + }, + false => {}, + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u64_to_u1(a), expected); + }, + false => {}, + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + + assert_eq_8(safe_u64_to_u8(a), expected); + }, + false => {}, + }; + + match if_test_this_function(7, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u16 = jet::rightmost_64_16(expected); + + assert_eq_16(safe_u64_to_u16(a), expected); + }, + false => {}, + }; + + match if_test_this_function(8, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u32 = jet::rightmost_64_32(expected); + + assert_eq_32(safe_u64_to_u32(a), expected); + }, + false => {}, + }; +} diff --git a/simf/u64_test.simf b/simf/u64_math_test.simf similarity index 85% rename from simf/u64_test.simf rename to simf/u64_math_test.simf index 879215f..0866a57 100644 --- a/simf/u64_test.simf +++ b/simf/u64_math_test.simf @@ -1,6 +1,14 @@ -use crate::lib::u64::{ - checked_add_64, safe_add_64, checked_sub_64, safe_sub_64, checked_mul_64, safe_mul_64, checked_div_64, safe_div_64, - gt_64, ge_64, u64_into_u256, +use crate::lib::u64::math::{ + checked_add_64, + safe_add_64, + checked_sub_64, + safe_sub_64, + checked_mul_64, + safe_mul_64, + checked_div_64, + safe_div_64, + gt_64, + ge_64 }; use crate::lib::asserts::{assert_none_64, assert_eq_64}; use crate::lib::binary::not; @@ -11,8 +19,8 @@ use crate::helper::if_test_this_function; /// witness value carries both, removing the need for a separate overflow flag. fn assert_eq_opt(result: Option, expected: Option) { match expected { - None => assert_none_64(result), Some(e: u64) => assert_eq_64(unwrap(result), e), + None => assert_none_64(result), } } @@ -32,9 +40,6 @@ fn main() { let b: u64 = witness::SECOND_ARG; let expected: Option = witness::EXPECTED; - let expected_widen: u256 = witness::EXPECTED_WIDEN; - - // add match if_test_this_function(0, fn_idx) { true => { assert_eq_opt(checked_add_64(a, b), expected); }, false => (), }; match if_test_this_function(1, fn_idx) { true => { assert!(jet::eq_64(safe_add_64(a, b), unwrap(expected))); }, false => (), }; @@ -54,7 +59,4 @@ fn main() { // gt, ge match if_test_this_function(8, fn_idx) { true => { assert_bool_by_opt(gt_64(a, b), expected); }, false => (), }; match if_test_this_function(9, fn_idx) { true => { assert_bool_by_opt(ge_64(a, b), expected); }, false => (), }; - - // Custom - match if_test_this_function(192, fn_idx) { true => { assert!(jet::eq_256(u64_into_u256(a), expected_widen)); }, false => (), }; } diff --git a/simf/u8_convert_test.simf b/simf/u8_convert_test.simf new file mode 100644 index 0000000..2256638 --- /dev/null +++ b/simf/u8_convert_test.simf @@ -0,0 +1,120 @@ +use crate::lib::u8::convert::{ + u8_to_u16, + u8_to_u32, + u8_to_u64, + u8_to_u128, + u8_to_u256, + 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, + assert_eq_32, + assert_eq_64, + assert_eq_128, + assert_eq_256 +}; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u8 = witness::FIRST_ARG; + + let expected: u256 = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, _, _, expected): (u16, u16, u16, u16) = ::into(expected); + + assert_eq_16(u8_to_u16(a), expected); + }, + false => (), + }; + + match if_test_this_function(1, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let (_, expected): (u32, u32) = ::into(expected); + + assert_eq_32(u8_to_u32(a), expected); + }, + false => (), + }; + + match if_test_this_function(2, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + + assert_eq_64(u8_to_u64(a), expected); + }, + false => (), + }; + + match if_test_this_function(3, fn_idx) { + true => { + let (_, expected): (u128, u128) = ::into(expected); + + assert_eq_128(u8_to_u128(a), expected); + }, + false => (), + }; + + match if_test_this_function(4, fn_idx) { + true => { + assert_eq_256(u8_to_u256(a), expected); + }, + false => (), + }; + + match if_test_this_function(5, fn_idx) { + true => { + let ( + a7, + a6, + a5, + a4, + a3, + a2, + a1, + a0 + ): (u1, u1, u1, u1, u1, u1, u1, u1) = split_u8_into_u1(a); + + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u8 = jet::rightmost_64_8(expected); + let ( + expected7, + expected6, + expected5, + expected4, + expected3, + expected2, + expected1, + expected0 + ): (u1, u1, u1, u1, u1, u1, u1, u1) = ::into(expected); + + assert_eq_1(a7, expected7); + assert_eq_1(a6, expected6); + assert_eq_1(a5, expected5); + assert_eq_1(a4, expected4); + assert_eq_1(a3, expected3); + assert_eq_1(a2, expected2); + assert_eq_1(a1, expected1); + assert_eq_1(a0, expected0); + }, + false => (), + }; + + match if_test_this_function(6, fn_idx) { + true => { + let (_, _, _, expected): (u64, u64, u64, u64) = ::into(expected); + let expected: u1 = jet::rightmost_64_1(expected); + + assert_eq_1(safe_u8_to_u1(a), expected); + }, + false => (), + }; +} diff --git a/simf/u8_test.simf b/simf/u8_math_test.simf similarity index 92% rename from simf/u8_test.simf rename to simf/u8_math_test.simf index 37e8d32..d8c4f44 100644 --- a/simf/u8_test.simf +++ b/simf/u8_math_test.simf @@ -1,4 +1,15 @@ -use crate::lib::u8::{checked_add_8, safe_add_8, checked_sub_8, safe_sub_8, checked_mul_8, safe_mul_8, checked_div_8, safe_div_8, gt_8, ge_8}; +use crate::lib::u8::math::{ + checked_add_8, + safe_add_8, + checked_sub_8, + safe_sub_8, + checked_mul_8, + safe_mul_8, + checked_div_8, + safe_div_8, + gt_8, + ge_8 +}; use crate::lib::asserts::{assert_none_8, assert_eq_8}; use crate::lib::binary::not; use crate::helper::if_test_this_function; @@ -8,8 +19,8 @@ use crate::helper::if_test_this_function; /// witness value carries both, removing the need for a separate overflow flag. fn assert_eq_opt(result: Option, expected: Option) { match expected { - None => assert_none_8(result), Some(e: u8) => assert_eq_8(unwrap(result), e), + None => assert_none_8(result), } } diff --git a/tests/asserts_test.rs b/tests/asserts_test.rs index fad2ee3..94fcee0 100644 --- a/tests/asserts_test.rs +++ b/tests/asserts_test.rs @@ -19,6 +19,7 @@ enum FunctionToTest { AssertEq64, AssertEq128, AssertEq256, + AssertEqBool, AssertNone1, AssertNone8, @@ -37,7 +38,7 @@ const DEFAULT_SOME_U128: Option = Some(0); const DEFAULT_SOME_U256: Option<[u8; 32]> = Some([0; 32]); fn program() -> AssertsTestProgram { - AssertsTestProgram::new(AssertsTestArguments {}) + AssertsTestProgram::new(&AssertsTestArguments {}) } /// Returns two values in `[min, max]` that are equal when `same`, distinct otherwise. @@ -113,6 +114,10 @@ fn build_witness(function: FunctionToTest, same: bool, none: bool) -> AssertsTes (witness.first_arg_u256, witness.second_arg_u256) = (Some([a as u8; 32]), Some([b as u8; 32])); } + FunctionToTest::AssertEqBool => { + let (a, b) = generate_uints_in_one_range(same, 0, 1u128); + (witness.first_arg_u1, witness.second_arg_u1) = (Some(a as u8), Some(b as u8)); + } FunctionToTest::AssertNone1 => { if none { witness.first_arg_u1 = None; @@ -315,6 +320,28 @@ mod asserts_test { ) } + #[simplex::test] + fn assert_eq_bool_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEqBool, + true, + false, + Expect::Ok, + ) + } + + #[simplex::test] + fn assert_eq_bool_unhappy_path(context: simplex::TestContext) -> anyhow::Result<()> { + run_assert( + &context, + FunctionToTest::AssertEqBool, + false, + false, + Expect::AssertFailed, + ) + } + // ---------- assert_none: happy = None arg, unhappy = Some arg ---------- #[simplex::test] fn assert_none_1_happy_path(context: simplex::TestContext) -> anyhow::Result<()> { diff --git a/tests/binary_test.rs b/tests/binary_test.rs index 7bc6f50..2da001e 100644 --- a/tests/binary_test.rs +++ b/tests/binary_test.rs @@ -12,7 +12,7 @@ mod binary_tests { #[simplex::test] fn binary_test(context: simplex::TestContext) -> anyhow::Result<()> { - let program = BinaryTestProgram::new(BinaryTestArguments {}); + let program = BinaryTestProgram::new(&BinaryTestArguments {}); run(&context, program, BinaryTestWitness {}, Expect::Ok) } } diff --git a/tests/common/helper.rs b/tests/common/helper.rs new file mode 100644 index 0000000..6b5a568 --- /dev/null +++ b/tests/common/helper.rs @@ -0,0 +1,22 @@ +// Each `tests/*.rs` is a separate crate that mounts this module but uses only +// part of it, so per-crate dead-code analysis would warn about the rest. +#![allow(dead_code)] + +use primitive_types::U256; +use rand::Rng; + +use crate::common::u256_wrapper::U256Wrapper; + +// Shared constants and helper functions used across integration tests + +pub const DEFAULT_BOOL: bool = false; + +pub fn generate_u256(lower_bound: U256, upper_bound: U256) -> U256 { + assert!( + lower_bound <= upper_bound, + "Error: lower bound is greater than upper bound" + ); + rand::thread_rng() + .gen_range(U256Wrapper(lower_bound)..=U256Wrapper(upper_bound)) + .0 +} diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 21e7049..e8669e2 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,2 +1,4 @@ pub mod core; +pub mod helper; +pub mod u256_wrapper; pub mod uint; diff --git a/tests/common/u256_wrapper.rs b/tests/common/u256_wrapper.rs new file mode 100644 index 0000000..97b2679 --- /dev/null +++ b/tests/common/u256_wrapper.rs @@ -0,0 +1,149 @@ +use primitive_types::U256; +use rand::Rng; +use rand::distributions::uniform::{SampleBorrow, SampleUniform, UniformSampler}; +use std::ops::{Add, Deref, DerefMut, Div, Mul, Sub}; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct U256Wrapper(pub U256); + +impl U256Wrapper { + pub fn to_be_bytes(&self) -> [u8; 32] { + self.0.to_big_endian() + } +} + +impl Deref for U256Wrapper { + type Target = U256; + fn deref(&self) -> &U256 { + &self.0 + } +} + +impl DerefMut for U256Wrapper { + fn deref_mut(&mut self) -> &mut U256 { + &mut self.0 + } +} + +impl From for U256Wrapper { + fn from(v: U256) -> Self { + U256Wrapper(v) + } +} + +impl From for U256 { + fn from(v: U256Wrapper) -> Self { + v.0 + } +} + +impl Add for U256Wrapper { + type Output = U256Wrapper; + fn add(self, rhs: Self) -> Self::Output { + U256Wrapper(self.0 + rhs.0) + } +} + +impl Sub for U256Wrapper { + type Output = U256Wrapper; + fn sub(self, rhs: Self) -> Self::Output { + U256Wrapper(self.0 - rhs.0) + } +} + +impl Mul for U256Wrapper { + type Output = U256Wrapper; + fn mul(self, rhs: Self) -> Self::Output { + U256Wrapper(self.0 * rhs.0) + } +} + +impl Div for U256Wrapper { + type Output = U256Wrapper; + fn div(self, rhs: Self) -> Self::Output { + U256Wrapper(self.0 / rhs.0) + } +} + +#[derive(Clone, Copy, Debug)] +pub struct UniformU256 { + lower: U256, + range: U256, + inclusive_max: bool, +} + +impl UniformSampler for UniformU256 { + type X = U256Wrapper; + + fn new(lower: B1, upper: B2) -> Self + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let lower = lower.borrow().0; + let upper = upper.borrow().0; + + assert!(lower < upper, "Lower bound must be less than upper bound"); + + UniformU256 { + lower, + range: upper - lower, + inclusive_max: false, + } + } + + fn new_inclusive(lower: B1, upper: B2) -> Self + where + B1: SampleBorrow + Sized, + B2: SampleBorrow + Sized, + { + let lower = lower.borrow().0; + let upper = upper.borrow().0; + + assert!( + lower <= upper, + "Lower bound must be less than or equal to upper bound" + ); + + if upper == U256::MAX && lower == U256::zero() { + UniformU256 { + lower, + range: U256::zero(), + inclusive_max: true, + } + } else { + UniformU256 { + lower, + range: upper - lower + U256::one(), + inclusive_max: false, + } + } + } + + fn sample(&self, rng: &mut R) -> Self::X { + if self.inclusive_max { + return U256Wrapper(random_u256(rng)); + } + loop { + let candidate = random_u256(rng); + let result = candidate % self.range; + let limit = U256::MAX - (U256::MAX % self.range); + + if candidate < limit { + return U256Wrapper(self.lower + result); + } + } + } +} + +impl SampleUniform for U256Wrapper { + type Sampler = UniformU256; +} + +fn random_u256(rng: &mut R) -> U256 { + let mut bytes = [0u8; 32]; + + rng.fill(&mut bytes); + + U256::from_big_endian(&bytes) +} diff --git a/tests/helper_test.rs b/tests/helper_test.rs new file mode 100644 index 0000000..8843942 --- /dev/null +++ b/tests/helper_test.rs @@ -0,0 +1,23 @@ +mod common; + +use primitive_types::U256; + +use crate::common::helper::generate_u256; + +#[test] +fn generate_u256_respects_bounds() { + let cases = [ + (U256::zero(), U256::MAX), + (U256::one(), U256::from(u128::MAX)), + (U256::from(u128::MAX) + 1, U256::MAX), + (U256::from(u64::MAX) + 1, U256::from(u128::MAX)), + (U256::MAX, U256::MAX), + ]; + + for (lo, hi) in cases { + for _ in 0..10_000 { + let v = generate_u256(lo, hi); + assert!(lo <= v && v <= hi, "{v:#x} outside [{lo:#x}, {hi:#x}]"); + } + } +} diff --git a/tests/op_return_test.rs b/tests/op_return_test.rs index 917bd49..4eb2fe1 100644 --- a/tests/op_return_test.rs +++ b/tests/op_return_test.rs @@ -3,6 +3,7 @@ mod common; use rand::Rng; use crate::common::core::{run, run_with_op_return}; +use crate::common::helper::DEFAULT_BOOL; use common::core::Expect; use simplicityhl_std::artifacts::op_return_test::OpReturnTestProgram; @@ -20,11 +21,10 @@ fn op(o: FunctionToTest) -> u8 { o as u8 } -const DEFAULT_BOOL: bool = false; const DEFAULT_DATA: &[u8; 1] = &[1]; fn program() -> OpReturnTestProgram { - OpReturnTestProgram::new(OpReturnTestArguments {}) + OpReturnTestProgram::new(&OpReturnTestArguments {}) } fn build_witness(function: u8, index: u32, expected: bool) -> OpReturnTestWitness { diff --git a/tests/secp256k1_operations_test.rs b/tests/secp256k1_operations_test.rs index 08b5b87..a834f63 100644 --- a/tests/secp256k1_operations_test.rs +++ b/tests/secp256k1_operations_test.rs @@ -48,7 +48,7 @@ const SECP_N: [u8; 32] = [ ]; fn program() -> Secp256k1OperationsTestProgram { - Secp256k1OperationsTestProgram::new(Secp256k1OperationsTestArguments {}) + Secp256k1OperationsTestProgram::new(&Secp256k1OperationsTestArguments {}) } #[allow(clippy::too_many_arguments)] diff --git a/tests/u128_test_arithmetic.rs b/tests/u128_basic_math_test.rs similarity index 74% rename from tests/u128_test_arithmetic.rs rename to tests/u128_basic_math_test.rs index 7b72ea9..77dd7b3 100644 --- a/tests/u128_test_arithmetic.rs +++ b/tests/u128_basic_math_test.rs @@ -3,24 +3,24 @@ mod common; use primitive_types::U256; use rand::Rng; +use crate::common::helper::DEFAULT_BOOL; use common::core::{Expect, run}; -use simplicityhl_std::artifacts::u128_test_arithmetic::U128TestArithmeticProgram; -use simplicityhl_std::artifacts::u128_test_arithmetic::derived_u128_test_arithmetic::{ - U128TestArithmeticArguments, U128TestArithmeticWitness, +use simplicityhl_std::artifacts::u128_basic_math_test::U128BasicMathTestProgram; +use simplicityhl_std::artifacts::u128_basic_math_test::derived_u128_basic_math_test::{ + U128BasicMathTestArguments, U128BasicMathTestWitness, }; enum FunctionToTest { - IsZero128, - Lt128, - Le128, Add128, Add128_64, + FullAdd128, Sub128, + FullSub128, Mul128, - Split256Into64, - NormalizeToThreshold, - AlgorithmD, + Mul128_64, + CalculateNormalizerBase64, + EstimateQuotientDigitBase64, DivMod128_64, DivMod128, Div128, @@ -31,11 +31,10 @@ fn op(o: FunctionToTest) -> u8 { o as u8 } -const DEFAULT_BOOL: bool = false; const DEFAULT_EXPECTED: u128 = 0; -fn program() -> U128TestArithmeticProgram { - U128TestArithmeticProgram::new(U128TestArithmeticArguments {}) +fn program() -> U128BasicMathTestProgram { + U128BasicMathTestProgram::new(&U128BasicMathTestArguments {}) } fn build_witness( @@ -45,16 +44,14 @@ fn build_witness( expected: Option, expected_bool: bool, second_expected: u128, - third_expected: u128, -) -> U128TestArithmeticWitness { - U128TestArithmeticWitness { +) -> U128BasicMathTestWitness { + U128BasicMathTestWitness { function_index: function, first_arg: a, second_arg: b, expected, expected_bool, second_expected, - third_expected, } } @@ -69,254 +66,184 @@ mod u128_tests_arithmetic { use super::*; #[simplex::test] - fn u128_test_is_zero_128_true(context: simplex::TestContext) -> anyhow::Result<()> { - let a = 0; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::IsZero128), - a, - DEFAULT_EXPECTED, - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_is_zero_128_false(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u128::MAX); + 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::IsZero128), + op(FunctionToTest::Add128), a, - DEFAULT_EXPECTED, - Some(DEFAULT_EXPECTED), + b, + Some(result), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_lt_128_less(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..u128::MAX); - let b = a + 1; + 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::Lt128), + op(FunctionToTest::Add128), a, b, - Some(DEFAULT_EXPECTED), + Some(result), true, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_lt_128_eq(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..u128::MAX); - let b = a; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Lt128), - a, - b, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_lt_128_bigger(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u128::MAX); - let b = a - 1; + 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::Lt128), + op(FunctionToTest::Add128_64), a, b, - Some(DEFAULT_EXPECTED), + Some(result), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_le_128_less(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..u128::MAX); - let b = a + 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Le128), - a, - b, - Some(DEFAULT_EXPECTED), - true, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_le_128_eq(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..u128::MAX); - let b = a; + 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::Le128), + op(FunctionToTest::Add128_64), a, b, - Some(DEFAULT_EXPECTED), + Some(result), true, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, - ), - Expect::Ok, - ) - } - - #[simplex::test] - fn u128_test_le_128_bigger(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(1..=u128::MAX); - let b = a - 1; - - run( - &context, - program(), - build_witness( - op(FunctionToTest::Le128), - a, - b, - Some(DEFAULT_EXPECTED), - false, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_add_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + 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::Add128), + op(FunctionToTest::FullAdd128), a, b, Some(result), - false, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, + result_carry, + carry_low, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_add_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + 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::Add128), + op(FunctionToTest::FullAdd128), a, b, Some(result), - true, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, + result_carry, + carry_low, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_add_128_64_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + 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..=u64::MAX) as u128; - let result = a + b; + 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::Add128_64), + op(FunctionToTest::FullAdd128), a, b, Some(result), - false, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, + result_carry, + carry_low, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_add_128_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + 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..=u64::MAX) as u128; - let result = b - 1; + 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::Add128_64), + op(FunctionToTest::FullAdd128), a, b, Some(result), - true, - DEFAULT_EXPECTED, - DEFAULT_EXPECTED, + result_carry, + carry_low, ), Expect::Ok, ) @@ -338,7 +265,6 @@ mod u128_tests_arithmetic { Some(result), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -358,7 +284,6 @@ mod u128_tests_arithmetic { Some(0), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -385,7 +310,6 @@ mod u128_tests_arithmetic { Some(result), carry, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -414,7 +338,6 @@ mod u128_tests_arithmetic { Some(result), carry, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -435,7 +358,6 @@ mod u128_tests_arithmetic { Some(a), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -457,7 +379,104 @@ mod u128_tests_arithmetic { Some(result), true, DEFAULT_EXPECTED, - 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, ) @@ -481,236 +500,231 @@ mod u128_tests_arithmetic { Some(result_high), DEFAULT_BOOL, result_low, - DEFAULT_EXPECTED, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_split_256_into_64(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); - let b = rand::thread_rng().gen_range(0..=u128::MAX); + fn u128_test_mul_128_64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = rand::thread_rng().gen_range(0..u64::MAX); + let result = U256::from(a) * U256::from(b); + + let (result_high, result_low) = split_helper(result); run( &context, program(), build_witness( - op(FunctionToTest::Split256Into64), + op(FunctionToTest::Mul128_64), a, - b, - Some(a), + b as u128, + Some(result_high), DEFAULT_BOOL, - b, - DEFAULT_EXPECTED, + result_low, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_u64( + fn u128_test_calculate_normalizer_base_64_b_is_u64( context: simplex::TestContext, ) -> anyhow::Result<()> { let threshold = 1u128 << 63; - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = rand::thread_rng().gen_range(1..threshold); let norm: u128 = threshold.div_ceil(b); - let result_a = U256::from(a) * U256::from(norm); - let result_b = b * norm; - let (result_a_high, result_a_low) = split_helper(result_a); - run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, - Some(result_a_high), + Some(norm), false, - result_a_low, - result_b, + DEFAULT_EXPECTED, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_big_enough_not_normalize( + fn u128_test_calculate_normalizer_base_64_b_is_big_enough_not_normalize( context: simplex::TestContext, ) -> anyhow::Result<()> { let threshold = 1u128 << 63; - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = rand::thread_rng().gen_range(threshold..=u64::MAX as u128); run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, - Some(0), + Some(1), false, - a, - b, + DEFAULT_EXPECTED, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_u128( + fn u128_test_calculate_normalizer_base_64_b_is_u128( context: simplex::TestContext, ) -> anyhow::Result<()> { let threshold = 1u128 << 63; - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = rand::thread_rng().gen_range((u64::MAX as u128) + 1..=u128::MAX); - let b_high = b >> 64; let norm: u128 = threshold.div_ceil(b_high); - let result_a = U256::from(a) * U256::from(norm); - let result_b = b * norm; - let (result_a_high, result_a_low) = split_helper(result_a); - run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, - Some(result_a_high), + Some(norm), true, - result_a_low, - result_b, + DEFAULT_EXPECTED, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_u64_fail( + fn u128_test_calculate_normalizer_base_64_b_is_u64_fail( context: simplex::TestContext, ) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = rand::thread_rng().gen_range((u64::MAX as u128) + 1..=u128::MAX); run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, Some(DEFAULT_EXPECTED), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::AssertFailed, ) } #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_u128_fail( + fn u128_test_calculate_normalizer_base_64_b_is_u128_fail( context: simplex::TestContext, ) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = rand::thread_rng().gen_range(1..=u64::MAX as u128); run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, Some(DEFAULT_EXPECTED), true, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::AssertFailed, ) } #[simplex::test] - fn u128_test_normalize_to_threshold_b_is_zero_fail( + fn u128_test_calculate_normalizer_base_64_b_is_zero_fail( context: simplex::TestContext, ) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = 0; run( &context, program(), build_witness( - op(FunctionToTest::NormalizeToThreshold), - a, + op(FunctionToTest::CalculateNormalizerBase64), + DEFAULT_EXPECTED, b, Some(DEFAULT_EXPECTED), false, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::AssertFailed, ) } #[simplex::test] - fn u128_test_algorithm_d(context: simplex::TestContext) -> anyhow::Result<()> { - // divisor is expected to be greater than or equal to 2^64 - let b = rand::thread_rng().gen_range((u64::MAX as u128) + 1..u128::MAX); - let a = rand::thread_rng().gen_range(b..=u128::MAX); + fn u128_test_estimate_quotient_digit_base_64( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let threshold = 1u64 << 63; - let q = a / b; - let r = a % b; + let b_high = rand::thread_rng().gen_range(threshold..=u64::MAX); + let b_low = rand::thread_rng().gen_range(0..=u64::MAX); + + let a_high = rand::thread_rng().gen_range(0..b_high); + let a_low = rand::thread_rng().gen_range(0..=u128::MAX); + + let a = ((U256::from(a_high)) << 128) | (U256::from(a_low)); + let b = ((b_high as u128) << 64) | (b_low as u128); + + let q = (a / b).as_u128(); run( &context, program(), build_witness( - op(FunctionToTest::AlgorithmD), - a, - b, + op(FunctionToTest::EstimateQuotientDigitBase64), + a_high as u128, + a_low, Some(q), DEFAULT_BOOL, - r, - DEFAULT_EXPECTED, + b, ), Expect::Ok, ) } #[simplex::test] - fn u128_test_algorithm_d_fail(context: simplex::TestContext) -> anyhow::Result<()> { - // expected to fail because divisor is less than 2^64 - let b = rand::thread_rng().gen_range(1..=u64::MAX as u128); - let a = rand::thread_rng().gen_range(b..=u128::MAX); + fn u128_test_estimate_quotient_digit_base_64_fail( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + // expected to fail because a is to big for q to fit unto u64 + let threshold = 1u64 << 63; - let q = a / b; - let r = a % b; + let b_high = rand::thread_rng().gen_range(threshold..u64::MAX); + let b_low = rand::thread_rng().gen_range(0..=u64::MAX); + + let a_high = rand::thread_rng().gen_range(b_high + 1..=u64::MAX); + let a_low = rand::thread_rng().gen_range(0..=u128::MAX); + + let a = ((U256::from(a_high)) << 128) | (U256::from(a_low)); + let b = ((b_high as u128) << 64) | (b_low as u128); + + let q = (a / b).as_u128(); run( &context, program(), build_witness( - op(FunctionToTest::AlgorithmD), - a, - b, + op(FunctionToTest::EstimateQuotientDigitBase64), + a_high as u128, + a_low, Some(q), DEFAULT_BOOL, - r, - DEFAULT_EXPECTED, + b, ), Expect::AssertFailed, ) @@ -734,7 +748,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -755,7 +768,6 @@ mod u128_tests_arithmetic { Some(DEFAULT_EXPECTED), DEFAULT_BOOL, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::AssertFailed, ) @@ -779,7 +791,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -803,7 +814,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -832,7 +842,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -856,7 +865,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -883,7 +891,6 @@ mod u128_tests_arithmetic { Some(q), DEFAULT_BOOL, r, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -903,7 +910,6 @@ mod u128_tests_arithmetic { Some(1u128), DEFAULT_BOOL, 0u128, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -928,7 +934,6 @@ mod u128_tests_arithmetic { Some(1u128), DEFAULT_BOOL, u64::MAX as u128, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -953,7 +958,6 @@ mod u128_tests_arithmetic { Some(0u128), DEFAULT_BOOL, a, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -975,7 +979,6 @@ mod u128_tests_arithmetic { Some(result), DEFAULT_BOOL, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -996,7 +999,6 @@ mod u128_tests_arithmetic { Some(DEFAULT_EXPECTED), DEFAULT_BOOL, DEFAULT_EXPECTED, - DEFAULT_EXPECTED, ), Expect::AssertFailed, ) diff --git a/tests/u128_convert_test.rs b/tests/u128_convert_test.rs new file mode 100644 index 0000000..e2d0a22 --- /dev/null +++ b/tests/u128_convert_test.rs @@ -0,0 +1,295 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u128_convert_test::U128ConvertTestProgram; +use simplicityhl_std::artifacts::u128_convert_test::derived_u128_convert_test::{ + U128ConvertTestArguments, U128ConvertTestWitness, +}; + +enum FunctionToTest { + U128ToU256, + SplitU128IntoU8, + SplitU128IntoU16, + SplitU128IntoU32, + SplitU128IntoU64, + SafeU128ToU1, + SafeU128ToU8, + SafeU128ToU16, + SafeU128ToU32, + SafeU128ToU64, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U128ConvertTestProgram { + U128ConvertTestProgram::new(&U128ConvertTestArguments {}) +} + +fn build_witness(function: u8, a: u128, expected: [u8; 32]) -> U128ConvertTestWitness { + U128ConvertTestWitness { + function_index: function, + first_arg: a, + expected, + } +} + +mod u128_convert_test { + use super::*; + + #[simplex::test] + fn u128_convert_test_u128_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U128ToU256), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_split_u128_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU128IntoU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_split_u128_into_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU128IntoU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_split_u128_into_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU128IntoU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_split_u128_into_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU128IntoU64), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u1_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX as u128); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u8_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u8::MAX as u128 + 1..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX as u128); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u16_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u16::MAX as u128 + 1..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX as u128); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u32_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u32::MAX as u128 + 1..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX as u128); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU64), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_convert_test_safe_u128_to_u64_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u64::MAX as u128 + 1..=u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU128ToU64), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u128_test.rs b/tests/u128_math_test.rs similarity index 52% rename from tests/u128_test.rs rename to tests/u128_math_test.rs index 5791165..06e2d98 100644 --- a/tests/u128_test.rs +++ b/tests/u128_math_test.rs @@ -2,15 +2,15 @@ mod common; use common::uint::TestUint; -use simplicityhl_std::artifacts::u128_test::U128TestProgram; -use simplicityhl_std::artifacts::u128_test::derived_u128_test::{ - U128TestArguments, U128TestWitness, +use simplicityhl_std::artifacts::u128_math_test::U128MathTestProgram; +use simplicityhl_std::artifacts::u128_math_test::derived_u128_math_test::{ + U128MathTestArguments, U128MathTestWitness, }; // The only per-width code for the common operations. impl TestUint for u128 { - type Program = U128TestProgram; - type Witness = U128TestWitness; + type Program = U128MathTestProgram; + type Witness = U128MathTestWitness; const ZERO: u128 = 0; const ONE: u128 = 1; @@ -18,12 +18,12 @@ impl TestUint for u128 { const HALF_MAX: u128 = u128::MAX / 2; const MUL_BOUND: u128 = 1 << 64; // 2^(128/2) - fn program() -> U128TestProgram { - U128TestProgram::new(U128TestArguments {}) + fn program() -> U128MathTestProgram { + U128MathTestProgram::new(&U128MathTestArguments {}) } - fn witness(op: u8, a: u128, b: u128, expected: Option) -> U128TestWitness { - U128TestWitness { + fn witness(op: u8, a: u128, b: u128, expected: Option) -> U128MathTestWitness { + U128MathTestWitness { function_index: op, first_arg: a, second_arg: b, @@ -32,9 +32,9 @@ impl TestUint for u128 { } } -mod u128_tests { +mod u128_math_tests { use super::*; - // Stamps the 16 `#[simplex::test]` entry points for u128. Logic lives in common::uint. + // Stamps the 22 `#[simplex::test]` entry points for u128. Logic lives in common::uint. uint_tests!(u128); } diff --git a/tests/u128_test_bits.rs b/tests/u128_test_bits.rs index cfc4531..508d5f7 100644 --- a/tests/u128_test_bits.rs +++ b/tests/u128_test_bits.rs @@ -2,6 +2,7 @@ mod common; use rand::Rng; +use crate::common::helper::DEFAULT_BOOL; use common::core::{Expect, run}; use simplicityhl_std::artifacts::u128_test_bits::U128TestBitsProgram; @@ -22,11 +23,10 @@ fn op(o: FunctionToTest) -> u8 { o as u8 } -const DEFAULT_BOOL: bool = false; const DEFAULT_EXPECTED: u128 = 0; fn program() -> U128TestBitsProgram { - U128TestBitsProgram::new(U128TestBitsArguments {}) + U128TestBitsProgram::new(&U128TestBitsArguments {}) } fn build_witness( diff --git a/tests/u128_test_compare.rs b/tests/u128_test_compare.rs new file mode 100644 index 0000000..1c7a01e --- /dev/null +++ b/tests/u128_test_compare.rs @@ -0,0 +1,142 @@ +mod common; + +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u128_test_compare::U128TestCompareProgram; +use simplicityhl_std::artifacts::u128_test_compare::derived_u128_test_compare::{ + U128TestCompareArguments, U128TestCompareWitness, +}; + +enum FunctionToTest { + IsZero128, + Lt128, + Le128, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: u128 = 0; + +fn program() -> U128TestCompareProgram { + U128TestCompareProgram::new(&U128TestCompareArguments {}) +} + +fn build_witness(function: u8, a: u128, b: u128, expected_bool: bool) -> U128TestCompareWitness { + U128TestCompareWitness { + function_index: function, + first_arg: a, + second_arg: b, + expected_bool, + } +} + +mod u128_tests_arithmetic { + use super::*; + + #[simplex::test] + fn u128_test_is_zero_128_true(context: simplex::TestContext) -> anyhow::Result<()> { + let a = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::IsZero128), a, DEFAULT_EXPECTED, true), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_is_zero_128_false(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + + run( + &context, + program(), + build_witness(op(FunctionToTest::IsZero128), a, DEFAULT_EXPECTED, false), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_lt_128_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a + 1; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Lt128), a, b, true), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_lt_128_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Lt128), a, b, false), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_lt_128_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + let b = a - 1; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Lt128), a, b, false), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_le_128_less(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a + 1; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Le128), a, b, true), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_le_128_eq(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..u128::MAX); + let b = a; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Le128), a, b, true), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_le_128_bigger(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + let b = a - 1; + + run( + &context, + program(), + build_witness(op(FunctionToTest::Le128), a, b, false), + Expect::Ok, + ) + } +} diff --git a/tests/u16_convert_test.rs b/tests/u16_convert_test.rs new file mode 100644 index 0000000..65df71a --- /dev/null +++ b/tests/u16_convert_test.rs @@ -0,0 +1,190 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u16_convert_test::U16ConvertTestProgram; +use simplicityhl_std::artifacts::u16_convert_test::derived_u16_convert_test::{ + U16ConvertTestArguments, U16ConvertTestWitness, +}; + +enum FunctionToTest { + U16ToU32, + U16ToU64, + U16ToU128, + U16ToU256, + 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_test.rs b/tests/u16_math_test.rs similarity index 52% rename from tests/u16_test.rs rename to tests/u16_math_test.rs index 7274e4e..07b4c00 100644 --- a/tests/u16_test.rs +++ b/tests/u16_math_test.rs @@ -2,13 +2,15 @@ mod common; use common::uint::TestUint; -use simplicityhl_std::artifacts::u16_test::U16TestProgram; -use simplicityhl_std::artifacts::u16_test::derived_u16_test::{U16TestArguments, U16TestWitness}; +use simplicityhl_std::artifacts::u16_math_test::U16MathTestProgram; +use simplicityhl_std::artifacts::u16_math_test::derived_u16_math_test::{ + U16MathTestArguments, U16MathTestWitness, +}; // The only per-width code for the common operations. impl TestUint for u16 { - type Program = U16TestProgram; - type Witness = U16TestWitness; + type Program = U16MathTestProgram; + type Witness = U16MathTestWitness; const ZERO: u16 = 0; const ONE: u16 = 1; @@ -16,12 +18,12 @@ impl TestUint for u16 { const HALF_MAX: u16 = u16::MAX / 2; const MUL_BOUND: u16 = 1 << 8; // 2^(16/2) - fn program() -> U16TestProgram { - U16TestProgram::new(U16TestArguments {}) + fn program() -> U16MathTestProgram { + U16MathTestProgram::new(&U16MathTestArguments {}) } - fn witness(op: u8, a: u16, b: u16, expected: Option) -> U16TestWitness { - U16TestWitness { + fn witness(op: u8, a: u16, b: u16, expected: Option) -> U16MathTestWitness { + U16MathTestWitness { function_index: op, first_arg: a, second_arg: b, @@ -30,9 +32,9 @@ impl TestUint for u16 { } } -mod u16_tests { +mod u16_math_tests { use super::*; - // Stamps the 16 `#[simplex::test]` entry points for u16. Logic lives in common::uint. + // Stamps the 22 `#[simplex::test]` entry points for u16. Logic lives in common::uint. uint_tests!(u16); } diff --git a/tests/u1_convert_test.rs b/tests/u1_convert_test.rs new file mode 100644 index 0000000..26c743c --- /dev/null +++ b/tests/u1_convert_test.rs @@ -0,0 +1,150 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u1_convert_test::U1ConvertTestProgram; +use simplicityhl_std::artifacts::u1_convert_test::derived_u1_convert_test::{ + U1ConvertTestArguments, U1ConvertTestWitness, +}; + +enum FunctionToTest { + U1ToU8, + U1ToU16, + U1ToU32, + U1ToU64, + U1ToU128, + U1ToU256, + U1ToBool, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U1ConvertTestProgram { + U1ConvertTestProgram::new(&U1ConvertTestArguments {}) +} + +fn build_witness(function: u8, a: u8, expected: [u8; 32]) -> U1ConvertTestWitness { + U1ConvertTestWitness { + function_index: function, + first_arg: a, + expected, + } +} + +mod u1_convert_test { + use super::*; + + #[simplex::test] + fn u1_convert_test_u1_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness(op(FunctionToTest::U1ToU8), a, U256::from(a).to_big_endian()), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_u1_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_u1_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_u1_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToU64), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_u1_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToU128), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_u1_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToU256), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u1_convert_test_split_u1_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U1ToBool), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } +} diff --git a/tests/u256_convert_test.rs b/tests/u256_convert_test.rs new file mode 100644 index 0000000..923c40a --- /dev/null +++ b/tests/u256_convert_test.rs @@ -0,0 +1,331 @@ +mod common; + +use primitive_types::U256; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_convert_test::U256ConvertTestProgram; +use simplicityhl_std::artifacts::u256_convert_test::derived_u256_convert_test::{ + U256ConvertTestArguments, U256ConvertTestWitness, +}; + +enum FunctionToTest { + SplitU256IntoU8, + SplitU256IntoU16, + SplitU256IntoU32, + SplitU256IntoU64, + SplitU256IntoU128, + SafeU256ToU1, + SafeU256ToU8, + SafeU256ToU16, + SafeU256ToU32, + SafeU256ToU64, + SafeU256ToU128, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U256ConvertTestProgram { + U256ConvertTestProgram::new(&U256ConvertTestArguments {}) +} + +fn build_witness(function: u8, a: [u8; 32], expected: [u8; 32]) -> U256ConvertTestWitness { + U256ConvertTestWitness { + function_index: function, + first_arg: a, + expected, + } +} + +mod u256_convert_test { + use crate::common::helper::generate_u256; + + use super::*; + + #[simplex::test] + fn u256_convert_test_u256_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU256IntoU8), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_u256_into_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU256IntoU16), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_u256_into_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU256IntoU32), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_u256_into_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU256IntoU64), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_u256_into_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU256IntoU128), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::one()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU1), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u1_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(2), U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU1), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u8::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU8), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u8_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u8::MAX) + 1, U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU8), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u16::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU16), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u16_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u16::MAX) + 1, U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU16), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u32::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU32), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u32_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u32::MAX) + 1, U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU32), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u64::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU64), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u64_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u64::MAX) + 1, U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU64), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u128::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU128), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_convert_test_safe_u256_to_u128_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU256ToU128), + a.to_big_endian(), + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u256_math_test.rs b/tests/u256_math_test.rs new file mode 100644 index 0000000..67ebc4a --- /dev/null +++ b/tests/u256_math_test.rs @@ -0,0 +1,48 @@ +mod common; + +use primitive_types::U256; + +use common::u256_wrapper::U256Wrapper; +use common::uint::TestUint; + +use simplicityhl_std::artifacts::u256_math_test::U256MathTestProgram; +use simplicityhl_std::artifacts::u256_math_test::derived_u256_math_test::{ + U256MathTestArguments, U256MathTestWitness, +}; + +// The only per-width code for the common operations. +impl TestUint for U256Wrapper { + type Program = U256MathTestProgram; + type Witness = U256MathTestWitness; + + const ZERO: U256Wrapper = U256Wrapper(U256::zero()); + const ONE: U256Wrapper = U256Wrapper(U256::one()); + const MAX: U256Wrapper = U256Wrapper(U256::MAX); + const HALF_MAX: U256Wrapper = U256Wrapper(U256([u64::MAX, u64::MAX, u64::MAX, u64::MAX >> 1])); + const MUL_BOUND: U256Wrapper = U256Wrapper(U256([0, 0, 1, 0])); // 2^(256/2) + + fn program() -> U256MathTestProgram { + U256MathTestProgram::new(&U256MathTestArguments {}) + } + + fn witness( + op: u8, + a: U256Wrapper, + b: U256Wrapper, + expected: Option, + ) -> U256MathTestWitness { + U256MathTestWitness { + function_index: op, + first_arg: a.to_big_endian(), + second_arg: b.to_big_endian(), + expected: expected.map(|w| w.to_be_bytes()), + } + } +} + +mod u256_math_tests { + use super::*; + + // Stamps the 22 `#[simplex::test]` entry points for U256Wrapper. Logic lives in common::uint. + uint_tests!(U256Wrapper); +} diff --git a/tests/u256_test_add.rs b/tests/u256_test_add.rs new file mode 100644 index 0000000..2e5bc44 --- /dev/null +++ b/tests/u256_test_add.rs @@ -0,0 +1,125 @@ +mod common; + +use primitive_types::U256; + +use crate::common::helper::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, +} + +#[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], + expected: Option<[u8; 32]>, + expected_bool: bool, +) -> U256TestAddWitness { + U256TestAddWitness { + function_index: function, + first_arg: a, + second_arg: b, + 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(), + Some(result), + false, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_add_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::MAX); + let result = (b - 1).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Add256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + true, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_add_256_128_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX / 2); + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let result = (a + b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Add256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + false, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_add_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let result = (b - 1).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Add256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + true, + ), + Expect::Ok, + ) + } +} diff --git a/tests/u256_test_bits.rs b/tests/u256_test_bits.rs new file mode 100644 index 0000000..c0c78e6 --- /dev/null +++ b/tests/u256_test_bits.rs @@ -0,0 +1,198 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use crate::common::helper::generate_u256; +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_test_bits::U256TestBitsProgram; +use simplicityhl_std::artifacts::u256_test_bits::derived_u256_test_bits::{ + U256TestBitsArguments, U256TestBitsWitness, +}; + +enum FunctionToTest { + And256, + Or256, + LeftShift256, + RightShift256, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U256TestBitsProgram { + U256TestBitsProgram::new(&U256TestBitsArguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, +) -> U256TestBitsWitness { + U256TestBitsWitness { + function_index: function, + first_arg: a, + second_arg: b, + expected, + } +} + +mod u256_tests_bits { + use super::*; + + #[simplex::test] + fn u256_test_and_256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::zero(), U256::MAX); + let result = (a & b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::And256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_or_256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::zero(), U256::MAX); + let result = (a | b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Or256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_left_shift_256(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = rand::thread_rng().gen_range(1..u8::MAX); + let val = generate_u256(U256::zero(), U256::MAX); + let result = (val << shift).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::LeftShift256), + U256::from(shift).to_big_endian(), + val.to_big_endian(), + Some(result), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_left_shift_256_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = 0; + let val = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + let result = val; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::LeftShift256), + U256::from(shift).to_big_endian(), + val, + Some(result), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_left_shift_256_max(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = u8::MAX; + let val = generate_u256(U256::zero(), U256::MAX); + let result = (val << shift).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::LeftShift256), + U256::from(shift).to_big_endian(), + val.to_big_endian(), + Some(result), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_right_shift_256(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = rand::thread_rng().gen_range(1..u8::MAX); + let val = generate_u256(U256::zero(), U256::MAX); + let result = (val >> shift).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::RightShift256), + U256::from(shift).to_big_endian(), + val.to_big_endian(), + Some(result), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_right_shift_256_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = 0; + let val = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + let result = val; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::RightShift256), + U256::from(shift).to_big_endian(), + val, + Some(result), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_right_shift_256_max(context: simplex::TestContext) -> anyhow::Result<()> { + let shift = u8::MAX; + let val = generate_u256(U256::zero(), U256::MAX); + let result = (val >> shift).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::RightShift256), + U256::from(shift).to_big_endian(), + val.to_big_endian(), + Some(result), + ), + Expect::Ok, + ) + } +} diff --git a/tests/u256_test_compare.rs b/tests/u256_test_compare.rs new file mode 100644 index 0000000..560e113 --- /dev/null +++ b/tests/u256_test_compare.rs @@ -0,0 +1,166 @@ +mod common; + +use primitive_types::U256; + +use crate::common::helper::generate_u256; +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_test_compare::U256TestCompareProgram; +use simplicityhl_std::artifacts::u256_test_compare::derived_u256_test_compare::{ + U256TestCompareArguments, U256TestCompareWitness, +}; + +enum FunctionToTest { + IsZero256, + Lt256, + Le256, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestCompareProgram { + U256TestCompareProgram::new(&U256TestCompareArguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected_bool: bool, +) -> U256TestCompareWitness { + U256TestCompareWitness { + function_index: function, + first_arg: a, + second_arg: b, + expected_bool, + } +} + +mod u256_tests_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 new file mode 100644 index 0000000..9c36ca9 --- /dev/null +++ b/tests/u256_test_div.rs @@ -0,0 +1,401 @@ +mod common; + +use primitive_types::U256; + +use crate::common::helper::generate_u256; +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_test_div::U256TestDivProgram; +use simplicityhl_std::artifacts::u256_test_div::derived_u256_test_div::{ + U256TestDivArguments, U256TestDivWitness, +}; + +enum FunctionToTest { + DivMod256_64, + DivMod256_128, + DivMod256, + Div256, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestDivProgram { + U256TestDivProgram::new(&U256TestDivArguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, + second_expected: [u8; 32], +) -> U256TestDivWitness { + U256TestDivWitness { + function_index: function, + first_arg: a, + second_arg: b, + expected, + second_expected, + } +} + +mod u256_tests_arithmetic { + use super::*; + + #[simplex::test] + fn test_div_mod_256_64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u64::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_64), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_64_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_64), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn test_div_mod_256_128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::from(u64::MAX) + 1, U256::from(u128::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_128_b_fits_into_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u64::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_div_mod_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256_128), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn test_div_mod_256_128_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = (generate_u256(U256::one(), U256::from(u128::MAX))).to_big_endian(); + + let q = U256::one().to_big_endian(); + let r = U256::zero().to_big_endian(); + + run( + &context, + program(), + build_witness(op(FunctionToTest::DivMod256_128), a, a, Some(q), r), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_a_less_than_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX - 1); + let b = generate_u256(a + 1, U256::MAX); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_div_128(context: simplex::TestContext) -> anyhow::Result<()> { + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let a = generate_u256(b, U256::from(u128::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_q_is_1(context: simplex::TestContext) -> anyhow::Result<()> { + // case where a >= b and a_high = b_high != 0 + let b_low = generate_u256(U256::zero(), U256::from(u128::MAX)); + let a_low = generate_u256(b_low, U256::from(u128::MAX)); + let high = generate_u256(U256::one(), U256::from(u128::MAX)); + + let a = (high << 128) | (a_low); + let b = (high << 128) | (b_low); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_b_fits_into_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_b_is_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let b = generate_u256(U256::one(), U256::MAX - 1); + let a = generate_u256(b + 1, U256::MAX); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_a_equal_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a, + a, + Some(U256::one().to_big_endian()), + [0; 32], + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_equal_high_words_max_low_diff( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let high = generate_u256(U256::one(), U256::from(u128::MAX)); + + let a = ((high << 128) | (U256::from(u128::MAX))).to_big_endian(); + let b = (high << 128).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a, + b, + Some(U256::one().to_big_endian()), + U256::from(u128::MAX).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_eq_high_words_a_less_than_b( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let high = generate_u256(U256::one(), U256::from(u128::MAX)); + + let a = (high << 128).to_big_endian(); + let b = ((high << 128) | (U256::from(u128::MAX))).to_big_endian(); + + run( + &context, + program(), + build_witness(op(FunctionToTest::DivMod256), a, b, Some([0; 32]), a), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_mod_256_edge_case(context: simplex::TestContext) -> anyhow::Result<()> { + let a: U256 = U256::from(2).pow(U256::from(255)); + let b = U256::from(2).pow(U256::from(127)) + U256::from(2).pow(U256::from(64)) - 1; + + let (q, r) = a.div_mod(b); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::DivMod256), + a.to_big_endian(), + b.to_big_endian(), + Some(q.to_big_endian()), + r.to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::one(), U256::MAX); + let result = (a / b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Div256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_div_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Div256), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u256_test_sub_mul.rs b/tests/u256_test_sub_mul.rs new file mode 100644 index 0000000..7c164a5 --- /dev/null +++ b/tests/u256_test_sub_mul.rs @@ -0,0 +1,261 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use crate::common::helper::{DEFAULT_BOOL, generate_u256}; +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u256_test_sub_mul::U256TestSubMulProgram; +use simplicityhl_std::artifacts::u256_test_sub_mul::derived_u256_test_sub_mul::{ + U256TestSubMulArguments, U256TestSubMulWitness, +}; + +enum FunctionToTest { + Sub256, + Mul256, + Mul256_64, + Mul256_128, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +fn program() -> U256TestSubMulProgram { + U256TestSubMulProgram::new(&U256TestSubMulArguments {}) +} + +fn build_witness( + function: u8, + a: [u8; 32], + b: [u8; 32], + expected: Option<[u8; 32]>, + expected_bool: bool, + second_expected: [u8; 32], +) -> U256TestSubMulWitness { + U256TestSubMulWitness { + function_index: function, + first_arg: a, + second_arg: b, + expected, + expected_bool, + second_expected, + } +} + +fn split_u512(a: [u8; 64]) -> ([u8; 32], [u8; 32]) { + let high = U256::from_big_endian(&a[0..32]); + let low = U256::from_big_endian(&a[32..64]); + + (high.to_big_endian(), low.to_big_endian()) +} + +mod u256_tests_arithmetic { + use super::*; + + #[simplex::test] + fn u256_test_sub_256_not_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::zero(), a); + let result = (a - b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a, + a, + Some([0; 32]), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_a_low_eq_b_low(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b_high = rand::thread_rng().gen_range(0..=u128::MAX); + + let low: u128 = a.low_u128(); + let b = (U256::from(b_high) << 128) | U256::from(low); + + let (result, carry) = a.overflowing_sub(b); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result.to_big_endian()), + carry, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_diff_is_u128_max(context: simplex::TestContext) -> anyhow::Result<()> { + let a_low: u128 = u128::MAX; + + let a_high = rand::thread_rng().gen_range(0..=u128::MAX); + let b_high = rand::thread_rng().gen_range(0..=u128::MAX); + + let a = (U256::from(a_high) << 128) | U256::from(a_low); + let b = (U256::from(b_high)) << 128; // b_low is 0 + + let (result, carry) = a.overflowing_sub(b); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result.to_big_endian()), + carry, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_diff_is_u256_max(context: simplex::TestContext) -> anyhow::Result<()> { + let a = U256::MAX.to_big_endian(); + let b = U256::zero(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a, + b.to_big_endian(), + Some(a), + false, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_sub_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX - 1); + let b = U256::MAX; + let result = a + 1; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Sub256), + a.to_big_endian(), + b.to_big_endian(), + Some(result.to_big_endian()), + true, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::MAX); + let result = a.full_mul(b).to_big_endian(); + + let (result_high, result_low) = split_u512(result); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Mul256), + a.to_big_endian(), + b.to_big_endian(), + Some(result_high), + DEFAULT_BOOL, + result_low, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_256_64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u64::MAX)); + let result = a.full_mul(b).to_big_endian(); + + let (result_high, result_low) = split_u512(result); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Mul256_64), + a.to_big_endian(), + b.to_big_endian(), + Some(result_high), + DEFAULT_BOOL, + result_low, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_256_128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let result = a.full_mul(b).to_big_endian(); + + let (result_high, result_low) = split_u512(result); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Mul256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(result_high), + DEFAULT_BOOL, + result_low, + ), + Expect::Ok, + ) + } +} diff --git a/tests/u32_convert_test.rs b/tests/u32_convert_test.rs new file mode 100644 index 0000000..e7874f0 --- /dev/null +++ b/tests/u32_convert_test.rs @@ -0,0 +1,225 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u32_convert_test::U32ConvertTestProgram; +use simplicityhl_std::artifacts::u32_convert_test::derived_u32_convert_test::{ + U32ConvertTestArguments, U32ConvertTestWitness, +}; + +enum FunctionToTest { + U32ToU64, + U32ToU128, + U32ToU256, + 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_test.rs b/tests/u32_math_test.rs similarity index 52% rename from tests/u32_test.rs rename to tests/u32_math_test.rs index 7c76910..2db6eda 100644 --- a/tests/u32_test.rs +++ b/tests/u32_math_test.rs @@ -2,13 +2,15 @@ mod common; use common::uint::TestUint; -use simplicityhl_std::artifacts::u32_test::U32TestProgram; -use simplicityhl_std::artifacts::u32_test::derived_u32_test::{U32TestArguments, U32TestWitness}; +use simplicityhl_std::artifacts::u32_math_test::U32MathTestProgram; +use simplicityhl_std::artifacts::u32_math_test::derived_u32_math_test::{ + U32MathTestArguments, U32MathTestWitness, +}; // The only per-width code for the common operations. impl TestUint for u32 { - type Program = U32TestProgram; - type Witness = U32TestWitness; + type Program = U32MathTestProgram; + type Witness = U32MathTestWitness; const ZERO: u32 = 0; const ONE: u32 = 1; @@ -16,12 +18,12 @@ impl TestUint for u32 { const HALF_MAX: u32 = u32::MAX / 2; const MUL_BOUND: u32 = 1 << 16; // 2^(32/2) - fn program() -> U32TestProgram { - U32TestProgram::new(U32TestArguments {}) + fn program() -> U32MathTestProgram { + U32MathTestProgram::new(&U32MathTestArguments {}) } - fn witness(op: u8, a: u32, b: u32, expected: Option) -> U32TestWitness { - U32TestWitness { + fn witness(op: u8, a: u32, b: u32, expected: Option) -> U32MathTestWitness { + U32MathTestWitness { function_index: op, first_arg: a, second_arg: b, @@ -30,9 +32,9 @@ impl TestUint for u32 { } } -mod u32_tests { +mod u32_math_tests { use super::*; - // Stamps the 16 `#[simplex::test]` entry points for u32. Logic lives in common::uint. + // Stamps the 22 `#[simplex::test]` entry points for u32. Logic lives in common::uint. uint_tests!(u32); } diff --git a/tests/u64_convert_test.rs b/tests/u64_convert_test.rs new file mode 100644 index 0000000..050faaa --- /dev/null +++ b/tests/u64_convert_test.rs @@ -0,0 +1,260 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u64_convert_test::U64ConvertTestProgram; +use simplicityhl_std::artifacts::u64_convert_test::derived_u64_convert_test::{ + U64ConvertTestArguments, U64ConvertTestWitness, +}; + +enum FunctionToTest { + U64ToU128, + U64ToU256, + SplitU64IntoU8, + SplitU64IntoU16, + SplitU64IntoU32, + SafeU64ToU1, + SafeU64ToU8, + SafeU64ToU16, + SafeU64ToU32, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U64ConvertTestProgram { + U64ConvertTestProgram::new(&U64ConvertTestArguments {}) +} + +fn build_witness(function: u8, a: u64, expected: [u8; 32]) -> U64ConvertTestWitness { + U64ConvertTestWitness { + function_index: function, + first_arg: a, + expected, + } +} + +mod u64_convert_test { + use super::*; + + #[simplex::test] + fn u64_convert_test_u64_to_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U64ToU128), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_u64_to_u256(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::U64ToU256), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_split_u64_into_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU64IntoU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_split_u64_into_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU64IntoU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_split_u64_into_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SplitU64IntoU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u1(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u1_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU1), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX as u64); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u8_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u8::MAX as u64 + 1..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU8), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX as u64); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u16_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u16::MAX as u64 + 1..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU16), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX as u64); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_convert_test_safe_u64_to_u32_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(u32::MAX as u64 + 1..=u64::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeU64ToU32), + a, + U256::from(a).to_big_endian(), + ), + Expect::AssertFailed, + ) + } +} diff --git a/tests/u64_math_test.rs b/tests/u64_math_test.rs new file mode 100644 index 0000000..216b4bf --- /dev/null +++ b/tests/u64_math_test.rs @@ -0,0 +1,40 @@ +mod common; + +use common::uint::TestUint; + +use simplicityhl_std::artifacts::u64_math_test::U64MathTestProgram; +use simplicityhl_std::artifacts::u64_math_test::derived_u64_math_test::{ + U64MathTestArguments, U64MathTestWitness, +}; + +// The only per-width code for the common operations. +impl TestUint for u64 { + type Program = U64MathTestProgram; + type Witness = U64MathTestWitness; + + const ZERO: u64 = 0; + const ONE: u64 = 1; + const MAX: u64 = u64::MAX; + const HALF_MAX: u64 = u64::MAX / 2; + const MUL_BOUND: u64 = 1 << 32; // 2^(64/2) + + fn program() -> U64MathTestProgram { + U64MathTestProgram::new(&U64MathTestArguments {}) + } + + fn witness(op: u8, a: u64, b: u64, expected: Option) -> U64MathTestWitness { + U64MathTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + expected, + } + } +} + +mod u64_math_tests { + use super::*; + + // Stamps the 22 `#[simplex::test]` entry points for u64. Logic lives in common::uint. + uint_tests!(u64); +} diff --git a/tests/u64_test.rs b/tests/u64_test.rs deleted file mode 100644 index 59cbed8..0000000 --- a/tests/u64_test.rs +++ /dev/null @@ -1,75 +0,0 @@ -mod common; - -use common::uint::TestUint; - -use simplicityhl_std::artifacts::u64_test::U64TestProgram; -use simplicityhl_std::artifacts::u64_test::derived_u64_test::{U64TestArguments, U64TestWitness}; - -use crate::common::uint::CUSTOM_BASE; - -// The only per-width code for the common operations. -impl TestUint for u64 { - type Program = U64TestProgram; - type Witness = U64TestWitness; - - const ZERO: u64 = 0; - const ONE: u64 = 1; - const MAX: u64 = u64::MAX; - const HALF_MAX: u64 = u64::MAX / 2; - const MUL_BOUND: u64 = 1 << 32; // 2^(64/2) - - fn program() -> U64TestProgram { - U64TestProgram::new(U64TestArguments {}) - } - - fn witness(op: u8, a: u64, b: u64, expected: Option) -> U64TestWitness { - U64TestWitness { - function_index: op, - first_arg: a, - second_arg: b, - expected, - ..Default::default() - } - } -} - -enum FunctionToTest { - U64Widen, -} - -#[inline] -fn op(o: FunctionToTest) -> u8 { - o as u8 + CUSTOM_BASE -} - -mod u64_tests { - use rand::Rng as _; - - use crate::FunctionToTest::U64Widen; - use crate::common::core::{Expect, run}; - - use super::*; - - // Stamps the 16 `#[simplex::test]` entry points for u64. Logic lives in common::uint. - uint_tests!(u64); - - #[simplex::test] - fn u64_into_u256(context: simplex::TestContext) -> anyhow::Result<()> { - let a = rand::thread_rng().gen_range(0..=u64::MAX); - - let mut expected_widen: [u8; 32] = [0u8; 32]; - expected_widen[24..32].copy_from_slice(&a.to_be_bytes()); - - run( - &context, - ::program(), - U64TestWitness { - function_index: op(U64Widen), - first_arg: a, - expected_widen, - ..Default::default() - }, - Expect::Ok, - ) - } -} diff --git a/tests/u8_convert_test.rs b/tests/u8_convert_test.rs new file mode 100644 index 0000000..c44ecd0 --- /dev/null +++ b/tests/u8_convert_test.rs @@ -0,0 +1,170 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u8_convert_test::U8ConvertTestProgram; +use simplicityhl_std::artifacts::u8_convert_test::derived_u8_convert_test::{ + U8ConvertTestArguments, U8ConvertTestWitness, +}; + +enum FunctionToTest { + U8ToU16, + U8ToU32, + U8ToU64, + U8ToU128, + U8ToU256, + 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_test.rs b/tests/u8_math_tests.rs similarity index 52% rename from tests/u8_test.rs rename to tests/u8_math_tests.rs index 90bfaff..3ff3d7b 100644 --- a/tests/u8_test.rs +++ b/tests/u8_math_tests.rs @@ -2,13 +2,15 @@ mod common; use common::uint::TestUint; -use simplicityhl_std::artifacts::u8_test::U8TestProgram; -use simplicityhl_std::artifacts::u8_test::derived_u8_test::{U8TestArguments, U8TestWitness}; +use simplicityhl_std::artifacts::u8_math_test::U8MathTestProgram; +use simplicityhl_std::artifacts::u8_math_test::derived_u8_math_test::{ + U8MathTestArguments, U8MathTestWitness, +}; // The only per-width code for the common operations. impl TestUint for u8 { - type Program = U8TestProgram; - type Witness = U8TestWitness; + type Program = U8MathTestProgram; + type Witness = U8MathTestWitness; const ZERO: u8 = 0; const ONE: u8 = 1; @@ -16,12 +18,12 @@ impl TestUint for u8 { const HALF_MAX: u8 = u8::MAX / 2; const MUL_BOUND: u8 = 1 << 4; // 2^(8/2) - fn program() -> U8TestProgram { - U8TestProgram::new(U8TestArguments {}) + fn program() -> U8MathTestProgram { + U8MathTestProgram::new(&U8MathTestArguments {}) } - fn witness(op: u8, a: u8, b: u8, expected: Option) -> U8TestWitness { - U8TestWitness { + fn witness(op: u8, a: u8, b: u8, expected: Option) -> U8MathTestWitness { + U8MathTestWitness { function_index: op, first_arg: a, second_arg: b, @@ -30,9 +32,9 @@ impl TestUint for u8 { } } -mod u8_tests { +mod u8_math_tests { use super::*; - // Stamps the 16 `#[simplex::test]` entry points for u8. Logic lives in common::uint. + // Stamps the 22 `#[simplex::test]` entry points for u8. Logic lives in common::uint. uint_tests!(u8); }