diff --git a/Cargo.lock b/Cargo.lock index 7001e42..7eb322e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -82,6 +82,16 @@ dependencies = [ "object", ] +[[package]] +name = "ariadne" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31beedec3ce83ae6da3a79592b3d8d7afd146a5b15bb9bb940279aced60faa89" +dependencies = [ + "unicode-width", + "yansi", +] + [[package]] name = "arrayvec" version = "0.7.8" @@ -96,9 +106,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-lc-rs" -version = "1.18.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e" +checksum = "b281d307588d634de920874890732659e2e7672f72b5e10e81badc1a8a83621e" dependencies = [ "aws-lc-sys", "zeroize", @@ -106,9 +116,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.44.0" +version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483" +checksum = "9bff6c3b54fad79a2e60b8102caf565819711497c1f5f092f49508e2f5c31b27" dependencies = [ "cc", "cmake", @@ -169,7 +179,7 @@ dependencies = [ "bitcoin-io", "bitcoin-units", "bitcoin_hashes", - "hex-conservative 0.2.2", + "hex-conservative 0.2.3", "hex_lit", "secp256k1", "serde", @@ -182,7 +192,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6712f9c6fd6785b3b270884e57c441c403dc5d7e19ca45368c97c7a1de3000ec" dependencies = [ "bitcoin-internals", - "hex-conservative 1.2.0", + "hex-conservative 1.3.0", "serde", ] @@ -224,7 +234,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bca4c7abb40c8817d77403c880988cfd484f23ab2365726afb2f798363e2c4a2" dependencies = [ "bitcoin-io", - "hex-conservative 0.2.2", + "hex-conservative 0.2.3", "serde", ] @@ -273,9 +283,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.1" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" [[package]] name = "bitvec" @@ -328,9 +338,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "cc" -version = "1.4.3" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "509591b7bcd67f4ef775afad7662703b4935daaa6ec0e5605cfb1090b32a2b6d" +checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80" dependencies = [ "find-msvc-tools", "jobserver", @@ -432,9 +442,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" +checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -442,18 +452,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.20" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.22" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" [[package]] name = "crunchy" @@ -496,9 +506,9 @@ checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" [[package]] name = "either" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" dependencies = [ "serde", ] @@ -577,9 +587,9 @@ checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "find-msvc-tools" -version = "0.1.11" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" [[package]] name = "fixed-hash" @@ -700,7 +710,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf760ebf69878d9fd8f110c89703d90ce35095324d1f1edcb595c63945ee757" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.13.2", "ignore", "walkdir", ] @@ -730,18 +740,18 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-conservative" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda06d18ac606267c40c04e41b9947729bf8b9efe74bd4e82b61a5f26a510b9f" +checksum = "db3fef046dca3ca91ee1408a8c1b80ab777e80a4d308d1bf4e7adb3fcb047e08" dependencies = [ "arrayvec", ] [[package]] name = "hex-conservative" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35431185f361ccf3ffc58254628af5f1f5d5f28531da2e02e5d6c82bbc282a10" +checksum = "271e0d19bcb473b6675739a2b536076b24a082316cb5199ad918edce10c599e8" dependencies = [ "arrayvec", ] @@ -808,9 +818,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.14.0" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", "hashbrown 0.17.1", @@ -849,9 +859,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.104" +version = "0.3.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" dependencies = [ "cfg-if", "futures-util", @@ -905,9 +915,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "memchr" @@ -1153,9 +1163,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "libc", "rand_chacha", @@ -1241,7 +1251,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.4.15", @@ -1254,7 +1264,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.1", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys 0.12.1", @@ -1263,9 +1273,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.43" +version = "0.23.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" +checksum = "6725596c3f2c3a0aef021139e145d4eafe314a6623e4680ca83852b2c67ab2ba" dependencies = [ "aws-lc-rs", "log", @@ -1287,9 +1297,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.14" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "aws-lc-rs", "ring", @@ -1356,6 +1366,12 @@ dependencies = [ "secp256k1-sys", ] +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + [[package]] name = "serde" version = "1.0.229" @@ -1383,7 +1399,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.5", ] [[package]] @@ -1437,7 +1453,7 @@ dependencies = [ "elements", "getrandom 0.2.17", "ghost-cell", - "hex-conservative 0.2.2", + "hex-conservative 0.2.3", "miniscript", "simplicity-sys", ] @@ -1454,10 +1470,11 @@ dependencies = [ [[package]] name = "simplicityhl" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "361316795ec753230c421d964ab60940d20d5252c2d39272452528832c9e0ec5" +checksum = "ae77168fa3367d234b11bb3999cbd6e04d7180db0b9e11f83de259528e3b23d1" dependencies = [ + "ariadne", "base64 0.21.7", "chumsky", "clap", @@ -1465,6 +1482,7 @@ dependencies = [ "getrandom 0.2.17", "itertools", "miniscript", + "semver", "serde", "serde_json", "simplicity-lang", @@ -1491,9 +1509,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smplx-build" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5a566aed019a661983dc6f1e420c6431f1cd47c0e3b2d3fabdeb9de75f0c07" +checksum = "ef1538c2de418c94bfec9824b140a0f8bc2f2542cde0f18ef1b2e3a23e5c9eb1" dependencies = [ "glob", "globwalk", @@ -1512,9 +1530,9 @@ dependencies = [ [[package]] name = "smplx-macros" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d68dfea03cfb3282d1c28c9cb80ccb683dc015d1f6ab2f1488806f7e9502fe2" +checksum = "8eb08baf344083ccad07886156cbd92feb2d66b1886b5b73e61a9e03db2ef8f7" dependencies = [ "smplx-build", "smplx-test", @@ -1523,9 +1541,9 @@ dependencies = [ [[package]] name = "smplx-regtest" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a0c9536049b55eebe96cbbd46c6b51026ae964f7c2e664cb4297af9b36178" +checksum = "11d419c9a387fcd2944ac487dff8ced762fc1d52e21186366ab70e7c899931a6" dependencies = [ "electrsd", "hex", @@ -1539,9 +1557,9 @@ dependencies = [ [[package]] name = "smplx-sdk" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "700f68a14dbbe1e6b214965470197e12505693f7e0fd82360efb53d5430cb823" +checksum = "3ac052541ca5611bcdd23d46317970b2065c73ebc7864e53ff5607b30c96dfa9" dependencies = [ "bip39", "bitcoin_hashes", @@ -1559,9 +1577,9 @@ dependencies = [ [[package]] name = "smplx-std" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b11221eb218a73f1d9e20c57364905e62351c43feb601dbc64d406137ebd63c" +checksum = "c6a9e1bdf1d8139fbdf2be662fdcd11761ae61604aeb78628ced8506f7c6a7b9" dependencies = [ "either", "serde", @@ -1573,9 +1591,9 @@ dependencies = [ [[package]] name = "smplx-test" -version = "0.0.10" +version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae375c32c2f220320459710b54bbd7d7289bde0a45daa20cc0193a1999cd98" +checksum = "a3bd10a1047d731372fc1589ebf2a76444ad8c65d352f84115718e6345f3aa0c" dependencies = [ "electrsd", "proc-macro2", @@ -1633,9 +1651,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" dependencies = [ "proc-macro2", "quote", @@ -1678,14 +1696,14 @@ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.5", ] [[package]] name = "tinyvec" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +checksum = "4cf0ded5c4e56918d8f8a339e1bb67d038d3bc6d144ac407904015ba2e4cde9b" dependencies = [ "tinyvec_macros", ] @@ -1796,6 +1814,12 @@ version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "unicode-xid" version = "0.2.6" @@ -1838,9 +1862,9 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasm-bindgen" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" dependencies = [ "cfg-if", "once_cell", @@ -1851,9 +1875,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1861,22 +1885,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.5", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" dependencies = [ "unicode-ident", ] @@ -2032,20 +2056,26 @@ dependencies = [ "tap", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + [[package]] name = "zerocopy" -version = "0.8.56" +version = "0.8.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" +checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.56" +version = "0.8.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" +checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index b918f7b..d54cc7f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ keywords = ["simplicity", "liquid", "elements", "smart-contracts"] categories = ["cryptography::cryptocurrencies"] [dependencies] -smplx-std = { version = "0.0.10" } +smplx-std = { version = "0.0.11" } [dev-dependencies] anyhow = { version = "1.0.101" } diff --git a/docs/stdlib.json b/docs/stdlib.json index 8fe6358..e51a52a 100644 --- a/docs/stdlib.json +++ b/docs/stdlib.json @@ -391,6 +391,13 @@ "output_type": "bool", "description": "Check if the first `u8` value is greater than or equal to the second." }, + { + "simplicityhl_name": "mul_div_8", + "section": "`u8` arithmetic", + "input_type": "u8, u8, u8", + "output_type": "u8", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u8." + }, { "simplicityhl_name": "u8_to_u16", "section": "`u8` conversions", @@ -510,6 +517,13 @@ "output_type": "bool", "description": "Check if the first `u16` value is greater than or equal to the second." }, + { + "simplicityhl_name": "mul_div_16", + "section": "`u16` arithmetic", + "input_type": "u16, u16, u16", + "output_type": "u16", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u16." + }, { "simplicityhl_name": "u16_to_u32", "section": "`u16` conversions", @@ -629,6 +643,13 @@ "output_type": "bool", "description": "Check if the first `u32` value is greater than or equal to the second." }, + { + "simplicityhl_name": "mul_div_32", + "section": "`u32` arithmetic", + "input_type": "u32, u32, u32", + "output_type": "u32", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u32." + }, { "simplicityhl_name": "u32_to_u64", "section": "`u32` conversions", @@ -755,6 +776,13 @@ "output_type": "bool", "description": "Check if the first `u64` value is greater than or equal to the second." }, + { + "simplicityhl_name": "mul_div_64", + "section": "`u64` arithmetic", + "input_type": "u64, u64, u64", + "output_type": "u64", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u64." + }, { "simplicityhl_name": "u64_to_u128", "section": "`u64` conversions", @@ -818,6 +846,153 @@ "output_type": "u32", "description": "Narrow a `u64` value to `u32`.\n\n## Panics\nPanics if the value does not fit in `u32`." }, + { + "simplicityhl_name": "add_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "(bool, u128)", + "description": "Add two `u128` values. Return the carry bit and the sum." + }, + { + "simplicityhl_name": "add_128_64", + "section": "`u128` arithmetic", + "input_type": "u128, u64", + "output_type": "(bool, u128)", + "description": "Add a `u64` value to a `u128` value. Return the carry bit and the sum." + }, + { + "simplicityhl_name": "checked_add_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "Option", + "description": "Add two `u128` values. Return `Some` of the sum, or `None` if the result overflows `u128`." + }, + { + "simplicityhl_name": "safe_add_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u128", + "description": "Add two `u128` values.\n\n## Panics\nPanics if the result overflows `u128`." + }, + { + "simplicityhl_name": "sub_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "(bool, u128)", + "description": "Subtract the second `u128` value from the first. Return the borrow bit and the difference." + }, + { + "simplicityhl_name": "checked_sub_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "Option", + "description": "Subtract the second `u128` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u128`." + }, + { + "simplicityhl_name": "safe_sub_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u128", + "description": "Subtract the second `u128` value from the first.\n\n## Panics\nPanics if the result would underflow `u128`." + }, + { + "simplicityhl_name": "mul_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u256", + "description": "Multiply two `u128` values. The full, non-truncated product is returned as a `u256`, so this operation can never overflow." + }, + { + "simplicityhl_name": "checked_mul_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "Option", + "description": "Multiply two `u128` values. Return `Some` of the product, or `None` if the result overflows `u128`." + }, + { + "simplicityhl_name": "safe_mul_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u128", + "description": "Multiply two `u128` values.\n\n## Panics\nPanics if the result overflows `u128`." + }, + { + "simplicityhl_name": "div_mod_128_64", + "section": "`u128` arithmetic", + "input_type": "u128, u64", + "output_type": "(u128, u64)", + "description": "Divide a `u128` value by a `u64` value, returning the `u128` quotient and the `u64` remainder.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "div_mod_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "(u128, u128)", + "description": "Divide the first `u128` value by the second, returning the quotient and the remainder.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "div_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u128", + "description": "Divide the first `u128` value by the second, returning the quotient.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "checked_div_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "Option", + "description": "Divide the first `u128` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." + }, + { + "simplicityhl_name": "safe_div_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128", + "output_type": "u128", + "description": "Divide the first `u128` value by the second.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "full_add_128", + "section": "`u128` arithmetic", + "input_type": "bool, u128, u128", + "output_type": "(bool, u128)", + "description": "Add two `u128` values, taking an incoming carry bit. Return the outgoing carry bit and the sum." + }, + { + "simplicityhl_name": "full_sub_128", + "section": "`u128` arithmetic", + "input_type": "bool, u128, u128", + "output_type": "(bool, u128)", + "description": "Subtract the second `u128` value from the first, taking an incoming borrow bit. Return the outgoing borrow bit and the difference." + }, + { + "simplicityhl_name": "mul_128_64", + "section": "`u128` arithmetic", + "input_type": "u128, u64", + "output_type": "u256", + "description": "Multiply a `u128` value by a `u64` value. The full, non-truncated product is returned as a `u256`, so this operation can never overflow." + }, + { + "simplicityhl_name": "calculate_normalizer_base_64", + "section": "`u128` arithmetic", + "input_type": "u128, bool", + "output_type": "u64", + "description": "Helper for `jet::div_mod_128_64`-based division algorithms. Returns the factor by which `b` should be multiplied so that its most-significant non-zero word is at least `2^63`, as required by those algorithms (which operate in base `2^64`). Set `is_b_u128` to `true` if `b`'s upper 64 bits may be non-zero, or `false` if `b` is known to fit in `u64` (in which case its upper 64 bits must already be zero).\n\n## Panics\nThe assertion fails if `is_b_u128` is `false` but `b`'s upper 64 bits are non-zero, or if `b` is zero." + }, + { + "simplicityhl_name": "estimate_quotient_digit_base_64", + "section": "`u128` arithmetic", + "input_type": "u64, u64, u64, u64, u64", + "output_type": "u64", + "description": "Helper for Algorithm D division. Estimates and corrects the next base-`2^64` quotient digit from the three most-significant dividend words (`u2`, `u1`, `u0`) and the two most-significant divisor words (`v1`, `v0`)." + }, + { + "simplicityhl_name": "mul_div_128", + "section": "`u128` arithmetic", + "input_type": "u128, u128, u128", + "output_type": "u128", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u128." + }, { "simplicityhl_name": "and_128", "section": "`u128` bit logic", @@ -959,144 +1134,172 @@ "description": "Narrow a `u128` value to `u64`.\n\n## Panics\nPanics if the value does not fit in `u64`." }, { - "simplicityhl_name": "add_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "(bool, u128)", - "description": "Add two `u128` values. Return the carry bit and the sum." + "simplicityhl_name": "add_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "(bool, u256)", + "description": "Add two `u256` values. Return the carry bit and the sum." }, { - "simplicityhl_name": "add_128_64", - "section": "`u128` arithmetic", - "input_type": "u128, u64", - "output_type": "(bool, u128)", - "description": "Add a `u64` value to a `u128` value. Return the carry bit and the sum." + "simplicityhl_name": "add_256_128", + "section": "`u256` arithmetic", + "input_type": "u256, u128", + "output_type": "(bool, u256)", + "description": "Add a `u128` value to a `u256` value. Return the carry bit and the sum." }, { - "simplicityhl_name": "checked_add_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "Option", - "description": "Add two `u128` values. Return `Some` of the sum, or `None` if the result overflows `u128`." + "simplicityhl_name": "full_add_256", + "section": "`u256` arithmetic", + "input_type": "bool, u256, u256", + "output_type": "(bool, u256)", + "description": "Add two u256 values, taking an incoming carry bit. Return the outgoing carry bit and the sum." }, { - "simplicityhl_name": "safe_add_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "u128", - "description": "Add two `u128` values.\n\n## Panics\nPanics if the result overflows `u128`." + "simplicityhl_name": "checked_add_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "Option", + "description": "Add two `u256` values. Return `Some` of the sum, or `None` if the result overflows `u256`." }, { - "simplicityhl_name": "sub_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "(bool, u128)", - "description": "Subtract the second `u128` value from the first. Return the borrow bit and the difference." + "simplicityhl_name": "safe_add_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "u256", + "description": "Add two `u256` values.\n\n## Panics\nPanics if the result overflows `u256`." }, { - "simplicityhl_name": "checked_sub_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "Option", - "description": "Subtract the second `u128` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u128`." + "simplicityhl_name": "sub_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "(bool, u256)", + "description": "Subtract the second `u256` value from the first. Return the borrow bit and the difference." }, { - "simplicityhl_name": "safe_sub_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "u128", - "description": "Subtract the second `u128` value from the first.\n\n## Panics\nPanics if the result would underflow `u128`." + "simplicityhl_name": "checked_sub_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "Option", + "description": "Subtract the second `u256` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u256`." }, { - "simplicityhl_name": "mul_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", + "simplicityhl_name": "safe_sub_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", "output_type": "u256", - "description": "Multiply two `u128` values. The full, non-truncated product is returned as a `u256`, so this operation can never overflow." + "description": "Subtract the second `u256` value from the first.\n\n## Panics\nPanics if the result would underflow `u256`." }, { - "simplicityhl_name": "checked_mul_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "Option", - "description": "Multiply two `u128` values. Return `Some` of the product, or `None` if the result overflows `u128`." + "simplicityhl_name": "mul_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "(u256, u256)", + "description": "Multiply two `u256` values. The full, non-truncated product is returned as a pair of `u256` values, most-significant first, so this operation can never overflow." }, { - "simplicityhl_name": "safe_mul_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "u128", - "description": "Multiply two `u128` values.\n\n## Panics\nPanics if the result overflows `u128`." + "simplicityhl_name": "mul_256_64", + "section": "`u256` arithmetic", + "input_type": "u256, u64", + "output_type": "(u64, u256)", + "description": "Multiply a `u256` value by a `u64` value. The full, non-truncated product is returned as a `u64`/`u256` pair, most-significant first, so this operation can never overflow." }, { - "simplicityhl_name": "div_mod_128_64", - "section": "`u128` arithmetic", - "input_type": "u128, u64", - "output_type": "(u128, u64)", - "description": "Divide a `u128` value by a `u64` value, returning the `u128` quotient and the `u64` remainder.\n\n## Panics\nPanics if the divisor is zero." + "simplicityhl_name": "mul_256_128", + "section": "`u256` arithmetic", + "input_type": "u256, u128", + "output_type": "(u128, u256)", + "description": "Multiply a `u256` value by a `u128` value. The full, non-truncated product is returned as a `u128`/`u256` pair, most-significant first, so this operation can never overflow." }, { - "simplicityhl_name": "div_mod_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "(u128, u128)", - "description": "Divide the first `u128` value by the second, returning the quotient and the remainder.\n\n## Panics\nPanics if the divisor is zero." + "simplicityhl_name": "mul_512_128", + "section": "`u256` arithmetic", + "input_type": "u256, u256, u128", + "output_type": "(u128, u256, u256)", + "description": "Multiply a `u512` value by a `u128` value. The full, non-truncated product is returned as a (`u128`, `u256`, `u256`) tuple, most-significant first, so this operation can never overflow." }, { - "simplicityhl_name": "div_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "u128", - "description": "Divide the first `u128` value by the second, returning the quotient.\n\n## Panics\nPanics if the divisor is zero." + "simplicityhl_name": "checked_mul_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "Option", + "description": "Multiply two `u256` values. Return `Some` of the product, or `None` if the result overflows `u256`." }, { - "simplicityhl_name": "checked_div_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", - "output_type": "Option", - "description": "Divide the first `u128` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." + "simplicityhl_name": "safe_mul_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "u256", + "description": "Multiply two `u256` values.\n\n## Panics\nPanics if the result overflows `u256`." }, { - "simplicityhl_name": "safe_div_128", - "section": "`u128` arithmetic", - "input_type": "u128, u128", + "simplicityhl_name": "safe_mul_256_128", + "section": "`u256` arithmetic", + "input_type": "u256, u128", + "output_type": "u256", + "description": "Multiply a `u256` value by a `u128` value.\n\n## Panics\nPanics if the result overflows `u256`." + }, + { + "simplicityhl_name": "calculate_normalizer_base_128", + "section": "`u256` arithmetic", + "input_type": "u256", "output_type": "u128", - "description": "Divide the first `u128` value by the second.\n\n## Panics\nPanics if the divisor is zero." + "description": "Helper for algorithm D division algorithms. Returns the factor by which `b` should be multiplied so that its upper 128 bits are at least `2^127`, as required by those algorithms (which operate in base `2^128`).\n\n## Panics\nThe assertion fails if `b`'s upper 128 bits are zero" }, { - "simplicityhl_name": "full_add_128", - "section": "`u128` arithmetic", - "input_type": "bool, u128, u128", - "output_type": "(bool, u128)", - "description": "Add two `u128` values, taking an incoming carry bit. Return the outgoing carry bit and the sum." + "simplicityhl_name": "div_mod_256_64", + "section": "`u256` arithmetic", + "input_type": "u256, u64", + "output_type": "(u256, u64)", + "description": "Divide a `u256` value by a `u64` value, returning the `u256` quotient and the `u64` remainder.\n\n## Panics\nPanics if the divisor is zero." }, { - "simplicityhl_name": "full_sub_128", - "section": "`u128` arithmetic", - "input_type": "bool, u128, u128", - "output_type": "(bool, u128)", - "description": "Subtract the second `u128` value from the first, taking an incoming borrow bit. Return the outgoing borrow bit and the difference." + "simplicityhl_name": "algorithm_d_256_128", + "section": "`u256` arithmetic", + "input_type": "u256, u128", + "output_type": "(u256, u128)", + "description": "Divide a `u256` value by a `u128` value, returning the `u256` quotient and the `u128` remainder.\n\n## Panics\nPanics if the divisor is zero or if divisor's upper 64 bits are zero." }, { - "simplicityhl_name": "mul_128_64", - "section": "`u128` arithmetic", - "input_type": "u128, u64", + "simplicityhl_name": "div_mod_256_128", + "section": "`u256` arithmetic", + "input_type": "u256, u128", + "output_type": "(u256, u128)", + "description": "Divide a `u256` value by a `u128` value, returning the `u256` quotient and the `u128` remainder.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "div_mod_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "(u256, u256)", + "description": "Divide the first `u256` value by the second, returning the quotient and the remainder.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "div_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", "output_type": "u256", - "description": "Multiply a `u128` value by a `u64` value. The full, non-truncated product is returned as a `u256`, so this operation can never overflow." + "description": "Divide the first `u256` value by the second, returning the quotient.\n\n## Panics\nPanics if the divisor is zero." }, { - "simplicityhl_name": "calculate_normalizer_base_64", - "section": "`u128` arithmetic", - "input_type": "u128, bool", - "output_type": "u64", - "description": "Helper for `jet::div_mod_128_64`-based division algorithms. Returns the factor by which `b` should be multiplied so that its most-significant non-zero word is at least `2^63`, as required by those algorithms (which operate in base `2^64`). Set `is_b_u128` to `true` if `b`'s upper 64 bits may be non-zero, or `false` if `b` is known to fit in `u64` (in which case its upper 64 bits must already be zero).\n\n## Panics\nThe assertion fails if `is_b_u128` is `false` but `b`'s upper 64 bits are non-zero, or if `b` is zero." + "simplicityhl_name": "checked_div_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "Option", + "description": "Divide the first `u256` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." }, { - "simplicityhl_name": "estimate_quotient_digit_base_64", - "section": "`u128` arithmetic", - "input_type": "u64, u64, u64, u64, u64", - "output_type": "u64", - "description": "Helper for Algorithm D division. Estimates and corrects the next base-`2^64` quotient digit from the three most-significant dividend words (`u2`, `u1`, `u0`) and the two most-significant divisor words (`v1`, `v0`)." + "simplicityhl_name": "safe_div_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256", + "output_type": "u256", + "description": "Divide the first `u256` value by the second.\n\n## Panics\nPanics if the divisor is zero." + }, + { + "simplicityhl_name": "mul_div_256", + "section": "`u256` arithmetic", + "input_type": "u256, u256, u256", + "output_type": "u256", + "description": "Calculate `floor(a * b / denominator)` with full precision. \n\n## Panics\nPanics if the result overflows a u256." }, { "simplicityhl_name": "and_256", @@ -1237,131 +1440,5 @@ "input_type": "u256", "output_type": "u128", "description": "Narrow a `u256` value to `u128`.\n\n## Panics\nPanics if the value does not fit in `u128`." - }, - { - "simplicityhl_name": "add_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "(bool, u256)", - "description": "Add two `u256` values. Return the carry bit and the sum." - }, - { - "simplicityhl_name": "add_256_128", - "section": "`u256` arithmetic", - "input_type": "u256, u128", - "output_type": "(bool, u256)", - "description": "Add a `u128` value to a `u256` value. Return the carry bit and the sum." - }, - { - "simplicityhl_name": "checked_add_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "Option", - "description": "Add two `u256` values. Return `Some` of the sum, or `None` if the result overflows `u256`." - }, - { - "simplicityhl_name": "safe_add_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "u256", - "description": "Add two `u256` values.\n\n## Panics\nPanics if the result overflows `u256`." - }, - { - "simplicityhl_name": "sub_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "(bool, u256)", - "description": "Subtract the second `u256` value from the first. Return the borrow bit and the difference." - }, - { - "simplicityhl_name": "checked_sub_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "Option", - "description": "Subtract the second `u256` value from the first. Return `Some` of the difference, or `None` if the result would underflow `u256`." - }, - { - "simplicityhl_name": "safe_sub_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "u256", - "description": "Subtract the second `u256` value from the first.\n\n## Panics\nPanics if the result would underflow `u256`." - }, - { - "simplicityhl_name": "mul_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "(u256, u256)", - "description": "Multiply two `u256` values. The full, non-truncated product is returned as a pair of `u256` values, most-significant first, so this operation can never overflow." - }, - { - "simplicityhl_name": "mul_256_64", - "section": "`u256` arithmetic", - "input_type": "u256, u64", - "output_type": "(u64, u256)", - "description": "Multiply a `u256` value by a `u64` value. The full, non-truncated product is returned as a `u64`/`u256` pair, most-significant first, so this operation can never overflow." - }, - { - "simplicityhl_name": "mul_256_128", - "section": "`u256` arithmetic", - "input_type": "u256, u128", - "output_type": "(u128, u256)", - "description": "Multiply a `u256` value by a `u128` value. The full, non-truncated product is returned as a `u128`/`u256` pair, most-significant first, so this operation can never overflow." - }, - { - "simplicityhl_name": "checked_mul_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "Option", - "description": "Multiply two `u256` values. Return `Some` of the product, or `None` if the result overflows `u256`." - }, - { - "simplicityhl_name": "safe_mul_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "u256", - "description": "Multiply two `u256` values.\n\n## Panics\nPanics if the result overflows `u256`." - }, - { - "simplicityhl_name": "div_mod_256_64", - "section": "`u256` arithmetic", - "input_type": "u256, u64", - "output_type": "(u256, u64)", - "description": "Divide a `u256` value by a `u64` value, returning the `u256` quotient and the `u64` remainder.\n\n## Panics\nPanics if the divisor is zero." - }, - { - "simplicityhl_name": "div_mod_256_128", - "section": "`u256` arithmetic", - "input_type": "u256, u128", - "output_type": "(u256, u128)", - "description": "Divide a `u256` value by a `u128` value, returning the `u256` quotient and the `u128` remainder.\n\n## Panics\nPanics if the divisor is zero." - }, - { - "simplicityhl_name": "div_mod_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "(u256, u256)", - "description": "Divide the first `u256` value by the second, returning the quotient and the remainder.\n\n## Panics\nPanics if the divisor is zero." - }, - { - "simplicityhl_name": "div_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "u256", - "description": "Divide the first `u256` value by the second, returning the quotient.\n\n## Panics\nPanics if the divisor is zero." - }, - { - "simplicityhl_name": "checked_div_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "Option", - "description": "Divide the first `u256` value by the second. Return `Some` of the quotient, or `None` if the divisor is zero." - }, - { - "simplicityhl_name": "safe_div_256", - "section": "`u256` arithmetic", - "input_type": "u256, u256", - "output_type": "u256", - "description": "Divide the first `u256` value by the second.\n\n## Panics\nPanics if the divisor is zero." } -] +] \ No newline at end of file diff --git a/simf/lib/u128/math.simf b/simf/lib/u128/math.simf index 1909850..38a679d 100644 --- a/simf/lib/u128/math.simf +++ b/simf/lib/u128/math.simf @@ -1,12 +1,15 @@ use crate::lib::binary::{not, or, and}; +use crate::lib::u64::convert::u64_to_u128; + use crate::lib::u128::comparison::{is_zero_128, lt_128}; +use crate::lib::u128::convert::{u128_to_u256, split_u128_into_u64}; /// Arithmetic /// Adds two integers and returns the carry pub fn add_128(a: u128, b: u128) -> (bool, u128) { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); let (carry_low, sum_low): (bool, u64) = jet::add_64(a_low, b_low); let (carry_high, sum_high): (bool, u64) = jet::full_add_64(carry_low, a_high, b_high); @@ -17,7 +20,7 @@ pub fn add_128(a: u128, b: u128) -> (bool, u128) { /// Adds the 128-bit integer with the 64-bit integer and returns the carry pub fn add_128_64(a: u128, b: u64) -> (bool, u128) { - let (a_high, a_low): (u64, u64) = ::into(a); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); let (carry_low, res_low): (bool, u64) = jet::add_64(a_low, b); let (carry_high, res_high): (bool, u64) = jet::full_add_64(carry_low, a_high, 0); @@ -28,8 +31,8 @@ pub fn add_128_64(a: u128, b: u64) -> (bool, u128) { /// Adds two integers. Takes a carry-in and returns a carry-out pub fn full_add_128(carry_in: bool, a: u128, b: u128) -> (bool, u128) { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); let (carry_low, sum_low): (bool, u64) = jet::full_add_64(carry_in, a_low, b_low); let (carry_out, sum_high): (bool, u64) = jet::full_add_64(carry_low, a_high, b_high); @@ -55,8 +58,8 @@ pub fn safe_add_128(a: u128, b: u128) -> u128 { /// Subtracts the second integer from the first integer, and returns the borrow bit pub fn sub_128(a: u128, b: u128) -> (bool, u128) { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); let (borrow_low, diff_low): (bool, u64) = jet::subtract_64(a_low, b_low); let (borrow_high, diff_high): (bool, u64) = jet::full_subtract_64(borrow_low, a_high, b_high); @@ -67,8 +70,8 @@ pub fn sub_128(a: u128, b: u128) -> (bool, u128) { /// Subtracts the second integer from the first integer, takes a borrow-in and returns a borrow-out pub fn full_sub_128(borrow_in: bool, a: u128, b: u128) -> (bool, u128) { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); let (borrow_low, diff_low): (bool, u64) = jet::full_subtract_64(borrow_in, a_low, b_low); let (borrow_out, diff_high): (bool, u64) = jet::full_subtract_64(borrow_low, a_high, b_high); @@ -98,19 +101,19 @@ pub fn safe_sub_128(a: u128, b: u128) -> u128 { /// In the same way, b = b_high * 2^64 + b_low. /// Therefore, a * b = 2^128 * a_high * b_high + 2^64(a_high * b_low + a_low * b_high) + a_low * b_low. pub fn mul_128(a: u128, b: u128) -> u256 { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); let highest: u128 = jet::multiply_64(a_high, b_high); let lowest: u128 = jet::multiply_64(a_low, b_low); - let (word_1, word_0): (u64, u64) = ::into(lowest); - let (word_3, word_2): (u64, u64) = ::into(highest); + let (word_1, word_0): (u64, u64) = split_u128_into_u64(lowest); + let (word_3, word_2): (u64, u64) = split_u128_into_u64(highest); let product_1: u128 = jet::multiply_64(a_high, b_low); let product_2: u128 = jet::multiply_64(b_high, a_low); let (carry_3a, middle): (bool, u128) = add_128(product_1, product_2); - let (middle_2, middle_1): (u64, u64) = ::into(middle); + let (middle_2, middle_1): (u64, u64) = split_u128_into_u64(middle); // fold the low-side carry directly into the word_2 addition via full_add_64, // then propagate any resulting carry into word_3 @@ -136,13 +139,13 @@ pub fn mul_128(a: u128, b: u128) -> u256 { /// so a = a_high * 2^64 + a_low. /// Therefore, a * b = 2^64 * a_high * b + a_low * b. pub fn mul_128_64(a: u128, b: u64) -> u256 { - let (a_high, a_low): (u64, u64) = ::into(a); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); let highest: u128 = jet::multiply_64(a_high, b); let lowest: u128 = jet::multiply_64(a_low, b); - let (word_1, word_0): (u64, u64) = ::into(lowest); - let (word_3, word_2): (u64, u64) = ::into(highest); + let (word_1, word_0): (u64, u64) = split_u128_into_u64(lowest); + let (word_3, word_2): (u64, u64) = split_u128_into_u64(highest); let (carry_2, res_1): (bool, u64) = jet::add_64(word_1, word_2); // a * b fits into u192, so addition below can not overflow and `full_add_64` @@ -171,17 +174,17 @@ pub fn safe_mul_128(a: u128, b: u128) -> u128 { /// Helper function that can be used with jet::div_mod_128_64 or Algorithm D. /// Returns the normalization factor by which `b` should be multiplied so that /// its most significant non-zero word is greater than or equal to 2^63 -pub fn calculate_normalizer_base_64(b: u128, is_b_u128: bool) -> u64 { +pub fn calculate_normalizer_base_64(b: u128, is_b_u128: bool) -> u64 { // Compile-time constant: 2^63. Avoids a runtime jet::left_shift_64 call let threshold: u64 = 0x8000000000000000; - let (b_high, b_low): (u64, u64) = ::into(b); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); let b_highest_word: u64 = match is_b_u128 { true => b_high, false => { assert!(jet::is_zero_64(b_high)); b_low - } + }, }; assert!(not(jet::is_zero_64(b_highest_word))); @@ -206,10 +209,10 @@ pub fn calculate_normalizer_base_64(b: u128, is_b_u128: bool) -> u64 { /// Division algorithms operate in base 2^64, so the normalization threshold is 2^63 fn normalize_to_threshold_128_63(a: u128, b: u128, is_b_u128: bool) -> (u256, u128, u64) { let norm: u64 = calculate_normalizer_base_64(b, is_b_u128); - let norm_128: u128 = <(u64, u64)>::into((0, norm)); + let norm_128: u128 = u64_to_u128(norm); match jet::eq_64(norm, 1) { - true => (<(u128, u128)>::into((0, a)), b, norm), + true => (u128_to_u256(a), b, norm), false => (mul_128(a, norm_128), safe_mul_128(b, norm_128), norm), } } @@ -220,7 +223,7 @@ fn normalize_to_threshold_128_63(a: u128, b: u128, is_b_u128: bool) -> (u256, u1 pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u64) -> u64 { let (q_hat, r_hat, carry): (u64, u64, bool) = match jet::lt_64(u2, v1) { true => { - let (q_hat, r_hat) : (u64, u64) =jet::div_mod_128_64(<(u64, u64)>::into((u2, u1)), v1); + let (q_hat, r_hat): (u64, u64) = jet::div_mod_128_64(<(u64, u64)>::into((u2, u1)), v1); (q_hat, r_hat, false) }, false => { @@ -230,7 +233,7 @@ pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u let (carry, r_hat): (bool, u64) = jet::add_64(u1, v1); (jet::high_64(), r_hat, carry) - } + }, }; match carry { @@ -242,7 +245,7 @@ pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u match lt_128(r_hat_u0, jet::multiply_64(q_hat, v0)) { true => { // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 - let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); + let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); let (carry, r_hat): (bool, u64) = jet::add_64(r_hat, v1); match carry { @@ -253,18 +256,18 @@ pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u match lt_128(r_hat_u0, jet::multiply_64(q_hat, v0)) { true => { // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 - let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); + let (_, q_hat): (bool, u64) = jet::subtract_64(q_hat, 1); q_hat - } + }, false => q_hat, } - } + }, } }, false => q_hat, } - } + }, } } @@ -273,22 +276,27 @@ pub fn estimate_quotient_digit_base_64(u2: u64, u1: u64, u0: u64, v1: u64, v0: u /// Implements Algorithm D by Donald Knuth. /// Requires the upper half of the divisor to be non-zero. fn algorithm_d_128_128(dividend: u128, divisor: u128) -> (u64, u128) { - let (norm_dividend, norm_divisor, _): (u256, u128, u64) = normalize_to_threshold_128_63(dividend, divisor, true); + let ( + norm_dividend, + norm_divisor, + _ + ): (u256, u128, u64) = normalize_to_threshold_128_63(dividend, divisor, true); // normalized dividend fits into 192 bits let (_, u2, u1, u0): (u64, u64, u64, u64) = ::into(norm_dividend); - let (v1, v0): (u64, u64) = ::into(norm_divisor); + let (v1, v0): (u64, u64) = split_u128_into_u64(norm_divisor); let q: u64 = estimate_quotient_digit_base_64(u2, u1, u0, v1, v0); - let remainder: u128 = safe_sub_128(dividend, safe_mul_128(divisor, <(u64, u64)>::into((0, q)))); + let remainder: u128 = safe_sub_128(dividend, safe_mul_128(divisor, u64_to_u128(q))); (q, remainder) } /// Divides the 128-bit integer by the 64-bit integer, -/// returns a tuple of the u128 quotient and the u64 remainder +/// returns a tuple of the u128 quotient and the u64 remainder. +/// Panics if divisor is equal to zero pub fn div_mod_128_64(a: u128, b: u64) -> (u128, u64) { - let (a_high, a_low): (u64, u64) = ::into(a); + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); // calculate the upper half of the quotient let (q_high, remainder): (u64, u64) = jet::div_mod_64(a_high, b); @@ -297,11 +305,19 @@ pub fn div_mod_128_64(a: u128, b: u64) -> (u128, u64) { let a_prime: u128 = <(u64, u64)>::into((remainder, a_low)); // we need to normalize here, because jet::div_mod_128_64 only accepts b >= 2^63 - let (a_normalized, b_normalized, norm): (u256, u128, u64) = normalize_to_threshold_128_63(a_prime, <(u64, u64)>::into((0, b)), false); + let ( + a_normalized, + b_normalized, + norm + ): (u256, u128, u64) = normalize_to_threshold_128_63( + a_prime, + u64_to_u128(b), + false + ); // a_normalized fits into u128, because remainder < b and b_normalized fits into u64 let (_, a_normalized): (u128, u128) = ::into(a_normalized); - let (_, b_normalized): (u64, u64) = ::into(b_normalized); + let (_, b_normalized): (u64, u64) = split_u128_into_u64(b_normalized); // remainder < b, so (remainder * 2^64 + a_low) / b fits into u64 let (q_low, r_normalized): (u64, u64) = jet::div_mod_128_64(a_normalized, b_normalized); @@ -313,42 +329,45 @@ pub fn div_mod_128_64(a: u128, b: u64) -> (u128, u64) { /// Divides the first integer by the second integer, /// returns the quotient and the remainder pub fn div_mod_128(a: u128, b: u128) -> (u128, u128) { - let (a_high, a_low): (u64, u64) = ::into(a); - let (b_high, b_low): (u64, u64) = ::into(b); - - match lt_128(a, b) { + match is_zero_128(b) { true => (0, a), false => { - match and(jet::is_zero_64(a_high), jet::is_zero_64(b_high)) { - true => { - // if both a_high and b_high are zero, this narrows down to 64-bit division - let (q, r): (u64, u64) = jet::div_mod_64(a_low, b_low); - (<(u64, u64)>::into((0, q)), <(u64, u64)>::into((0, r))) - }, + let (a_high, a_low): (u64, u64) = split_u128_into_u64(a); + let (b_high, b_low): (u64, u64) = split_u128_into_u64(b); + + match lt_128(a, b) { + true => (0, a), false => { - match jet::eq_64(a_high, b_high) { + match and(jet::is_zero_64(a_high), jet::is_zero_64(b_high)) { true => { - // safe: !lt_128(a, b) and a_high == b_high, so a_low >= b_low, - // and the subtraction can not underflow - let (_, diff): (bool, u64) = jet::subtract_64(a_low, b_low); - (1, <(u64, u64)>::into((0, diff))) + // if both a_high and b_high are zero, this narrows down to 64-bit division + let (q, r): (u64, u64) = jet::div_mod_64(a_low, b_low); + (u64_to_u128(q), u64_to_u128(r)) }, false => { - match jet::is_zero_64(b_high) { + match jet::eq_64(a_high, b_high) { true => { - let (q, r): (u128, u64) = div_mod_128_64(a, b_low); - (q, <(u64, u64)>::into((0, r))) + // safe: !lt_128(a, b) and a_high == b_high, so a_low >= b_low, + // and the subtraction can not underflow + let (_, diff): (bool, u64) = jet::subtract_64(a_low, b_low); + (1, u64_to_u128(diff)) }, - false => { - let (q, r): (u64, u128) = algorithm_d_128_128(a, b); - (<(u64, u64)>::into((0, q)), r) - } - } - } + false => match jet::is_zero_64(b_high) { + true => { + let (q, r): (u128, u64) = div_mod_128_64(a, b_low); + (q, u64_to_u128(r)) + }, + false => { + let (q, r): (u64, u128) = algorithm_d_128_128(a, b); + (u64_to_u128(q), r) + }, + }, + } + }, } - } + }, } - } + }, } } diff --git a/simf/lib/u128/mul_div.simf b/simf/lib/u128/mul_div.simf new file mode 100644 index 0000000..6b4c0e2 --- /dev/null +++ b/simf/lib/u128/mul_div.simf @@ -0,0 +1,15 @@ +use crate::lib::u128::math::mul_128; +use crate::lib::u128::convert::u128_to_u256; + +use crate::lib::u256::math::div_256; +use crate::lib::u256::convert::safe_u256_to_u128; + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u128 +pub fn mul_div_128(a: u128, b: u128, denominator: u128) -> u128 { + let denominator_256: u256 = u128_to_u256(denominator); + + let result_256: u256 = div_256(mul_128(a, b), denominator_256); + + safe_u256_to_u128(result_256) +} diff --git a/simf/lib/u16/mul_div.simf b/simf/lib/u16/mul_div.simf new file mode 100644 index 0000000..92e7b04 --- /dev/null +++ b/simf/lib/u16/mul_div.simf @@ -0,0 +1,12 @@ +use crate::lib::u16::convert::u16_to_u32; +use crate::lib::u32::convert::safe_u32_to_u16; + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u16 +pub fn mul_div_16(a: u16, b: u16, denominator: u16) -> u16 { + let denominator_32: u32 = u16_to_u32(denominator); + + let result_32: u32 = jet::divide_32(jet::multiply_16(a, b), denominator_32); + + safe_u32_to_u16(result_32) +} diff --git a/simf/lib/u256/math.simf b/simf/lib/u256/math.simf index 12a37f7..f06ea7c 100644 --- a/simf/lib/u256/math.simf +++ b/simf/lib/u256/math.simf @@ -1,4 +1,6 @@ -use crate::lib::binary::{not, and}; +use crate::lib::binary::{not, and, or}; +use crate::lib::u64::convert::u64_to_u128; + use crate::lib::u128::math::{ add_128, full_add_128, @@ -14,16 +16,17 @@ use crate::lib::u128::math::{ }; use crate::lib::u128::bit::eq_128; use crate::lib::u128::comparison::{is_zero_128, lt_128}; +use crate::lib::u128::convert::{u128_to_u256, split_u128_into_u64}; -use crate::lib::u256::convert::split_u256_into_u64; use crate::lib::u256::comparison::{is_zero_256, lt_256}; +use crate::lib::u256::convert::{split_u256_into_u64, split_u256_into_u128}; /// Arithmetic /// Adds two integers and returns the carry pub fn add_256(a: u256, b: u256) -> (bool, u256) { - let (a_high, a_low): (u128, u128) = ::into(a); - let (b_high, b_low): (u128, u128) = ::into(b); + let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); + let (b_high, b_low): (u128, u128) = split_u256_into_u128(b); let (carry_low, sum_low): (bool, u128) = add_128(a_low, b_low); let (carry_high, sum_high): (bool, u128) = full_add_128(carry_low, a_high, b_high); @@ -34,11 +37,23 @@ pub fn add_256(a: u256, b: u256) -> (bool, u256) { /// Adds the 256-bit integer with the 128-bit integer and returns the carry pub fn add_256_128(a: u256, b: u128) -> (bool, u256) { - let b: u256 = <(u128, u128)>::into((0, b)); + let b: u256 = u128_to_u256(b); add_256(a, b) } +/// Adds two integers. Takes a carry-in and returns a carry-out +pub fn full_add_256(carry_in: bool, a: u256, b: u256) -> (bool, u256) { + let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); + let (b_high, b_low): (u128, u128) = split_u256_into_u128(b); + + let (carry_low, sum_low): (bool, u128) = full_add_128(carry_in, a_low, b_low); + let (carry_out, sum_high): (bool, u128) = full_add_128(carry_low, a_high, b_high); + + let res: u256 = <(u128, u128)>::into((sum_high, sum_low)); + (carry_out, res) +} + /// Returns the sum of two u256 values wrapped in Some, or None if the result overflows u256 pub fn checked_add_256(a: u256, b: u256) -> Option { let (carry, sum): (bool, u256) = add_256(a, b); @@ -56,8 +71,8 @@ pub fn safe_add_256(a: u256, b: u256) -> u256 { /// Subtracts the second integer from the first integer, and returns the borrow bit pub fn sub_256(a: u256, b: u256) -> (bool, u256) { - let (a_high, a_low): (u128, u128) = ::into(a); - let (b_high, b_low): (u128, u128) = ::into(b); + let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); + let (b_high, b_low): (u128, u128) = split_u256_into_u128(b); let (borrow_low, diff_low): (bool, u128) = sub_128(a_low, b_low); let (borrow_high, diff_high): (bool, u128) = full_sub_128(borrow_low, a_high, b_high); @@ -87,19 +102,19 @@ pub fn safe_sub_256(a: u256, b: u256) -> u256 { /// In the same way, b = b_high * 2^128 + b_low. /// Therefore, a * b = 2^256 * a_high * b_high + 2^128(a_high * b_low + a_low * b_high) + a_low * b_low. pub fn mul_256(a: u256, b: u256) -> (u256, u256) { - let (a_high, a_low): (u128, u128) = ::into(a); - let (b_high, b_low): (u128, u128) = ::into(b); + let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); + let (b_high, b_low): (u128, u128) = split_u256_into_u128(b); let highest: u256 = mul_128(a_high, b_high); let lowest: u256 = mul_128(a_low, b_low); - let (word_1, word_0): (u128, u128) = ::into(lowest); - let (word_3, word_2): (u128, u128) = ::into(highest); + let (word_1, word_0): (u128, u128) = split_u256_into_u128(lowest); + let (word_3, word_2): (u128, u128) = split_u256_into_u128(highest); let product_1: u256 = mul_128(a_high, b_low); let product_2: u256 = mul_128(b_high, a_low); let (carry_3a, middle): (bool, u256) = add_256(product_1, product_2); - let (middle_2, middle_1): (u128, u128) = ::into(middle); + let (middle_2, middle_1): (u128, u128) = split_u256_into_u128(middle); // fold the low-side carry directly into the word_2 addition via full_add_128, // then propagate any resulting carry into word_3 @@ -126,15 +141,15 @@ pub fn mul_256(a: u256, b: u256) -> (u256, u256) { /// so a = a_high * 2^128 + a_low, /// a * b = 2^128 * a_high * b + a_low * b pub fn mul_256_64(a: u256, b: u64) -> (u64, u256) { - let (a_high, a_low): (u128, u128) = ::into(a); + let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); let lowest: u256 = mul_128_64(a_low, b); let highest: u256 = mul_128_64(a_high, b); - let (word_1, word_0): (u128, u128) = ::into(lowest); - let (word_3, word_2): (u128, u128) = ::into(highest); + let (word_1, word_0): (u128, u128) = split_u256_into_u128(lowest); + let (word_3, word_2): (u128, u128) = split_u256_into_u128(highest); - let (_, word_3): (u64, u64) = ::into(word_3); + let (_, word_3): (u64, u64) = split_u128_into_u64(word_3); let (carry_2, res_1): (bool, u128) = add_128(word_1, word_2); // a * b fits into u320, so addition below can not overflow and @@ -150,13 +165,13 @@ pub fn mul_256_64(a: u256, b: u64) -> (u64, u256) { /// so a = a_high * 2^128 + a_low, /// and a * b = 2^128 * a_high * b + a_low * b pub fn mul_256_128(a: u256, b: u128) -> (u128, u256) { - let (a_high, a_low): (u128, u128) = ::into(a); + let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); let lowest: u256 = mul_128(a_low, b); let highest: u256 = mul_128(a_high, b); - let (word_1, word_0): (u128, u128) = ::into(lowest); - let (word_3, word_2): (u128, u128) = ::into(highest); + let (word_1, word_0): (u128, u128) = split_u256_into_u128(lowest); + let (word_3, word_2): (u128, u128) = split_u256_into_u128(highest); let (carry_2, res_1): (bool, u128) = add_128(word_1, word_2); // a * b fits into u384, so addition below can not overflow and @@ -166,6 +181,24 @@ pub fn mul_256_128(a: u256, b: u128) -> (u128, u256) { (res_2, <(u128, u128)>::into((res_1, word_0))) } +/// Multiplies two integers. +/// The output consists of a 128-bit integer and two 256-bit integers. +/// The idea is that 512-bit `a` divides into 256-bit `a_high` and `a_low`, +/// so a = a_high * 2^256 + a_low, +/// and a * b = 2^256 * a_high * b + a_low * b +pub fn mul_512_128(a_high: u256, a_low: u256, b: u128) -> (u128, u256, u256) { + let (word_1, word_0): (u128, u256) = mul_256_128(a_low, b); + let (word_3, word_2): (u128, u256) = mul_256_128(a_high, b); + + let (carry, res_1): (bool, u256) = add_256_128(word_2, word_1); + + // a * b fits into u640, so addition below can not overflow and + // `full_add_128` is used to avoid the unnecessary overflow check + let (_, res_2): (bool, u128) = full_add_128(carry, word_3, 0); + + (res_2, res_1, word_0) +} + /// Returns the product of two u256 values wrapped in Some, or None if the result overflows u256 pub fn checked_mul_256(a: u256, b: u256) -> Option { let (result_high, result_low): (u256, u256) = mul_256(a, b); @@ -182,13 +215,39 @@ pub fn safe_mul_256(a: u256, b: u256) -> u256 { } /// Returns the product of u256 and u128 values, panics if the result overflows u256 -fn safe_mul_256_128(a: u256, b: u128) -> u256 { +pub fn safe_mul_256_128(a: u256, b: u128) -> u256 { let (result_high, result_low): (u128, u256) = mul_256_128(a, b); assert!(is_zero_128(result_high)); result_low } +/// Helper function that can be used with Algorithm D. +/// Returns the normalization factor by which `b` should be multiplied so that +/// its most significant non-zero word is greater than or equal to 2^127 +pub fn calculate_normalizer_base_128(b: u256) -> u128 { + // Compile-time constant: 2^127 + let threshold: u128 = 0x80000000000000000000000000000000; + let (b_high, _): (u128, u128) = split_u256_into_u128(b); + + assert!(not(is_zero_128(b_high))); + + match lt_128(b_high, threshold) { + true => { + let (norm, remainder): (u128, u128) = div_mod_128(threshold, b_high); + + match is_zero_128(remainder) { + true => norm, + false => { + let (_, norm): (bool, u128) = add_128(norm, 1); // norm <= 2^127, so norm + 1 can not overflow + norm + }, + } + }, + false => 1, + } +} + /// Normalizes the dividend and divisor for Algorithm D by multiplying /// both u256 and u128 by the same factor, ensuring that /// the most significant non-zero word of `b` is at least 2^63. @@ -197,7 +256,7 @@ fn safe_mul_256_128(a: u256, b: u128) -> u256 { /// the normalized `b` and the normalization factor. fn normalize_to_threshold_256_63(a: u256, b: u128, is_b_u128: bool) -> (u64, u256, u128, u64) { let norm: u64 = calculate_normalizer_base_64(b, is_b_u128); - let norm_128: u128 = <(u64, u64)>::into((0, norm)); + let norm_128: u128 = u64_to_u128(norm); match jet::eq_64(norm, 1) { true => (0, a, b, norm), @@ -217,30 +276,15 @@ fn normalize_to_threshold_256_63(a: u256, b: u128, is_b_u128: bool) -> (u64, u25 /// The normalized dividend fits into 384 bits and is returned as a /// `(u128, u256)` pair. The normalized divisor fits into a `u256`. fn normalize_to_threshold_256_127(a: u256, b: u256) -> (u128, u256, u256) { - // Compile-time constant: 2^127 - let threshold: u128 = 0x80000000000000000000000000000000; - - let (b_high, _): (u128, u128) = ::into(b); - - assert!(not(is_zero_128(b_high))); + let norm: u128 = calculate_normalizer_base_128(b); - let (norm, remainder): (u128, u128) = div_mod_128(threshold, b_high); - - let norm: u128 = match is_zero_128(remainder) { - true => norm, + match eq_128(norm, 1) { + true => (0, a, b), false => { - let (_, norm): (bool, u128) = add_128(norm, 1); // norm <= 2^127, so norm + 1 can not overflow - norm - } - }; - - match lt_128(b_high, threshold) { - true => { let (high, low): (u128, u256) = mul_256_128(a, norm); (high, low, safe_mul_256_128(b, norm)) }, - false => (0, a, b), } } @@ -252,23 +296,27 @@ pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { // calculate the upper part of the quotient let (q3, remainder): (u64, u64) = jet::div_mod_64(u3, divisor); - let divisor: u128 = <(u64, u64)>::into((0, divisor)); + let divisor: u128 = u64_to_u128(divisor); let dividend: u256 = <(u64, u64, u64, u64)>::into((remainder, u2, u1, u0)); // normalized dividend fits into 256 bits because remainder < divisor and // divisor * norm fits into u64, so dividend * norm also fits - let (_, norm_dividend_low, norm_divisor, norm): (u64, u256, u128, u64) = - normalize_to_threshold_256_63(dividend, divisor, false); + let ( + _, + norm_dividend_low, + norm_divisor, + norm + ): (u64, u256, u128, u64) = normalize_to_threshold_256_63(dividend, divisor, false); let (u3, u2, u1, u0): (u64, u64, u64, u64) = split_u256_into_u64(norm_dividend_low); - let (_, v0): (u64, u64) = ::into(norm_divisor); + let (_, v0): (u64, u64) = split_u128_into_u64(norm_divisor); - let (q2, remainder): (u64, u64) = jet::div_mod_128_64( <(u64, u64)>::into((u3, u2)), v0); - let (q1, remainder): (u64, u64) = jet::div_mod_128_64( <(u64, u64)>::into((remainder, u1)), v0); - let (q0, remainder): (u64, u64) = jet::div_mod_128_64( <(u64, u64)>::into((remainder, u0)), v0); + let (q2, remainder): (u64, u64) = jet::div_mod_128_64(<(u64, u64)>::into((u3, u2)), v0); + let (q1, remainder): (u64, u64) = jet::div_mod_128_64(<(u64, u64)>::into((remainder, u1)), v0); + let (q0, remainder): (u64, u64) = jet::div_mod_128_64(<(u64, u64)>::into((remainder, u0)), v0); let remainder: u64 = jet::divide_64(remainder, norm); - let q: u256 = <(u64, u64, u64, u64)>::into(( q3, q2, q1, q0)); + let q: u256 = <(u64, u64, u64, u64)>::into((q3, q2, q1, q0)); (q, remainder) } @@ -276,7 +324,7 @@ pub fn div_mod_256_64(dividend: u256, divisor: u64) -> (u256, u64) { /// Multiplies the divisor by the quotient digit and subtracts the result from /// the corresponding dividend limbs. Returns the updated dividend segment, /// which is used by Algorithm D -fn mul_and_sub(q: u64, u2: u64, u1: u64, u0: u64, v: u128) -> (u64, u64) { +fn mul_and_sub_64(q: u64, u2: u64, u1: u64, u0: u64, v: u128) -> (u64, u64) { let u: u256 = <(u64, u64, u64, u64)>::into((0, u2, u1, u0)); let q_v: u256 = mul_128_64(v, q); @@ -291,22 +339,26 @@ fn mul_and_sub(q: u64, u2: u64, u1: u64, u0: u64, v: u128) -> (u64, u64) { /// returns a tuple of the u256 quotient and the u128 remainder. /// Implements Algorithm D by Donald Knuth. /// Requires the upper half of the divisor to be non-zero (divisor >= 2^64) -fn algorithm_d_256_128(dividend: u256, divisor: u128) -> (u256, u128) { - let (u4, norm_dividend_low, norm_divisor, norm): (u64, u256, u128, u64) = - normalize_to_threshold_256_63(dividend, divisor, true); +pub fn algorithm_d_256_128(dividend: u256, divisor: u128) -> (u256, u128) { + let ( + u4, + norm_dividend_low, + norm_divisor, + norm + ): (u64, u256, u128, u64) = normalize_to_threshold_256_63(dividend, divisor, true); // normalized dividend fits into 320 bits let (u3, u2, u1, u0): (u64, u64, u64, u64) = split_u256_into_u64(norm_dividend_low); - let (v1, v0): (u64, u64) = ::into(norm_divisor); + let (v1, v0): (u64, u64) = split_u128_into_u64(norm_divisor); let q2: u64 = estimate_quotient_digit_base_64(u4, u3, u2, v1, v0); - let (u3, u2): (u64, u64) = mul_and_sub(q2, u4, u3, u2, norm_divisor); + let (u3, u2): (u64, u64) = mul_and_sub_64(q2, u4, u3, u2, norm_divisor); let q1: u64 = estimate_quotient_digit_base_64(u3, u2, u1, v1, v0); - let (u2, u1): (u64, u64) = mul_and_sub(q1, u3, u2, u1, norm_divisor); + let (u2, u1): (u64, u64) = mul_and_sub_64(q1, u3, u2, u1, norm_divisor); let q0: u64 = estimate_quotient_digit_base_64(u2, u1, u0, v1, v0); - let (u1, u0): (u64, u64) = mul_and_sub(q0, u2, u1, u0, norm_divisor); + let (u1, u0): (u64, u64) = mul_and_sub_64(q0, u2, u1, u0, norm_divisor); let q: u256 = <(u64, u64, u64, u64)>::into((0, q2, q1, q0)); let (remainder, _): (u128, u64) = div_mod_128_64(<(u64, u64)>::into((u1, u0)), norm); @@ -317,13 +369,13 @@ fn algorithm_d_256_128(dividend: u256, divisor: u128) -> (u256, u128) { /// Divides the 256-bit integer by the 128-bit integer, /// returns a tuple of the u256 quotient and the u128 remainder pub fn div_mod_256_128(dividend: u256, divisor: u128) -> (u256, u128) { - let (divisor_high, divisor_low): (u64, u64) = ::into(divisor); + let (divisor_high, divisor_low): (u64, u64) = split_u128_into_u64(divisor); match jet::is_zero_64(divisor_high) { true => { let (q, r): (u256, u64) = div_mod_256_64(dividend, divisor_low); - (q, <(u64, u64)>::into((0, r))) + (q, u64_to_u128(r)) }, false => algorithm_d_256_128(dividend, divisor), } @@ -334,13 +386,17 @@ pub fn div_mod_256_128(dividend: u256, divisor: u128) -> (u256, u128) { /// Implements Algorithm D by Donald Knuth. /// Requires the upper half of the divisor to be non-zero (divisor >= 2^128). fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { - let (u2, norm_dividend_low, norm_divisor): (u128, u256, u256) = normalize_to_threshold_256_127(dividend, divisor); + let ( + u2, + norm_dividend_low, + norm_divisor + ): (u128, u256, u256) = normalize_to_threshold_256_127(dividend, divisor); - let (u1, u0): (u128, u128) = ::into(norm_dividend_low); - let (v1, v0): (u128, u128) = ::into(norm_divisor); + let (u1, u0): (u128, u128) = split_u256_into_u128(norm_dividend_low); + let (v1, v0): (u128, u128) = split_u256_into_u128(norm_divisor); let (q_hat, r_hat): (u256, u128) = algorithm_d_256_128(<(u128, u128)>::into((u2, u1)), v1); - let( _, q_hat): (u128, u128) = ::into(q_hat); + let (_, q_hat): (u128, u128) = split_u256_into_u128(q_hat); let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); let u_hat: u256 = mul_128(q_hat, v0); @@ -349,25 +405,25 @@ fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { let q: u128 = match lt_256(r_hat_u0, u_hat) { true => { // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 - let (_, q_hat): (bool, u128) = sub_128(q_hat, 1); + let (_, q_hat): (bool, u128) = sub_128(q_hat, 1); let (carry, r_hat): (bool, u128) = add_128(r_hat, v1); match carry { true => q_hat, false => { let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); - let (_, u_hat): (bool, u256) = sub_256(u_hat, <(u128, u128)>::into((0, v0))); + let (_, u_hat): (bool, u256) = sub_256(u_hat, u128_to_u256(v0)); match lt_256(r_hat_u0, u_hat) { true => { // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 - let (_, q_hat): (bool, u128) = sub_128(q_hat, 1); + let (_, q_hat): (bool, u128) = sub_128(q_hat, 1); q_hat - } + }, false => q_hat, } - } + }, } }, false => q_hat, @@ -380,17 +436,17 @@ fn algorithm_d_256_256(dividend: u256, divisor: u256) -> (u128, u256) { /// Divides the first integer by the second integer, /// returns the quotient and the remainder pub fn div_mod_256(a: u256, b: u256) -> (u256, u256) { - let (a_high, a_low): (u128, u128) = ::into(a); - let (b_high, b_low): (u128, u128) = ::into(b); + let (a_high, a_low): (u128, u128) = split_u256_into_u128(a); + let (b_high, b_low): (u128, u128) = split_u256_into_u128(b); - match lt_256(a, b) { + match or(is_zero_256(b), lt_256(a, b)) { true => (0, a), false => { - match and(is_zero_128(a_high), is_zero_128(b_high)) { - true => { + match and(is_zero_128(a_high), is_zero_128(b_high)) { + true => { // if both a_high and b_high are zero, this narrows down to 128-bit division let (q, r): (u128, u128) = div_mod_128(a_low, b_low); - (<(u128, u128)>::into((0, q)), <(u128, u128)>::into((0, r))) + (u128_to_u256(q), u128_to_u256(r)) }, false => { match eq_128(a_high, b_high) { @@ -398,24 +454,22 @@ pub fn div_mod_256(a: u256, b: u256) -> (u256, u256) { // safe: !lt_256(a, b) and a_high == b_high, so a_low >= b_low, // and the subtraction can not underflow let (_, diff): (bool, u128) = sub_128(a_low, b_low); - (1, <(u128, u128)>::into((0, diff))) + (1, u128_to_u256(diff)) + }, + false => match is_zero_128(b_high) { + true => { + let (q, r): (u256, u128) = div_mod_256_128(a, b_low); + (q, u128_to_u256(r)) + }, + false => { + let (q, r): (u128, u256) = algorithm_d_256_256(a, b); + (u128_to_u256(q), r) + }, }, - false => { - match is_zero_128(b_high) { - true => { - let (q, r): (u256, u128) = div_mod_256_128(a, b_low); - (q, <(u128, u128)>::into((0, r))) - }, - false => { - let (q, r): (u128, u256) = algorithm_d_256_256(a, b); - (<(u128, u128)>::into((0, q)), r) - } - } - } } - } + }, } - } + }, } } diff --git a/simf/lib/u256/mul_div.simf b/simf/lib/u256/mul_div.simf new file mode 100644 index 0000000..52ad6ef --- /dev/null +++ b/simf/lib/u256/mul_div.simf @@ -0,0 +1,179 @@ +use crate::lib::binary::not; + +use crate::lib::u128::math::{add_128, sub_128, full_sub_128, mul_128}; +use crate::lib::u128::bit::eq_128; +use crate::lib::u128::comparison::{is_zero_128, lt_128}; + +use crate::lib::u256::math::{ + algorithm_d_256_128, + mul_256_128, + sub_256, + safe_sub_256, + calculate_normalizer_base_128, + mul_512_128, + safe_mul_256_128, + mul_256, + div_mod_256_128, + div_256 +}; +use crate::lib::u256::convert::split_u256_into_u128; +use crate::lib::u256::comparison::{is_zero_256, lt_256}; + +/// Estimates and corrects the next quotient digit (q_hat) for Algorithm D. +/// Returns the quotient digit to use in the subsequent multiply-and-subtract step. +/// Expects result to fit into u128 +fn estimate_quotient_digit_base_128(u2: u128, u1: u128, u0: u128, v1: u128, v0: u128) -> u128 { + let (q_hat, r_hat, carry): (u128, u128, bool) = match lt_128(u2, v1) { + true => { + let (q_hat, r_hat) : (u256, u128) = algorithm_d_256_128(<(u128, u128)>::into((u2, u1)), v1); + let (_, q_hat): (u128, u128) = split_u256_into_u128(q_hat); // skip the check for optimization. u2 < v1, so q_hat fits into u128 + + (q_hat, r_hat, false) + }, + false => { + // This means u2 == v1, q_hat = 2^128, and r_hat = u1. + // Therefore, we need to decrement q and add v1 to r_hat. + // r_hat = u1 + v1 may overflow u128, which means that the estimate is exact + let (carry, r_hat): (bool, u128) = add_128(u1, v1); + + let u128_max: u128 = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + (u128_max, r_hat, carry) + }, + }; + + match carry { + true => q_hat, + false => { + let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); + + // correcting estimation: q_hat is off by at most 2. + match lt_256(r_hat_u0, mul_128(q_hat, v0)) { + true => { + // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 + let (_, q_hat): (bool, u128) = sub_128(q_hat, 1); + let (carry, r_hat): (bool, u128) = add_128(r_hat, v1); + + match carry { + true => q_hat, + false => { + let r_hat_u0: u256 = <(u128, u128)>::into((r_hat, u0)); + + match lt_256(r_hat_u0, mul_128(q_hat, v0)) { + true => { + // can not overflow because r_hat_u0 < q_hat * v0, so q_hat is at least 1 + let (_, q_hat): (bool, u128) = sub_128(q_hat, 1); + + q_hat + }, + false => q_hat, + } + }, + } + }, + false => q_hat, + } + }, + } +} + +/// Normalizes the dividend and divisor for Algorithm D by multiplying +/// all values by the same factor, ensuring that +/// the most significant non-zero word of `b` is at least 2^127. +/// +/// The normalized dividend fits into 640 bits and is returned as a +/// `(u128, u256, u256)` tuple. The normalized divisor fits into a `u256` +fn normalize_to_threshold_512_127(a_high: u256, a_low: u256, b: u256) -> (u128, u256, u256, u256) { + let norm: u128 = calculate_normalizer_base_128(b); + + match eq_128(norm, 1) { + true => (0, a_high, a_low, b), + false => { + let (a_2, a_1, a_0): (u128, u256, u256) = mul_512_128(a_high, a_low, norm); + + (a_2, a_1, a_0, safe_mul_256_128(b, norm)) + }, + } +} + +/// Multiplies the divisor by the quotient digit and subtracts the result from +/// the corresponding dividend limbs. Returns the updated dividend segment, +/// which is used by Algorithm D +fn mul_and_sub_128(q: u128, u2: u128, u1: u128, u0: u128, v: u256) -> (u128, u128) { + let u1_0: u256 = <(u128, u128)>::into((u1, u0)); + + let (q_v_1, q_v_0): (u128, u256) = mul_256_128(v, q); + + let (borrow_low, u_updated_1_0): (bool, u256) = sub_256(u1_0, q_v_0); + let (borrow, u_updated_2): (bool, u128) = full_sub_128(borrow_low, u2, q_v_1); + + assert!(not(borrow)); + assert!(is_zero_128(u_updated_2)); + + split_u256_into_u128(u_updated_1_0) +} + +/// Divides a 512-bit integer by a 256-bit integer and returns a `u256` quotient. +/// Implements Algorithm D by Donald Knuth. +/// Requires the upper half of the divisor to be non-zero (`divisor >= 2^128`) +/// and the quotient to fit into `u256`. +fn algorithm_d_512_256(dividend_high: u256, dividend_low: u256, divisor: u256) -> u256 { + let ( + _, + norm_dividend_high, + norm_dividend_low, + norm_divisor + ): (u128, u256, u256, u256) = (normalize_to_threshold_512_127(dividend_high, dividend_low, divisor)); + + let (u3, u2): (u128, u128) = split_u256_into_u128(norm_dividend_high); + let (u1, u0): (u128, u128) = split_u256_into_u128(norm_dividend_low); + let (v1, v0): (u128, u128) = split_u256_into_u128(norm_divisor); + + let q1: u128 = estimate_quotient_digit_base_128(u3, u2, u1, v1, v0); + let (u2, u1): (u128, u128) = mul_and_sub_128(q1, u3, u2, u1, norm_divisor); + + let q0: u128 = estimate_quotient_digit_base_128(u2, u1, u0, v1, v0); + + <(u128, u128)>::into((q1, q0)) +} + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u256 +pub fn mul_div_256(a: u256, b: u256, denominator: u256) -> u256 { + match is_zero_256(denominator) { + true => 0, + false => { + let (result_high, result_low): (u256, u256) = mul_256(a, b); + + match is_zero_256(result_high) { + true => div_256(result_low, denominator), + false => { + // if result_high >= denominator, result will overflow u256 + assert!(lt_256(result_high, denominator)); + + let (denom_high, denom_low): (u128, u128) = split_u256_into_u128(denominator); + + match is_zero_128(denom_high) { + true => { + // result_high < denominator, denominator fits into u128, so result_high fits too. + // split_u256_into_u128 is used instead of safe_u256_to_u128 to avoid the unnecessary check + let (_, result_2): (u128, u128) = split_u256_into_u128(result_high); + let (result_1, result_0): (u128, u128) = split_u256_into_u128(result_low); + + // result_high < denominator, so result_2 < denom_low and q1 fits into u128 + let (q1, r): (u256, u128) = div_mod_256_128(<(u128, u128)>::into((result_2, result_1)), denom_low); + let (_, q1): (u128, u128) = split_u256_into_u128(q1); + + // r < denom_low, so q0 fits into u128 + let (q0, _): (u256, u128) = div_mod_256_128(<(u128, u128)>::into((r, result_0)), denom_low); + let (_, q0): (u128, u128) = split_u256_into_u128(q0); + + <(u128, u128)>::into((q1, q0)) + }, + false => algorithm_d_512_256(result_high, result_low, denominator), + } + }, + } + }, + } +} diff --git a/simf/lib/u32/mul_div.simf b/simf/lib/u32/mul_div.simf new file mode 100644 index 0000000..8e4bc43 --- /dev/null +++ b/simf/lib/u32/mul_div.simf @@ -0,0 +1,12 @@ +use crate::lib::u32::convert::u32_to_u64; +use crate::lib::u64::convert::safe_u64_to_u32; + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u32 +pub fn mul_div_32(a: u32, b: u32, denominator: u32) -> u32 { + let denominator_64: u64 = u32_to_u64(denominator); + + let result_64: u64 = jet::divide_64(jet::multiply_32(a, b), denominator_64); + + safe_u64_to_u32(result_64) +} diff --git a/simf/lib/u64/mul_div.simf b/simf/lib/u64/mul_div.simf new file mode 100644 index 0000000..367630a --- /dev/null +++ b/simf/lib/u64/mul_div.simf @@ -0,0 +1,13 @@ +use crate::lib::u64::convert::u64_to_u128; +use crate::lib::u128::convert::safe_u128_to_u64; +use crate::lib::u128::math::div_128; + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u64 +pub fn mul_div_64(a: u64, b: u64, denominator: u64) -> u64 { + let denominator_128: u128 = u64_to_u128(denominator); + + let result_128: u128 = div_128(jet::multiply_64(a, b), denominator_128); + + safe_u128_to_u64(result_128) +} diff --git a/simf/lib/u8/mul_div.simf b/simf/lib/u8/mul_div.simf new file mode 100644 index 0000000..222f53b --- /dev/null +++ b/simf/lib/u8/mul_div.simf @@ -0,0 +1,12 @@ +use crate::lib::u8::convert::u8_to_u16; +use crate::lib::u16::convert::safe_u16_to_u8; + +/// Calculates `floor(a * b / denominator)` with full precision. +/// Panics if the result overflows a u8 +pub fn mul_div_8(a: u8, b: u8, denominator: u8) -> u8 { + let denominator_16: u16 = u8_to_u16(denominator); + + let result_16: u16 = jet::divide_16(jet::multiply_8(a, b), denominator_16); + + safe_u16_to_u8(result_16) +} diff --git a/simf/u128_mul_div_test.simf b/simf/u128_mul_div_test.simf new file mode 100644 index 0000000..614349a --- /dev/null +++ b/simf/u128_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u128::mul_div::mul_div_128; + +use crate::lib::asserts::assert_eq_128; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u128 = witness::FIRST_ARG; + let b: u128 = witness::SECOND_ARG; + let c: u128 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_128(mul_div_128(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/simf/u16_mul_div_test.simf b/simf/u16_mul_div_test.simf new file mode 100644 index 0000000..646fde3 --- /dev/null +++ b/simf/u16_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u16::mul_div::mul_div_16; + +use crate::lib::asserts::assert_eq_16; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u16 = witness::FIRST_ARG; + let b: u16 = witness::SECOND_ARG; + let c: u16 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_16(mul_div_16(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/simf/u256_mul_div_test.simf b/simf/u256_mul_div_test.simf new file mode 100644 index 0000000..aeddd10 --- /dev/null +++ b/simf/u256_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u256::mul_div::mul_div_256; + +use crate::lib::asserts::assert_eq_256; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u256 = witness::FIRST_ARG; + let b: u256 = witness::SECOND_ARG; + let c: u256 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_256(mul_div_256(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/simf/u256_test_add.simf b/simf/u256_test_add.simf index 1c06f46..5f210af 100644 --- a/simf/u256_test_add.simf +++ b/simf/u256_test_add.simf @@ -1,4 +1,4 @@ -use crate::lib::u256::math::{add_256, add_256_128}; +use crate::lib::u256::math::{add_256, add_256_128, full_add_256}; use crate::lib::asserts::assert_eq_256; use crate::helper::{if_test_this_function, assert_bool}; @@ -17,11 +17,31 @@ fn main() { let a: u256 = witness::FIRST_ARG; let b: u256 = witness::SECOND_ARG; + let carry_in: bool = witness::THIRD_ARG; let expected: Option = witness::EXPECTED; let expected_bool: bool = witness::EXPECTED_BOOL; /// Arithmetic - match if_test_this_function(0, fn_idx) { true => { assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); }, false => (), }; - match if_test_this_function(1, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); assert_eq_uint_bool(add_256_128(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_uint_bool(add_256(a, b), unwrap(expected), expected_bool); + }, + false => (), + }; + + match if_test_this_function(1, fn_idx) { + true => { + let (_, b): (u128, u128) = ::into(b); + assert_eq_uint_bool(add_256_128(a, b), unwrap(expected), expected_bool); + }, + false => (), + }; + + match if_test_this_function(2, fn_idx) { + true => { + assert_eq_uint_bool(full_add_256(carry_in, a, b), unwrap(expected), expected_bool); + }, + false => (), + }; } diff --git a/simf/u256_test_div.simf b/simf/u256_test_div.simf index 7c895b2..854a857 100644 --- a/simf/u256_test_div.simf +++ b/simf/u256_test_div.simf @@ -1,4 +1,11 @@ -use crate::lib::u256::math::{div_mod_256_64, div_mod_256_128, div_mod_256, div_256}; +use crate::lib::u256::math::{ + calculate_normalizer_base_128, + div_mod_256_64, + algorithm_d_256_128, + div_mod_256_128, + div_mod_256, + div_256 +}; use crate::lib::u256::convert::split_u256_into_u64; use crate::lib::asserts::{assert_eq_128, assert_eq_256}; use crate::helper::if_test_this_function; @@ -15,40 +22,64 @@ fn main() { /// Arithmetic match if_test_this_function(0, fn_idx) { + true => { + let result: u128 = calculate_normalizer_base_128(a); + + let (_, expected_r): (u128, u128) = ::into(unwrap(expected)); + + assert_eq_128(result, expected_r); + }, + false => (), + }; + + match if_test_this_function(1, fn_idx) { true => { let (_, _, _, b): (u64, u64, u64, u64) = split_u256_into_u64(b); let (_, _, _, expected_r): (u64, u64, u64, u64) = split_u256_into_u64(second_expected); let (q, r): (u256, u64) = div_mod_256_64(a, b); - assert_eq_256(q, unwrap(expected)); - assert!(jet::eq_64(r, expected_r)); + assert_eq_256(q, unwrap(expected)); + assert!(jet::eq_64(r, expected_r)); }, false => (), }; - match if_test_this_function(1, fn_idx) { + match if_test_this_function(2, fn_idx) { + true => { + let (_, b): (u128, u128) = ::into(b); + let (_, expected_r): (u128, u128) = ::into(second_expected); + + let (q, r): (u256, u128) = algorithm_d_256_128(a, b); + + assert_eq_256(q, unwrap(expected)); + assert_eq_128(r, expected_r); + }, + false => (), + }; + + match if_test_this_function(3, fn_idx) { true => { let (_, b): (u128, u128) = ::into(b); let (_, expected_r): (u128, u128) = ::into(second_expected); let (q, r): (u256, u128) = div_mod_256_128(a, b); - assert_eq_256(q, unwrap(expected)); - assert_eq_128(r, expected_r); + assert_eq_256(q, unwrap(expected)); + assert_eq_128(r, expected_r); }, false => (), }; - match if_test_this_function(2, fn_idx) { + match if_test_this_function(4, fn_idx) { true => { let (q, r): (u256, u256) = div_mod_256(a, b); - assert_eq_256(q, unwrap(expected)); - assert_eq_256(r, second_expected); + assert_eq_256(q, unwrap(expected)); + assert_eq_256(r, second_expected); }, false => (), }; - match if_test_this_function(3, fn_idx) { true => { assert_eq_256(div_256(a, b), unwrap(expected)); }, false => (), }; + match if_test_this_function(5, fn_idx) { true => { assert_eq_256(div_256(a, b), unwrap(expected)); }, false => (), }; } diff --git a/simf/u256_test_sub_mul.simf b/simf/u256_test_sub_mul.simf index aa6b673..bbadd85 100644 --- a/simf/u256_test_sub_mul.simf +++ b/simf/u256_test_sub_mul.simf @@ -1,4 +1,11 @@ -use crate::lib::u256::math::{sub_256, mul_256, mul_256_64, mul_256_128}; +use crate::lib::u256::math::{ + sub_256, + mul_256, + mul_256_64, + mul_256_128, + mul_512_128, + safe_mul_256_128 +}; use crate::lib::u256::convert::split_u256_into_u64; use crate::lib::asserts::{assert_eq_64, assert_eq_128, assert_eq_256}; use crate::helper::{if_test_this_function, assert_bool}; @@ -18,14 +25,21 @@ fn main() { let a: u256 = witness::FIRST_ARG; let b: u256 = witness::SECOND_ARG; + let c: u128 = witness::THIRD_ARG; let expected: Option = witness::EXPECTED; let expected_bool: bool = witness::EXPECTED_BOOL; let second_expected: u256 = witness::SECOND_EXPECTED; + let third_expected: u256 = witness::THIRD_EXPECTED; /// Arithmetic - match if_test_this_function(0, fn_idx) { true => { assert_eq_uint_bool(sub_256(a, b), unwrap(expected), expected_bool); }, false => (), }; + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_uint_bool(sub_256(a, b), unwrap(expected), expected_bool); + }, + false => (), + }; match if_test_this_function(1, fn_idx) { true => { @@ -62,4 +76,27 @@ fn main() { }, false => (), }; + + match if_test_this_function(4, fn_idx) { + true => { + let (result_2, result_1, result_0): (u128, u256, u256) = mul_512_128(a, b, c); + + let (_, expected): (u128, u128) = ::into(unwrap(expected)); + + assert_eq_128(result_2, expected); + assert_eq_256(result_1, second_expected); + assert_eq_256(result_0, third_expected); + }, + false => (), + }; + + match if_test_this_function(5, fn_idx) { + true => { + let (_, b): (u128, u128) = ::into(b); + let result: u256 = safe_mul_256_128(a, b); + + assert_eq_256(result, unwrap(expected)); + }, + false => (), + }; } diff --git a/simf/u32_mul_div_test.simf b/simf/u32_mul_div_test.simf new file mode 100644 index 0000000..b41fb21 --- /dev/null +++ b/simf/u32_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u32::mul_div::mul_div_32; + +use crate::lib::asserts::assert_eq_32; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u32 = witness::FIRST_ARG; + let b: u32 = witness::SECOND_ARG; + let c: u32 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_32(mul_div_32(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/simf/u64_mul_div_test.simf b/simf/u64_mul_div_test.simf new file mode 100644 index 0000000..fe99df2 --- /dev/null +++ b/simf/u64_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u64::mul_div::mul_div_64; + +use crate::lib::asserts::assert_eq_64; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u64 = witness::FIRST_ARG; + let b: u64 = witness::SECOND_ARG; + let c: u64 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_64(mul_div_64(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/simf/u8_mul_div_test.simf b/simf/u8_mul_div_test.simf new file mode 100644 index 0000000..0cb508d --- /dev/null +++ b/simf/u8_mul_div_test.simf @@ -0,0 +1,21 @@ +use crate::lib::u8::mul_div::mul_div_8; + +use crate::lib::asserts::assert_eq_8; +use crate::helper::if_test_this_function; + +fn main() { + let fn_idx: u8 = witness::FUNCTION_INDEX; + + let a: u8 = witness::FIRST_ARG; + let b: u8 = witness::SECOND_ARG; + let c: u8 = witness::THIRD_ARG; + + let expected: Option = witness::EXPECTED; + + match if_test_this_function(0, fn_idx) { + true => { + assert_eq_8(mul_div_8(a, b, c), unwrap(expected)); + }, + false => (), + }; +} diff --git a/tests/u128_basic_math_test.rs b/tests/u128_basic_math_test.rs index 77dd7b3..5d0b4ab 100644 --- a/tests/u128_basic_math_test.rs +++ b/tests/u128_basic_math_test.rs @@ -985,7 +985,7 @@ mod u128_tests_arithmetic { } #[simplex::test] - fn u128_test_div_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + fn u128_test_div_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let a = rand::thread_rng().gen_range(0..=u128::MAX); let b = 0; @@ -996,11 +996,11 @@ mod u128_tests_arithmetic { op(FunctionToTest::Div128), a, b, - Some(DEFAULT_EXPECTED), + Some(0), DEFAULT_BOOL, DEFAULT_EXPECTED, ), - Expect::AssertFailed, + Expect::Ok, ) } } diff --git a/tests/u128_mul_div_test.rs b/tests/u128_mul_div_test.rs new file mode 100644 index 0000000..5abd937 --- /dev/null +++ b/tests/u128_mul_div_test.rs @@ -0,0 +1,108 @@ +mod common; + +use primitive_types::U256; +use rand::Rng; + +use crate::common::core::{Expect, run}; + +use simplicityhl_std::artifacts::u128_mul_div_test::U128MulDivTestProgram; +use simplicityhl_std::artifacts::u128_mul_div_test::derived_u128_mul_div_test::{ + U128MulDivTestArguments, U128MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: u128 = 0; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U128MulDivTestProgram { + U128MulDivTestProgram::new(&U128MulDivTestArguments {}) +} + +fn build_witness( + op: u8, + a: u128, + b: u128, + c: u128, + expected: Option, +) -> U128MulDivTestWitness { + U128MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +mod u128_mul_div_test { + use super::*; + + #[simplex::test] + fn u128_test_mul_div_128_product_is_u128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u64::MAX) as u128; + let b = rand::thread_rng().gen_range(0..=u64::MAX) as u128; + let c = rand::thread_rng().gen_range(1..=u128::MAX); + + let res = a * b / c; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_mul_div_128_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u128::MAX); + let b = u128::MAX; + let c = rand::thread_rng().gen_range(a..=u128::MAX); + + let res = U256::from(a) * U256::from(b) / U256::from(c); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res.low_u128())), + Expect::Ok, + ) + } + + #[simplex::test] + fn u128_test_mul_div_128_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u128::MAX); + let b = u128::MAX; + let c = rand::thread_rng().gen_range(1..a); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u128_test_mul_div_128_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u128::MAX); + let b = rand::thread_rng().gen_range(1..=u128::MAX); + let c = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), + Expect::Ok, + ) + } +} diff --git a/tests/u16_mul_div_test.rs b/tests/u16_mul_div_test.rs new file mode 100644 index 0000000..eb18073 --- /dev/null +++ b/tests/u16_mul_div_test.rs @@ -0,0 +1,100 @@ +mod common; + +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::u16_mul_div_test::U16MulDivTestProgram; +use simplicityhl_std::artifacts::u16_mul_div_test::derived_u16_mul_div_test::{ + U16MulDivTestArguments, U16MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: u16 = 0; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U16MulDivTestProgram { + U16MulDivTestProgram::new(&U16MulDivTestArguments {}) +} + +fn build_witness(op: u8, a: u16, b: u16, c: u16, expected: Option) -> U16MulDivTestWitness { + U16MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +mod u16_mul_div_test { + use super::*; + + #[simplex::test] + fn u16_test_mul_div_16_product_is_u16(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u8::MAX) as u16; + let b = rand::thread_rng().gen_range(0..=u8::MAX) as u16; + let c = rand::thread_rng().gen_range(1..=u16::MAX); + + let res = a * b / c; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u16_test_mul_div_16_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u16::MAX); + let b = u16::MAX; + let c = rand::thread_rng().gen_range(a..=u16::MAX); + + let res = (a as u32) * (b as u32) / (c as u32); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u16)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u16_test_mul_div_16_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u16::MAX); + let b = u16::MAX; + let c = rand::thread_rng().gen_range(1..a); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u16_test_mul_div_16_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u16::MAX); + let b = rand::thread_rng().gen_range(1..=u16::MAX); + let c = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), + Expect::Ok, + ) + } +} diff --git a/tests/u256_mul_div_test.rs b/tests/u256_mul_div_test.rs new file mode 100644 index 0000000..5723c8a --- /dev/null +++ b/tests/u256_mul_div_test.rs @@ -0,0 +1,346 @@ +mod common; + +use primitive_types::U256; +use std::cmp::max; +use std::ops::Div; + +use crate::common::core::{Expect, run}; +use crate::common::helper::generate_u256; + +use simplicityhl_std::artifacts::u256_mul_div_test::U256MulDivTestProgram; +use simplicityhl_std::artifacts::u256_mul_div_test::derived_u256_mul_div_test::{ + U256MulDivTestArguments, U256MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U256MulDivTestProgram { + U256MulDivTestProgram::new(&U256MulDivTestArguments {}) +} + +fn build_witness( + op: u8, + a: [u8; 32], + b: [u8; 32], + c: [u8; 32], + expected: Option<[u8; 32]>, +) -> U256MulDivTestWitness { + U256MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +fn safe_u512_to_u256(a: [u8; 64]) -> [u8; 32] { + let high = U256::from_big_endian(&a[0..32]); + let low = U256::from_big_endian(&a[32..64]); + + assert!(high == U256::zero()); + + low.to_big_endian() +} + +mod u256_mul_div_test { + use super::*; + + #[simplex::test] + fn u256_test_mul_div_256_product_fits_into_u256( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u128::MAX)); + let b = generate_u256(U256::zero(), U256::from(u128::MAX)); + let c = generate_u256(U256::one(), U256::MAX); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(2), U256::MAX); + let b = U256::MAX; + let c = generate_u256(a, U256::MAX); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(2), U256::MAX); + let b = U256::MAX; + let c = generate_u256(U256::one(), a); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(DEFAULT_EXPECTED), + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_remainder_is_zero( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let b = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let c = a; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(b.to_big_endian()), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_denominator_is_u128( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u128::MAX)); + let c = generate_u256(b, U256::from(u128::MAX)); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_min_denom_high(context: simplex::TestContext) -> anyhow::Result<()> { + let pow129: U256 = U256::from(2).pow(U256::from(129)); + + let a = generate_u256(U256::from(u128::MAX) + 1, pow129); + let b = generate_u256(U256::from(u128::MAX) + 1, pow129); + let c = generate_u256(U256::from(u128::MAX) + 1, pow129 - 1); + + let res = safe_u512_to_u256(a.full_mul(b).div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::one(), U256::MAX); + let b = generate_u256(U256::one(), U256::MAX); + let c = U256::zero(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some([0; 32]), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_algorithm_d_512_256_check( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let pow2_128: U256 = U256::from(u128::MAX) + 1; + + let a = generate_u256(pow2_128, U256::MAX); + let b = generate_u256(pow2_128, U256::MAX - pow2_128); + + let product = a.full_mul(b); + let result_high = + U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); + + let c = generate_u256(max(U256::from(u128::MAX) + 1, result_high + 1), U256::MAX); + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_algorithm_d_512_256_c_is_res_high( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let pow2_128: U256 = U256::from(u128::MAX) + 1; + + let a: U256 = generate_u256(pow2_128, U256::MAX); + let b = generate_u256(pow2_128, U256::MAX - pow2_128); + + let product = a.full_mul(b); + let result_high = + U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); + + let c = result_high + 1; + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_normalize_to_threshold_512_127_norm_is_1( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let pow2_128: U256 = U256::from(u128::MAX) + 1; + + let a: U256 = generate_u256(pow2_128, U256::MAX); + let b = generate_u256(pow2_128, U256::MAX - pow2_128); + + let product = a.full_mul(b); + let result_high = + U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))); + + let c = generate_u256( + max(U256::from(2).pow(U256::from(255)), result_high + 1), + U256::MAX, + ); + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_div_256_normalize_to_threshold_512_127_norm_greater_than_1( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let b = generate_u256( + U256::from(u128::MAX) + 1, + U256::from(2).pow(U256::from(192)), + ); + + let product = a.full_mul(b); + let result_high = + U256::from_big_endian(&(safe_u512_to_u256((product >> 256).to_big_endian()))) + 1; + + let c = generate_u256( + max(U256::from(u128::MAX) + 1, result_high), + U256::from(2).pow(U256::from(255)) - 1, + ); + + let res = safe_u512_to_u256(product.div(c).to_big_endian()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::MulDiv), + a.to_big_endian(), + b.to_big_endian(), + c.to_big_endian(), + Some(res), + ), + Expect::Ok, + ) + } +} diff --git a/tests/u256_test_add.rs b/tests/u256_test_add.rs index 2e5bc44..bdda2ef 100644 --- a/tests/u256_test_add.rs +++ b/tests/u256_test_add.rs @@ -2,7 +2,7 @@ mod common; use primitive_types::U256; -use crate::common::helper::generate_u256; +use crate::common::helper::{DEFAULT_BOOL, generate_u256}; use common::core::{Expect, run}; use simplicityhl_std::artifacts::u256_test_add::U256TestAddProgram; @@ -13,6 +13,7 @@ use simplicityhl_std::artifacts::u256_test_add::derived_u256_test_add::{ enum FunctionToTest { Add256, Add256_128, + FullAdd256, } #[inline] @@ -28,6 +29,7 @@ fn build_witness( function: u8, a: [u8; 32], b: [u8; 32], + c: bool, expected: Option<[u8; 32]>, expected_bool: bool, ) -> U256TestAddWitness { @@ -35,6 +37,7 @@ fn build_witness( function_index: function, first_arg: a, second_arg: b, + third_arg: c, expected, expected_bool, } @@ -56,6 +59,7 @@ mod u256_tests_arithmetic { op(FunctionToTest::Add256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_BOOL, Some(result), false, ), @@ -76,6 +80,7 @@ mod u256_tests_arithmetic { op(FunctionToTest::Add256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_BOOL, Some(result), true, ), @@ -96,6 +101,7 @@ mod u256_tests_arithmetic { op(FunctionToTest::Add256_128), a.to_big_endian(), b.to_big_endian(), + DEFAULT_BOOL, Some(result), false, ), @@ -116,10 +122,115 @@ mod u256_tests_arithmetic { op(FunctionToTest::Add256_128), a.to_big_endian(), b.to_big_endian(), + DEFAULT_BOOL, Some(result), true, ), Expect::Ok, ) } + + #[simplex::test] + fn u256_test_full_add_256_not_overflow_carry_low_false( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX / 2); + let b = generate_u256(U256::zero(), U256::MAX / 2); + + let result = (a + b).to_big_endian(); + let result_carry = false; + let carry_low = false; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd256), + a.to_big_endian(), + b.to_big_endian(), + carry_low, + Some(result), + result_carry, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_full_add_256_overflow_carry_low_false( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::MAX); + + let result = (b - 1).to_big_endian(); + let result_carry = true; + let carry_low = false; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd256), + a.to_big_endian(), + b.to_big_endian(), + carry_low, + Some(result), + result_carry, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_full_add_256_not_overflow_carry_low_true( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX / 2); + let b = generate_u256(U256::zero(), U256::MAX / 2); + + let result = (a + b + 1).to_big_endian(); + let result_carry = false; + let carry_low = true; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd256), + a.to_big_endian(), + b.to_big_endian(), + carry_low, + Some(result), + result_carry, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_full_add_256_overflow_carry_low_true( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = U256::MAX; + let b = generate_u256(U256::one(), U256::MAX).to_big_endian(); + + let result = b; + let result_carry = true; + let carry_low = true; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::FullAdd256), + a.to_big_endian(), + b, + carry_low, + Some(result), + result_carry, + ), + Expect::Ok, + ) + } } diff --git a/tests/u256_test_div.rs b/tests/u256_test_div.rs index 9c36ca9..d74a0ba 100644 --- a/tests/u256_test_div.rs +++ b/tests/u256_test_div.rs @@ -11,7 +11,9 @@ use simplicityhl_std::artifacts::u256_test_div::derived_u256_test_div::{ }; enum FunctionToTest { + CalculateNormalizerBase128, DivMod256_64, + AlgorithmD256_128, DivMod256_128, DivMod256, Div256, @@ -47,6 +49,132 @@ fn build_witness( mod u256_tests_arithmetic { use super::*; + #[simplex::test] + fn u256_test_calculate_normalizer_base_128( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let a_high = (a >> 128).as_u128(); + + let norm = threshold.div_ceil(a_high); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::CalculateNormalizerBase128), + a.to_big_endian(), + DEFAULT_EXPECTED, + Some(U256::from(norm).to_big_endian()), + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_calculate_normalizer_base_128_norm_is_1( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + // a >= 2^255 keeps a_high >= 2^127, so the divisor is already normalized + let a = generate_u256(U256::from(2).pow(U256::from(255)), U256::MAX); + let a_high = (a >> 128).as_u128(); + + let norm = threshold.div_ceil(a_high); + assert_eq!(norm, 1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::CalculateNormalizerBase128), + a.to_big_endian(), + DEFAULT_EXPECTED, + Some(U256::from(norm).to_big_endian()), + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_calculate_normalizer_base_128_norm_greater_than_1( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + // a < 2^255 keeps a_high < 2^127, so the divisor has to be scaled up + let a = generate_u256( + U256::from(u128::MAX) + 1, + U256::from(2).pow(U256::from(255)) - 1, + ); + let a_high = (a >> 128).as_u128(); + + let norm = threshold.div_ceil(a_high); + assert!(norm > 1); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::CalculateNormalizerBase128), + a.to_big_endian(), + DEFAULT_EXPECTED, + Some(U256::from(norm).to_big_endian()), + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_calculate_normalizer_base_128_a_is_u128_fail( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let threshold = 1u128 << 127; + + let a = generate_u256(U256::one(), U256::from(threshold) - 1); + + let norm: u128 = threshold.div_ceil(a.low_u128()); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::CalculateNormalizerBase128), + a.to_big_endian(), + DEFAULT_EXPECTED, + Some(U256::from(norm).to_big_endian()), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u256_test_calculate_normalizer_base_128_b_is_zero_fail( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::CalculateNormalizerBase128), + a, + DEFAULT_EXPECTED, + Some(DEFAULT_EXPECTED), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + #[simplex::test] fn test_div_mod_256_64(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); @@ -88,6 +216,86 @@ mod u256_tests_arithmetic { ) } + #[simplex::test] + fn test_algorithm_d_256_128(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::from(u64::MAX) + 1, U256::from(u128::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::AlgorithmD256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn test_algorithm_d_256_128_fail_b_fits_into_u64( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = generate_u256(U256::one(), U256::from(u64::MAX)); + + let q = (a / b).to_big_endian(); + let r = (a % b).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::AlgorithmD256_128), + a.to_big_endian(), + b.to_big_endian(), + Some(q), + r, + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn test_algorithm_d_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::MAX); + let b = [0; 32]; + + run( + &context, + program(), + build_witness( + op(FunctionToTest::AlgorithmD256_128), + a.to_big_endian(), + b, + Some(DEFAULT_EXPECTED), + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn test_algorithm_d_256_128_a_eq_b(context: simplex::TestContext) -> anyhow::Result<()> { + let a = (generate_u256(U256::one(), U256::from(u128::MAX))).to_big_endian(); + + let q = U256::one().to_big_endian(); + let r = U256::zero().to_big_endian(); + + run( + &context, + program(), + build_witness(op(FunctionToTest::AlgorithmD256_128), a, a, Some(q), r), + Expect::Ok, + ) + } + #[simplex::test] fn test_div_mod_256_128(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); @@ -381,7 +589,7 @@ mod u256_tests_arithmetic { } #[simplex::test] - fn u256_test_div_256_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + fn u256_test_div_256_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { let a = generate_u256(U256::zero(), U256::MAX); let b = [0; 32]; @@ -392,10 +600,10 @@ mod u256_tests_arithmetic { op(FunctionToTest::Div256), a.to_big_endian(), b, - Some(DEFAULT_EXPECTED), + Some([0; 32]), DEFAULT_EXPECTED, ), - Expect::AssertFailed, + Expect::Ok, ) } } diff --git a/tests/u256_test_sub_mul.rs b/tests/u256_test_sub_mul.rs index 7c164a5..ccb3963 100644 --- a/tests/u256_test_sub_mul.rs +++ b/tests/u256_test_sub_mul.rs @@ -1,6 +1,7 @@ mod common; use primitive_types::U256; +use primitive_types::U512; use rand::Rng; use crate::common::helper::{DEFAULT_BOOL, generate_u256}; @@ -16,6 +17,8 @@ enum FunctionToTest { Mul256, Mul256_64, Mul256_128, + Mul512_128, + SafeMul256_128, } #[inline] @@ -24,6 +27,7 @@ fn op(o: FunctionToTest) -> u8 { } const DEFAULT_EXPECTED: [u8; 32] = [0; 32]; +const DEFAULT_U128: u128 = 0; fn program() -> U256TestSubMulProgram { U256TestSubMulProgram::new(&U256TestSubMulArguments {}) @@ -33,17 +37,21 @@ fn build_witness( function: u8, a: [u8; 32], b: [u8; 32], + c: u128, expected: Option<[u8; 32]>, expected_bool: bool, second_expected: [u8; 32], + third_expected: [u8; 32], ) -> U256TestSubMulWitness { U256TestSubMulWitness { function_index: function, first_arg: a, second_arg: b, + third_arg: c, expected, expected_bool, second_expected, + third_expected, } } @@ -70,9 +78,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result), false, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -89,9 +99,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a, a, + DEFAULT_U128, Some([0; 32]), false, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -114,9 +126,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result.to_big_endian()), carry, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -141,9 +155,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result.to_big_endian()), carry, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -161,9 +177,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a, b.to_big_endian(), + DEFAULT_U128, Some(a), false, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -182,9 +200,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Sub256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result.to_big_endian()), true, DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -205,9 +225,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Mul256), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result_high), DEFAULT_BOOL, result_low, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -228,9 +250,11 @@ mod u256_tests_arithmetic { op(FunctionToTest::Mul256_64), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result_high), DEFAULT_BOOL, result_low, + DEFAULT_EXPECTED, ), Expect::Ok, ) @@ -251,11 +275,92 @@ mod u256_tests_arithmetic { op(FunctionToTest::Mul256_128), a.to_big_endian(), b.to_big_endian(), + DEFAULT_U128, Some(result_high), DEFAULT_BOOL, result_low, + DEFAULT_EXPECTED, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_mul_512_128(context: simplex::TestContext) -> anyhow::Result<()> { + let a_1 = generate_u256(U256::one(), U256::MAX); + let a_0 = generate_u256(U256::one(), U256::MAX); + let b = rand::thread_rng().gen_range(1..=u128::MAX); + + let result_low = U512::from(a_0) * U512::from(b); + let result_high = U512::from(a_1) * U512::from(b); + + let (res_1, res_0) = split_u512(result_low.to_big_endian()); + let (res_3, res_2) = split_u512(result_high.to_big_endian()); + + let res_2_1 = U512::from_big_endian(&res_1) + U512::from_big_endian(&res_2); + let (res_3_1, res_2_1) = split_u512(res_2_1.to_big_endian()); + + let res_3_final = + (U256::from_big_endian(&res_3_1) + U256::from_big_endian(&res_3)).to_big_endian(); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::Mul512_128), + a_1.to_big_endian(), + a_0.to_big_endian(), + b, + Some(res_3_final), + DEFAULT_BOOL, + res_2_1, + res_0, + ), + Expect::Ok, + ) + } + + #[simplex::test] + fn u256_test_safe_mul_256_128_fitting(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::zero(), U256::from(u128::MAX)); + let b = generate_u256(U256::zero(), U256::from(u128::MAX)); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeMul256_128), + a.to_big_endian(), + b.to_big_endian(), + DEFAULT_U128, + Some((a * b).to_big_endian()), + DEFAULT_BOOL, + DEFAULT_EXPECTED, + DEFAULT_EXPECTED, ), Expect::Ok, ) } + + #[simplex::test] + fn u256_test_safe_mul_256_128_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = generate_u256(U256::from(u128::MAX) + 1, U256::MAX); + let b = U256::from(u128::MAX); + + run( + &context, + program(), + build_witness( + op(FunctionToTest::SafeMul256_128), + a.to_big_endian(), + b.to_big_endian(), + DEFAULT_U128, + Some(DEFAULT_EXPECTED), + DEFAULT_BOOL, + DEFAULT_EXPECTED, + DEFAULT_EXPECTED, + ), + Expect::AssertFailed, + ) + } } diff --git a/tests/u32_mul_div_test.rs b/tests/u32_mul_div_test.rs new file mode 100644 index 0000000..2487f08 --- /dev/null +++ b/tests/u32_mul_div_test.rs @@ -0,0 +1,100 @@ +mod common; + +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::u32_mul_div_test::U32MulDivTestProgram; +use simplicityhl_std::artifacts::u32_mul_div_test::derived_u32_mul_div_test::{ + U32MulDivTestArguments, U32MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: u32 = 0; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U32MulDivTestProgram { + U32MulDivTestProgram::new(&U32MulDivTestArguments {}) +} + +fn build_witness(op: u8, a: u32, b: u32, c: u32, expected: Option) -> U32MulDivTestWitness { + U32MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +mod u32_mul_div_test { + use super::*; + + #[simplex::test] + fn u32_test_mul_div_32_product_is_u32(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u16::MAX) as u32; + let b = rand::thread_rng().gen_range(0..=u16::MAX) as u32; + let c = rand::thread_rng().gen_range(1..=u32::MAX); + + let res = a * b / c; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_test_mul_div_32_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u32::MAX); + let b = u32::MAX; + let c = rand::thread_rng().gen_range(a..=u32::MAX); + + let res = (a as u64) * (b as u64) / (c as u64); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u32)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u32_test_mul_div_32_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u32::MAX); + let b = u32::MAX; + let c = rand::thread_rng().gen_range(1..a); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u32_test_mul_div_32_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u32::MAX); + let b = rand::thread_rng().gen_range(1..=u32::MAX); + let c = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), + Expect::Ok, + ) + } +} diff --git a/tests/u64_mul_div_test.rs b/tests/u64_mul_div_test.rs new file mode 100644 index 0000000..bbd17d3 --- /dev/null +++ b/tests/u64_mul_div_test.rs @@ -0,0 +1,100 @@ +mod common; + +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::u64_mul_div_test::U64MulDivTestProgram; +use simplicityhl_std::artifacts::u64_mul_div_test::derived_u64_mul_div_test::{ + U64MulDivTestArguments, U64MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: u64 = 0; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U64MulDivTestProgram { + U64MulDivTestProgram::new(&U64MulDivTestArguments {}) +} + +fn build_witness(op: u8, a: u64, b: u64, c: u64, expected: Option) -> U64MulDivTestWitness { + U64MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +mod u64_mul_div_test { + use super::*; + + #[simplex::test] + fn u64_test_mul_div_64_product_is_u64(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(0..=u32::MAX) as u64; + let b = rand::thread_rng().gen_range(0..=u32::MAX) as u64; + let c = rand::thread_rng().gen_range(1..=u64::MAX); + + let res = a * b / c; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_test_mul_div_64_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u64::MAX); + let b = u64::MAX; + let c = rand::thread_rng().gen_range(a..=u64::MAX); + + let res = (a as u128) * (b as u128) / (c as u128); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u64)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u64_test_mul_div_64_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u64::MAX); + let b = u64::MAX; + let c = rand::thread_rng().gen_range(1..a); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u64_test_mul_div_64_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u64::MAX); + let b = rand::thread_rng().gen_range(1..=u64::MAX); + let c = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), + Expect::Ok, + ) + } +} diff --git a/tests/u8_math_tests.rs b/tests/u8_math_test.rs similarity index 100% rename from tests/u8_math_tests.rs rename to tests/u8_math_test.rs diff --git a/tests/u8_mul_div_test.rs b/tests/u8_mul_div_test.rs new file mode 100644 index 0000000..f18e46a --- /dev/null +++ b/tests/u8_mul_div_test.rs @@ -0,0 +1,100 @@ +mod common; + +use crate::common::core::{Expect, run}; +use rand::Rng; + +use simplicityhl_std::artifacts::u8_mul_div_test::U8MulDivTestProgram; +use simplicityhl_std::artifacts::u8_mul_div_test::derived_u8_mul_div_test::{ + U8MulDivTestArguments, U8MulDivTestWitness, +}; + +const DEFAULT_EXPECTED: u8 = 0; + +enum FunctionToTest { + MulDiv, +} + +#[inline] +fn op(o: FunctionToTest) -> u8 { + o as u8 +} + +fn program() -> U8MulDivTestProgram { + U8MulDivTestProgram::new(&U8MulDivTestArguments {}) +} + +fn build_witness(op: u8, a: u8, b: u8, c: u8, expected: Option) -> U8MulDivTestWitness { + U8MulDivTestWitness { + function_index: op, + first_arg: a, + second_arg: b, + third_arg: c, + expected, + } +} + +mod u8_mul_div_test { + use super::*; + + #[simplex::test] + fn u8_test_mul_div_8_product_is_u8(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u8::MAX); + let b = u8::MAX / a; + let c = rand::thread_rng().gen_range(1..=u8::MAX); + + let res = a * b / c; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u8_test_mul_div_8_intermediate_overflow( + context: simplex::TestContext, + ) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u8::MAX); + let b = u8::MAX; + let c = rand::thread_rng().gen_range(a..=u8::MAX); + + let res = (a as u16) * (b as u16) / (c as u16); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(res as u8)), + Expect::Ok, + ) + } + + #[simplex::test] + fn u8_test_mul_div_8_result_overflow(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(2..=u8::MAX); + let b = u8::MAX; + let c = rand::thread_rng().gen_range(1..a); + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(DEFAULT_EXPECTED)), + Expect::AssertFailed, + ) + } + + #[simplex::test] + fn u8_test_mul_div_8_div_by_zero(context: simplex::TestContext) -> anyhow::Result<()> { + let a = rand::thread_rng().gen_range(1..=u8::MAX); + let b = rand::thread_rng().gen_range(1..=u8::MAX); + let c = 0; + + run( + &context, + program(), + build_witness(op(FunctionToTest::MulDiv), a, b, c, Some(0)), + Expect::Ok, + ) + } +}