diff --git a/.gitignore b/.gitignore index 791af9f..6c477af 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /target /logs +/mutants.out* +/lcov.info diff --git a/Cargo.lock b/Cargo.lock index 4ebe6f5..5dee45c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,52 +2,1129 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloy-rlp" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26154390b1d205a4a7ac7352aa2eb4f81f391399d4e2f546fb81a2f8bb383f62" +dependencies = [ + "arrayvec", + "bytes", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "ark-ff" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b3235cc41ee7a12aaaf2c575a2ad7b46713a8a50bda2fc3b003a04845c05dd6" +dependencies = [ + "ark-ff-asm 0.3.0", + "ark-ff-macros 0.3.0", + "ark-serialize 0.3.0", + "ark-std 0.3.0", + "derivative", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.3.3", + "zeroize", +] + +[[package]] +name = "ark-ff" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec847af850f44ad29048935519032c33da8aa03340876d351dfab5660d2966ba" +dependencies = [ + "ark-ff-asm 0.4.2", + "ark-ff-macros 0.4.2", + "ark-serialize 0.4.2", + "ark-std 0.4.0", + "derivative", + "digest 0.10.7", + "itertools 0.10.5", + "num-bigint", + "num-traits", + "paste", + "rustc_version 0.4.1", + "zeroize", +] + +[[package]] +name = "ark-ff-asm" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db02d390bf6643fb404d3d22d31aee1c4bc4459600aef9113833d17e786c6e44" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-asm" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed4aa4fe255d0bc6d79373f7e31d2ea147bcf486cba1be5ba7ea85abdb92348" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fd794a08ccb318058009eefdf15bcaaaaf6f8161eb3345f907222bac38b20" +dependencies = [ + "num-bigint", + "num-traits", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-ff-macros" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abe79b0e4288889c4574159ab790824d0033b9fdcb2a112a3182fac2e514565" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "ark-serialize" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6c2b318ee6e10f8c2853e73a83adc0ccb88995aa978d8a3408d492ab2ee671" +dependencies = [ + "ark-std 0.3.0", + "digest 0.9.0", +] + +[[package]] +name = "ark-serialize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb7b85a02b83d2f22f89bd5cac66c9c89474240cb6207cb1efc16d098e822a5" +dependencies = [ + "ark-std 0.4.0", + "digest 0.10.7", + "num-bigint", +] + +[[package]] +name = "ark-std" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "ark-std" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" +dependencies = [ + "num-traits", + "rand", +] + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "auto_impl" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + [[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byte-slice-cast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ac9f8b63eca6fd385229b3675f6cc0dc5c8a5c8a54a59d4f52ffd670d87b0c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5a21b8495e732f1b3c364c9949b201ca7bae518c502c80256c96ad79eaf6ac" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cf2dd12af7a047ad9d6da2b6b249759a22a7abc0f474c1dae1777afa4b21a73" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "const-decimal" +version = "0.1.0" +dependencies = [ + "criterion", + "expect-test", + "malachite", + "num-traits", + "paste", + "proptest", + "ruint", + "serde", + "thiserror", +] + +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "crypto-common", +] + +[[package]] +name = "dissimilar" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "expect-test" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e0be0a561335815e06dab7c62e50353134c796e7a6155402a64bcff66b6a5e0" +dependencies = [ + "dissimilar", + "once_cell", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + +[[package]] +name = "fastrlp" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139834ddba373bbdd213dffe02c8d110508dcf1726c2be27e8d1f7d7e1856418" +dependencies = [ + "arrayvec", + "auto_impl", + "bytes", +] + +[[package]] +name = "fixed-hash" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" +dependencies = [ + "byteorder", + "rand", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "impl-codec" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba6a270039626615617f3f36d15fc827041df3b78c439da2cadfa47455a77f2f" +dependencies = [ + "parity-scale-codec", +] + +[[package]] +name = "impl-trait-for-tuples" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d7a9f6330b71fea57921c9b61c47ee6e84f72d394754eff6163ae67e7395eb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "indexmap" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is-terminal" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "261f68e344040fbd0edea105bef17c66edf46f984ddb1115b775ce31be948f4b" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" + +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "malachite" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5616515d632967cd329b6f6db96be9a03ea0b3a49cdbc45b0016803dad8a77b7" +dependencies = [ + "malachite-base", + "malachite-nz", + "malachite-q", +] + +[[package]] +name = "malachite-base" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46059721011b0458b7bd6d9179be5d0b60294281c23320c207adceaecc54d13b" +dependencies = [ + "hashbrown", + "itertools 0.11.0", + "libm", + "ryu", +] + +[[package]] +name = "malachite-nz" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1503b27e825cabd1c3d0ff1e95a39fb2ec9eab6fd3da6cfa41aec7091d273e78" +dependencies = [ + "itertools 0.11.0", + "libm", + "malachite-base", +] + +[[package]] +name = "malachite-q" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a475503a70a3679dbe3b9b230a23622516742528ba614a7b2490f180ea9cb514" +dependencies = [ + "itertools 0.11.0", + "malachite-base", + "malachite-nz", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "oorandom" +version = "11.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b410bbe7e14ab526a0e86877eb47c6996a2bd7746f027ba551028c925390e4e9" + +[[package]] +name = "parity-scale-codec" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306800abfa29c7f16596b5970a588435e3d5b3149683d00c12b699cc19f895ee" +dependencies = [ + "arrayvec", + "bitvec", + "byte-slice-cast", + "impl-trait-for-tuples", + "parity-scale-codec-derive", + "serde", +] + +[[package]] +name = "parity-scale-codec-derive" +version = "3.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d830939c76d294956402033aee57a6da7b438f2294eb94864c37b0569053a42c" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pest" +version = "2.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c73c26c01b8c87956cea613c907c9d6ecffd8d18a2a5908e5de0adfaa185cea" +dependencies = [ + "memchr", + "thiserror", + "ucd-trie", +] + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "primitive-types" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b34d9fd68ae0b74a41b21c03c2f62847aa0ffea044eee893b4c140b37e244e2" +dependencies = [ + "fixed-hash", + "impl-codec", + "uint", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c2511913b88df1637da85cc8d96ec8e43a3f8bb8ccb71ee1ac240d6f3df58d" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "lazy_static", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_xorshift" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] [[package]] -name = "const-decimal" -version = "0.1.0" +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rlp" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb919243f34364b6bd2fc10ef797edbfa75f33c252e7998527479c6d6b47e1ec" +dependencies = [ + "bytes", + "rustc-hex", +] + +[[package]] +name = "ruint" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c3cc4c2511671f327125da14133d0c5c5d137f006a1017a16f557bc85b16286" dependencies = [ + "alloy-rlp", + "ark-ff 0.3.0", + "ark-ff 0.4.2", + "bytes", + "fastrlp", + "num-bigint", "num-traits", - "paste", + "parity-scale-codec", + "primitive-types", + "proptest", + "rand", + "rlp", + "ruint-macro", "serde", + "valuable", + "zeroize", ] [[package]] -name = "num-traits" -version = "0.2.19" +name = "ruint-macro" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" dependencies = [ - "autocfg", + "semver 0.11.0", ] [[package]] -name = "paste" -version = "1.0.15" +name = "rustc_version" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.23", +] [[package]] -name = "proc-macro2" -version = "1.0.86" +name = "rustix" +version = "0.38.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" dependencies = [ - "unicode-ident", + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", ] [[package]] -name = "quote" -version = "1.0.37" +name = "rusty-fork" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ - "proc-macro2", + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", ] [[package]] @@ -67,7 +1144,36 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.77", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] @@ -81,8 +1187,356 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" + +[[package]] +name = "toml_edit" +version = "0.22.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" +dependencies = [ + "indexmap", + "toml_datetime", + "winnow", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "uint" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76f64bba2c53b04fcab63c01a7d7427eadc821e3bc48c34dc9ba29c501164b52" +dependencies = [ + "byteorder", + "crunchy", + "hex", + "static_assertions", +] + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wait-timeout" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f" +dependencies = [ + "memchr", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] diff --git a/Cargo.toml b/Cargo.toml index cb97b29..e672471 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,21 +6,36 @@ edition = "2021" readme = "README.md" [lints.clippy] -cast_possible_truncation = 'warn' -cast_possible_wrap = 'warn' -cast_sign_loss = 'warn' +pedantic = "warn" + +cast_possible_truncation = "warn" +cast_possible_wrap = "warn" +cast_sign_loss = "warn" # See `clippy.toml`. -disallowed_methods = 'warn' +disallowed_methods = "warn" +arithmetic_side_effects = "warn" +match_bool = "allow" [features] serde = ["dep:serde"] +[[bench]] +name = "main" +path = "benches/main.rs" +harness = false + [dependencies] num-traits = "0.2.19" -serde = { version = "1.0.210", features = ["derive"], optional = true } +paste = "1.0.15" +ruint = "1.12.3" +serde = { version = "~1.0", features = ["derive"], optional = true } +thiserror = "1.0.63" [dev-dependencies] -paste = "1.0.15" +criterion = "0.5.1" +expect-test = "1.5.0" +malachite = "0.4.16" +proptest = "1.5.0" [profile.release] opt-level = 3 diff --git a/README.md b/README.md index 5b1c380..e3c492b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,19 @@ -# Const Point +# Const Decimal -Fixed precision maths type implemented using compile time constants. +`const-decimal` is a fixed precision decimal backed by an underlying integer. +This means it can compute Add/Sub/Mul/Div* operations within it's range without +precision loss. This crate was created as there was no pre-existing crate that +offered these features for decimal numbers (`fixed` cannot represent decimals +with precision). + +This is a fairly simple crate and is mainly intended to serve as a lossless +representation of decimal values. While math operations are supported it is not +intended to be a general purpose maths library. + +## Goals + +The goals in order of priority are: + +- Safety: Panic on over/underflow (even in release). +- Precision: Use integers instead of floats. +- Performance: Use primitive underlying types were possible. diff --git a/benches/add.rs b/benches/add.rs new file mode 100644 index 0000000..b58c8fd --- /dev/null +++ b/benches/add.rs @@ -0,0 +1,49 @@ +use const_decimal::{Decimal, Integer, Primitive}; +use criterion::measurement::WallTime; +use criterion::{black_box, BatchSize, BenchmarkGroup}; +use prop::strategy::ValueTree; +use prop::test_runner::TestRunner; +use proptest::prelude::*; + +pub fn bench_all(group: &mut BenchmarkGroup<'_, WallTime>) +where + I: Integer + Arbitrary, +{ + bench_primitive_add::(group); + bench_decimal_add::(group); +} + +fn bench_primitive_add(group: &mut BenchmarkGroup<'_, WallTime>) +where + I: Primitive + Arbitrary, +{ + // Use proptest to generate arbitrary input values. + let mut runner = TestRunner::deterministic(); + let input = (I::arbitrary(), I::arbitrary()); + + group.bench_function("primitive/add", |bencher| { + bencher.iter_batched( + || input.new_tree(&mut runner).unwrap().current(), + |(a, b)| black_box(black_box(a) + black_box(b)), + BatchSize::SmallInput, + ) + }); +} + +fn bench_decimal_add(group: &mut BenchmarkGroup<'_, WallTime>) +where + I: Integer + Arbitrary, +{ + // Use proptest to generate arbitrary input values. + let mut runner = TestRunner::deterministic(); + let input = (I::arbitrary(), I::arbitrary()) + .prop_map(|(a, b)| (Decimal::<_, D>(a / I::TWO), Decimal::<_, D>(b / I::TWO))); + + group.bench_function("decimal/add", |bencher| { + bencher.iter_batched( + || input.new_tree(&mut runner).unwrap().current(), + |(a, b)| black_box(black_box(a) + black_box(b)), + BatchSize::SmallInput, + ) + }); +} diff --git a/benches/div.rs b/benches/div.rs new file mode 100644 index 0000000..ca332e3 --- /dev/null +++ b/benches/div.rs @@ -0,0 +1,81 @@ +use std::fmt::Debug; +use std::ops::Div; + +use const_decimal::{Decimal, Integer, Primitive}; +use criterion::measurement::WallTime; +use criterion::{black_box, BatchSize, BenchmarkGroup}; +use prop::strategy::ValueTree; +use prop::test_runner::TestRunner; +use proptest::prelude::*; + +pub fn bench_all( + group: &mut BenchmarkGroup<'_, WallTime>, + lo_strategy: impl Strategy + Clone, + hi_strategy: impl Strategy + Clone, +) where + I: Integer + Debug + Div, +{ + primitive_div::(group, lo_strategy.clone(), "lo"); + decimal_div::(group, lo_strategy, "lo"); + primitive_div::(group, hi_strategy.clone(), "hi"); + decimal_div::(group, hi_strategy, "hi"); +} + +fn primitive_div( + group: &mut BenchmarkGroup<'_, WallTime>, + strategy: impl Strategy + Clone, + strategy_label: &str, +) where + I: Primitive + Div, +{ + // Use proptest to generate arbitrary input values. + let mut runner = TestRunner::deterministic(); + let input = (strategy.clone(), strategy); + + group.bench_function(&format!("primitive/div/{strategy_label}"), |bencher| { + bencher.iter_batched( + || { + let (numer, denom) = input.new_tree(&mut runner).unwrap().current(); + // Avoid division by zero. + let denom = match denom == I::ZERO { + true => I::ONE, + false => denom, + }; + + (numer, denom) + }, + |(a, b)| black_box(black_box(a) / black_box(b)), + BatchSize::SmallInput, + ) + }); +} + +fn decimal_div( + group: &mut BenchmarkGroup<'_, WallTime>, + strategy: impl Strategy + Clone, + strategy_label: &str, +) where + I: Integer + Debug, +{ + // Use proptest to generate arbitrary input values. + let mut runner = TestRunner::deterministic(); + let input = + (strategy.clone(), strategy).prop_map(|(a, b)| (Decimal::<_, D>(a), Decimal::<_, D>(b))); + + group.bench_function(format!("decimal/div/{strategy_label}"), |bencher| { + bencher.iter_batched( + || { + let (numer, denom) = input.new_tree(&mut runner).unwrap().current(); + // Avoid division by zero. + let denom = match denom == Decimal::ZERO { + true => Decimal::ONE, + false => denom, + }; + + (numer, denom) + }, + |(a, b)| black_box(black_box(a) / black_box(b)), + BatchSize::SmallInput, + ) + }); +} diff --git a/benches/main.rs b/benches/main.rs new file mode 100644 index 0000000..71b2106 --- /dev/null +++ b/benches/main.rs @@ -0,0 +1,74 @@ +use std::fmt::Debug; +use std::ops::{Div, Neg}; + +use const_decimal::Integer; +use criterion::measurement::WallTime; +use criterion::BenchmarkGroup; +use num_traits::ConstOne; +use proptest::prelude::{Arbitrary, Strategy}; + +mod add; +mod div; +mod mul; +mod sub; + +fn to_sign(positive: bool) -> I +where + I: ConstOne + Neg, +{ + match positive { + true => I::ONE, + false => -I::ONE, + } +} + +fn main() { + let mut criterion = criterion::Criterion::default().configure_from_args(); + + // TODO: Our `hi` range for signed integers does not sample negative values. + + bench_integers::<9, u64>( + &mut criterion.benchmark_group("u64_9"), + 0..(u32::MAX as u64), + ((u32::MAX as u64) - 10u64.pow(9) + 1)..(u64::MAX / 10u64.pow(9)), + (u64::MAX / 10u64.pow(9) + 1)..u64::MAX, + ); + bench_integers::<9, i64>( + &mut criterion.benchmark_group("i64_9"), + (i32::MIN as i64)..(i32::MAX as i64), + (bool::arbitrary(), ((u32::MAX as i64) + 1)..(i64::MAX / 10i64.pow(9))) + .prop_map(|(sign, unsigned)| to_sign::(sign) * unsigned), + (bool::arbitrary(), (i64::MAX / 10i64.pow(9) + 1)..i64::MAX) + .prop_map(|(sign, unsigned)| to_sign::(sign) * unsigned), + ); + bench_integers::<18, u128>( + &mut criterion.benchmark_group("u128_18"), + 0..(u64::MAX as u128), + ((u64::MAX as u128) - 10u128.pow(18) + 1)..(u128::MAX / 10u128.pow(18)), + (u128::MAX / 10u128.pow(18) + 1)..u128::MAX, + ); + bench_integers::<18, i128>( + &mut criterion.benchmark_group("i128_18"), + (i64::MIN as i128)..(i64::MAX as i128), + (bool::arbitrary(), ((u64::MAX as i128) + 1)..(i128::MAX / 10i128.pow(18))) + .prop_map(|(sign, unsigned)| to_sign::(sign) * unsigned), + (bool::arbitrary(), (i128::MAX / 10i128.pow(18) + 1)..i128::MAX) + .prop_map(|(sign, unsigned)| to_sign::(sign) * unsigned), + ); + + criterion.final_summary(); +} + +fn bench_integers( + group: &mut BenchmarkGroup<'_, WallTime>, + lo_range: impl Strategy + Clone + Debug, + hi_mul_range: impl Strategy + Clone + Debug, + hi_div_range: impl Strategy + Clone + Debug, +) where + I: Integer + Arbitrary + Div, +{ + add::bench_all::(group); + sub::bench_all::(group); + mul::bench_all::(group, lo_range.clone(), hi_mul_range); + div::bench_all::(group, lo_range, hi_div_range); +} diff --git a/benches/mul.rs b/benches/mul.rs new file mode 100644 index 0000000..2544501 --- /dev/null +++ b/benches/mul.rs @@ -0,0 +1,62 @@ +use std::fmt::Debug; + +use const_decimal::{Decimal, Integer, Primitive}; +use criterion::measurement::WallTime; +use criterion::{black_box, BatchSize, BenchmarkGroup}; +use prop::strategy::ValueTree; +use prop::test_runner::TestRunner; +use proptest::prelude::*; + +pub fn bench_all( + group: &mut BenchmarkGroup<'_, WallTime>, + lo_strategy: impl Strategy + Clone, + hi_strategy: impl Strategy + Clone, +) where + I: Integer + Debug, +{ + primitive_mul::(group, lo_strategy.clone(), "lo"); + decimal_mul::(group, lo_strategy, "lo"); + primitive_mul::(group, hi_strategy.clone(), "hi"); + decimal_mul::(group, hi_strategy, "hi"); +} + +fn primitive_mul( + group: &mut BenchmarkGroup<'_, WallTime>, + strategy: impl Strategy + Clone, + strategy_label: &str, +) where + I: Primitive, +{ + // Use proptest to generate arbitrary input values. + let mut runner = TestRunner::deterministic(); + let input = (strategy.clone(), strategy); + + group.bench_function(&format!("primitive/mul/{strategy_label}"), |bencher| { + bencher.iter_batched( + || input.new_tree(&mut runner).unwrap().current(), + |(a, b)| black_box(black_box(a) * black_box(b)), + BatchSize::SmallInput, + ) + }); +} + +fn decimal_mul( + group: &mut BenchmarkGroup<'_, WallTime>, + strategy: impl Strategy + Clone, + strategy_label: &str, +) where + I: Integer + Debug, +{ + // Use proptest to generate arbitrary input values. + let mut runner = TestRunner::deterministic(); + let input = + (strategy.clone(), strategy).prop_map(|(a, b)| (Decimal::<_, D>(a), Decimal::<_, D>(b))); + + group.bench_function(format!("decimal/mul/{strategy_label}"), |bencher| { + bencher.iter_batched( + || input.new_tree(&mut runner).unwrap().current(), + |(a, b)| black_box(black_box(a) * black_box(b)), + BatchSize::SmallInput, + ) + }); +} diff --git a/benches/sub.rs b/benches/sub.rs new file mode 100644 index 0000000..3edb9f3 --- /dev/null +++ b/benches/sub.rs @@ -0,0 +1,56 @@ +use const_decimal::{Decimal, Integer, Primitive}; +use criterion::measurement::WallTime; +use criterion::{black_box, BatchSize, BenchmarkGroup}; +use prop::strategy::ValueTree; +use prop::test_runner::TestRunner; +use proptest::prelude::*; + +pub fn bench_all(group: &mut BenchmarkGroup<'_, WallTime>) +where + I: Integer + Arbitrary, +{ + bench_primitive_sub::(group); + bench_decimal_sub::(group); +} + +fn bench_primitive_sub(group: &mut BenchmarkGroup<'_, WallTime>) +where + I: Primitive + Arbitrary, +{ + // Use proptest to generate arbitrary input values. + let mut runner = TestRunner::deterministic(); + let input = (I::arbitrary(), I::arbitrary()); + + group.bench_function("primitive/sub", |bencher| { + bencher.iter_batched( + || input.new_tree(&mut runner).unwrap().current(), + |(a, b)| black_box(black_box(a) - black_box(b)), + BatchSize::SmallInput, + ) + }); +} + +fn bench_decimal_sub(group: &mut BenchmarkGroup<'_, WallTime>) +where + I: Integer + Arbitrary, +{ + // Use proptest to generate arbitrary input values. + let mut runner = TestRunner::deterministic(); + let input = (I::arbitrary(), I::arbitrary()) + .prop_map(|(a, b)| (Decimal::<_, D>(a / I::TWO), Decimal::<_, D>(b / I::TWO))); + + group.bench_function("decimal/sub", |bencher| { + bencher.iter_batched( + || { + let (a, b) = input.new_tree(&mut runner).unwrap().current(); + + match a >= b { + true => (a, b), + false => (b, a), + } + }, + |(a, b)| black_box(black_box(a) - black_box(b)), + BatchSize::SmallInput, + ) + }); +} diff --git a/src/aliases.rs b/src/aliases.rs new file mode 100644 index 0000000..81e974b --- /dev/null +++ b/src/aliases.rs @@ -0,0 +1,6 @@ +use crate::Decimal; + +pub type Uint64_9 = Decimal; +pub type Uint128_18 = Decimal; +pub type Int64_9 = Decimal; +pub type Int128_18 = Decimal; diff --git a/src/cheats.rs b/src/cheats.rs new file mode 100644 index 0000000..c54b3a2 --- /dev/null +++ b/src/cheats.rs @@ -0,0 +1,30 @@ +use paste::paste; + +pub trait Cheats { + const TWO: Self; + const TEN: Self; + const SCALING_FACTOR: Self; +} + +macro_rules! impl_primitive { + ($primitive:tt) => { + impl Cheats for $primitive { + const TWO: Self = 2; + const TEN: Self = 10; + paste! { + const SCALING_FACTOR: Self = [<10 $primitive>].pow(D as u32); + } + } + }; +} + +impl_primitive!(u8); +impl_primitive!(i8); +impl_primitive!(u16); +impl_primitive!(i16); +impl_primitive!(u32); +impl_primitive!(i32); +impl_primitive!(u64); +impl_primitive!(i64); +impl_primitive!(u128); +impl_primitive!(i128); diff --git a/src/const_traits.rs b/src/const_traits.rs deleted file mode 100644 index 7511a06..0000000 --- a/src/const_traits.rs +++ /dev/null @@ -1,19 +0,0 @@ -pub trait PrecisionFactor { - const PRECISION_FACTOR: Self; -} - -impl PrecisionFactor for u64 { - const PRECISION_FACTOR: Self = 10u64.pow(D as u32); -} - -impl PrecisionFactor for i64 { - const PRECISION_FACTOR: Self = 10i64.pow(D as u32); -} - -impl PrecisionFactor for u128 { - const PRECISION_FACTOR: Self = 10u128.pow(D as u32); -} - -impl PrecisionFactor for i128 { - const PRECISION_FACTOR: Self = 10i128.pow(D as u32); -} diff --git a/src/conversion.rs b/src/conversion.rs new file mode 100644 index 0000000..f605e65 --- /dev/null +++ b/src/conversion.rs @@ -0,0 +1,66 @@ +use crate::{Decimal, Int128_18, Int64_9, Uint128_18, Uint64_9}; + +// TODO: Implement From generically where the result cannot overflow. +// TODO: Implement TryFrom generically where the result can overflow. + +impl From for Uint128_18 { + fn from(value: Uint64_9) -> Self { + // We know this multiplication can never overflow. + #[allow(clippy::arithmetic_side_effects)] + Decimal((u128::from(value.0)) * 10u128.pow(9)) + } +} + +impl From for Int128_18 { + fn from(value: Int64_9) -> Self { + // We know this multiplication can never overflow. + #[allow(clippy::arithmetic_side_effects)] + Decimal((i128::from(value.0)) * 10i128.pow(9)) + } +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; + + use proptest::prelude::Arbitrary; + use proptest::test_runner::TestRunner; + + use super::*; + + #[test] + fn uint128_18_from_uint64_9() { + let mut runner = TestRunner::default(); + let input = Decimal::arbitrary(); + + runner + .run(&input, |decimal: Decimal| { + let out = Uint128_18::from(decimal); + let out_f = f64::from_str(&out.to_string()).unwrap(); + let decimal_f = f64::from_str(&decimal.to_string()).unwrap(); + + assert_eq!(out_f, decimal_f); + + Ok(()) + }) + .unwrap(); + } + + #[test] + fn int128_18_from_int64_9() { + let mut runner = TestRunner::default(); + let input = Decimal::arbitrary(); + + runner + .run(&input, |decimal: Decimal| { + let out = Int128_18::from(decimal); + let out_f = f64::from_str(&out.to_string()).unwrap(); + let decimal_f = f64::from_str(&decimal.to_string()).unwrap(); + + assert_eq!(out_f, decimal_f); + + Ok(()) + }) + .unwrap(); + } +} diff --git a/src/decimal.rs b/src/decimal.rs index 137e23f..e750ea7 100644 --- a/src/decimal.rs +++ b/src/decimal.rs @@ -1,50 +1,45 @@ -use std::ops::{Add, Div, Mul, Sub}; +use std::cmp::Ordering; +use std::ops::{Add, Div, Mul, Neg, Sub}; -use num_traits::{ConstOne, One}; - -use crate::const_traits::PrecisionFactor; - -pub type Uint64 = Decimal; -pub type Uint128 = Decimal; -pub type Int64 = Decimal; -pub type Int128 = Decimal; +use crate::integer::{Integer, SignedInteger}; #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] pub struct Decimal(pub I); -pub trait Integer: - PrecisionFactor - + ConstOne - + One - + Add - + Sub - + Mul - + Div - + Clone - + Copy -{ -} - -impl Integer for I where - I: PrecisionFactor - + ConstOne - + One - + Add - + Sub - + Mul - + Div - + Clone - + Copy -{ -} - impl Decimal where I: Integer, { - pub const ONE: Decimal = Decimal(I::PRECISION_FACTOR); - pub const PRECISION_FACTOR: I = I::PRECISION_FACTOR; + pub const ZERO: Decimal = Decimal(I::ZERO); + pub const ONE: Decimal = Decimal(I::SCALING_FACTOR); + pub const DECIMALS: u8 = D; + pub const SCALING_FACTOR: I = I::SCALING_FACTOR; + + // TODO: See if we can generate a constant. + #[must_use] + pub fn min() -> Self { + Decimal(I::min_value()) + } + + // TODO: See if we can generate a constant. + #[must_use] + pub fn max() -> Self { + Decimal(I::max_value()) + } + + pub fn from_scaled(integer: I, scale: u8) -> Self { + match scale.cmp(&D) { + Ordering::Greater => todo!(), + Ordering::Less => todo!(), + Ordering::Equal => Decimal(integer), + } + } + + pub fn is_zero(&self) -> bool { + self.0 == I::ZERO + } } impl Add for Decimal @@ -55,7 +50,7 @@ where #[inline] fn add(self, rhs: Self) -> Self::Output { - Decimal(self.0 + rhs.0) + Decimal(self.0.checked_add(&rhs.0).unwrap()) } } @@ -67,7 +62,7 @@ where #[inline] fn sub(self, rhs: Self) -> Self::Output { - Decimal(self.0 - rhs.0) + Decimal(self.0.checked_sub(&rhs.0).unwrap()) } } @@ -79,7 +74,7 @@ where #[inline] fn mul(self, rhs: Self) -> Self::Output { - Decimal(self.0 * rhs.0 / I::PRECISION_FACTOR) + Decimal(I::full_mul_div(self.0, rhs.0, I::SCALING_FACTOR)) } } @@ -91,15 +86,30 @@ where #[inline] fn div(self, rhs: Self) -> Self::Output { - Decimal(self.0 * I::PRECISION_FACTOR / rhs.0) + Decimal(I::full_mul_div(self.0, I::SCALING_FACTOR, rhs.0)) + } +} + +impl Neg for Decimal +where + I: SignedInteger, +{ + type Output = Self; + + fn neg(self) -> Self::Output { + Decimal(self.0.checked_neg().unwrap()) } } #[cfg(test)] mod tests { + use std::ops::Shr; + use paste::paste; + use proptest::prelude::*; use super::*; + use crate::{Int128_18, Int64_9, Uint128_18, Uint64_9}; macro_rules! test_basic_ops { ($variant:ty) => { @@ -108,7 +118,7 @@ mod tests { fn [<$variant:lower _add>]() { assert_eq!( $variant::ONE + $variant::ONE, - Decimal($variant::PRECISION_FACTOR * 2), + Decimal($variant::SCALING_FACTOR * 2), ); } @@ -126,12 +136,147 @@ mod tests { fn [<$variant:lower _div>]() { assert_eq!($variant::ONE / $variant::ONE, $variant::ONE); } + + #[test] + fn [<$variant:lower _mul_min_by_one>]() { + assert_eq!($variant::min() * $variant::ONE, $variant::min()); + } + + #[test] + fn [<$variant:lower _div_min_by_one>]() { + assert_eq!($variant::min() / $variant::ONE, $variant::min()); + } + + #[test] + fn [<$variant:lower _mul_max_by_one>]() { + assert_eq!($variant::max() * $variant::ONE, $variant::max()); + } + + #[test] + fn [<$variant:lower _div_max_by_one>]() { + assert_eq!($variant::max() / $variant::ONE, $variant::max()); + } + } + }; + } + + test_basic_ops!(Uint64_9); + test_basic_ops!(Uint128_18); + test_basic_ops!(Int64_9); + test_basic_ops!(Int128_18); + + macro_rules! fuzz_against_primitive { + ($primitive:tt, $decimals:literal) => { + paste! { + proptest! { + /// Addition functions the same as regular unsigned integer addition. + #[test] + fn [<$primitive _ $decimals _add>]( + x in $primitive::MIN..$primitive::MAX, + y in $primitive::MIN..$primitive::MAX, + ) { + let decimal = std::panic::catch_unwind( + || Decimal::<_, $decimals>(x) + Decimal(y) + ); + let primitive = std::panic::catch_unwind(|| x.checked_add(y).unwrap()); + + match (decimal, primitive) { + (Ok(decimal), Ok(primitive)) => assert_eq!(decimal.0, primitive), + (Err(_), Err(_)) => {} + (decimal, primitive) => panic!( + "Mismatch; decimal={decimal:?}; primitive={primitive:?}" + ) + } + } + + /// Subtraction functions the same as regular unsigned integer addition. + #[test] + fn [<$primitive _ $decimals _sub>]( + x in $primitive::MIN..$primitive::MAX, + y in $primitive::MIN..$primitive::MAX, + ) { + let decimal = std::panic::catch_unwind( + || Decimal::<_, $decimals>(x) - Decimal(y) + ); + let primitive = std::panic::catch_unwind(|| x.checked_sub(y).unwrap()); + + match (decimal, primitive) { + (Ok(decimal), Ok(primitive)) => assert_eq!(decimal.0, primitive), + (Err(_), Err(_)) => {} + (decimal, primitive) => panic!( + "Mismatch; decimal={decimal:?}; primitive={primitive:?}", + ) + } + } + + /// Multiplication requires the result to be divided by the scaling factor. + #[test] + fn [<$primitive _ $decimals _mul>]( + x in ($primitive::MIN.shr($primitive::BITS / 2)) + ..($primitive::MAX.shr($primitive::BITS / 2)), + y in ($primitive::MIN.shr($primitive::BITS / 2)) + ..($primitive::MAX.shr($primitive::BITS / 2)), + ) { + let decimal = std::panic::catch_unwind( + || Decimal::<_, $decimals>(x) * Decimal(y) + ); + let primitive = std::panic::catch_unwind( + || x + .checked_mul(y) + .unwrap() + .checked_div($primitive::pow(10, $decimals)) + .unwrap() + ); + + match (decimal, primitive) { + (Ok(decimal), Ok(primitive)) => assert_eq!(decimal.0, primitive), + (Err(_), Err(_)) => {} + (decimal, primitive) => panic!( + "Mismatch; decimal={decimal:?}; primitive={primitive:?}" + ) + } + } + + /// Division requires the numerator to first be scaled by the scaling factor. + #[test] + fn [<$primitive _ $decimals _div>]( + x in ($primitive::MIN / $primitive::pow(10, $decimals)) + ..($primitive::MAX / $primitive::pow(10, $decimals)), + y in ($primitive::MIN / $primitive::pow(10, $decimals)) + ..($primitive::MAX / $primitive::pow(10, $decimals)), + ) { + let decimal = std::panic::catch_unwind( + || Decimal::<_, $decimals>(x) / Decimal(y) + ); + let primitive = std::panic::catch_unwind( + || x + .checked_mul($primitive::pow(10, $decimals)) + .unwrap() + .checked_div(y) + .unwrap() + ); + + match (decimal, primitive) { + (Ok(decimal), Ok(primitive)) => assert_eq!(decimal.0, primitive), + (Err(_), Err(_)) => {} + (decimal, primitive) => panic!( + "Mismatch; decimal={decimal:?}; primitive={primitive:?}" + ) + } + } + } } }; } - test_basic_ops!(Uint64); - test_basic_ops!(Uint128); - test_basic_ops!(Int64); - test_basic_ops!(Int128); + fuzz_against_primitive!(u8, 1); + fuzz_against_primitive!(i8, 1); + fuzz_against_primitive!(u16, 2); + fuzz_against_primitive!(i16, 2); + fuzz_against_primitive!(u32, 5); + fuzz_against_primitive!(i32, 5); + fuzz_against_primitive!(u64, 9); + fuzz_against_primitive!(i64, 9); + fuzz_against_primitive!(u128, 18); + fuzz_against_primitive!(i128, 18); } diff --git a/src/display.rs b/src/display.rs new file mode 100644 index 0000000..0ec8aa9 --- /dev/null +++ b/src/display.rs @@ -0,0 +1,284 @@ +use std::cmp::Ordering; +use std::fmt::Display; +use std::num::ParseIntError; +use std::str::FromStr; + +use thiserror::Error; + +use crate::{Decimal, Integer}; + +impl Display for Decimal +where + I: Integer, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let (sign, unsigned) = match self.0 < I::ONE { + // NB: Integers do not implement negation, so lets use two's complement to flip the sign + // of the signed integer (modelled as an unsigned integer). + true => ("-", (!self.0).wrapping_add(&I::ONE)), + false => ("", self.0), + }; + // `SCALING_FACTOR` cannot be zero. + #[allow(clippy::arithmetic_side_effects)] + let integer = unsigned / I::SCALING_FACTOR; + // `SCALING_FACTOR` cannot be zero. + #[allow(clippy::arithmetic_side_effects)] + let fractional = unsigned % I::SCALING_FACTOR; + + write!(f, "{sign}{integer}.{fractional:0>decimals$}", decimals = D as usize) + } +} + +impl FromStr for Decimal +where + I: Integer, +{ + type Err = ParseDecimalError; + + fn from_str(s: &str) -> Result { + // Strip the sign (-0 would parse to 0 and break our output). + let unsigned_s = s.strip_prefix('-').unwrap_or(s); + + // Parse the unsigned representation. + let (integer_s, fractional_s) = unsigned_s + .split_once('.') + .ok_or(ParseDecimalError::MissingDecimalPoint)?; + let integer = I::from_str(integer_s)?; + let fractional = I::from_str(fractional_s)?; + + let scaled_integer = integer + .checked_mul(&I::SCALING_FACTOR) + .ok_or(ParseDecimalError::Overflow(integer, fractional))?; + + let fractional_s_len = fractional_s.len(); + let fractional = match fractional_s_len.cmp(&(D as usize)) { + Ordering::Equal => fractional, + Ordering::Less => { + // `fractional_s_len` guaranteed to be less than D. + #[allow(clippy::arithmetic_side_effects)] + let shortfall = D as usize - fractional_s_len; + + // TODO: Remove the `checked_mul` in favor of ensuring `D` cannot overflow. + fractional.checked_mul(&I::pow(I::TEN, shortfall)).unwrap() + } + Ordering::Greater => return Err(ParseDecimalError::PrecisionLoss(fractional_s.len())), + }; + let unsigned = scaled_integer + .checked_add(&fractional) + .ok_or(ParseDecimalError::Overflow(integer, fractional))?; + + // Use two's complement to convert to the signed representation. + Ok(match unsigned_s.len() == s.len() { + true => Decimal(unsigned), + false => { + debug_assert_eq!(unsigned_s.len().checked_add(1).unwrap(), s.len()); + + Decimal((!unsigned).wrapping_add(&I::ONE)) + } + }) + } +} + +#[derive(Debug, PartialEq, Eq, Error)] +pub enum ParseDecimalError +where + I: Display, +{ + #[error("Missing decimal point")] + MissingDecimalPoint, + #[error("Resultant decimal overflowed; integer={0}; fractional={1}")] + Overflow(I, I), + #[error("Failed to parse integer; err={0}")] + ParseInt(#[from] ParseIntError), + #[error("Could not parse without precision loss; decimals={0}")] + PrecisionLoss(usize), +} + +#[cfg(test)] +mod tests { + use expect_test::expect; + use proptest::prelude::Arbitrary; + use proptest::proptest; + use proptest::test_runner::TestRunner; + + use super::*; + use crate::{Int64_9, Uint64_9}; + + #[test] + fn uint64_9_to_string() { + assert_eq!(Uint64_9::ONE.to_string(), "1.000000000"); + assert_eq!(Uint64_9::from_scaled(123, 9).to_string(), "0.000000123"); + assert_eq!((Uint64_9::ONE + Uint64_9::from_scaled(123, 9)).to_string(), "1.000000123"); + } + + #[test] + fn uint64_9_from_str() { + assert_eq!("".parse::(), Err(ParseDecimalError::MissingDecimalPoint)); + expect![[r#" + Err( + ParseInt( + ParseIntError { + kind: Empty, + }, + ), + ) + "#]] + .assert_debug_eq(&"1.".parse::()); + assert_eq!("1.0".parse::(), Ok(Uint64_9::ONE)); + assert_eq!("0.1".parse::(), Ok(Decimal(10u64.pow(8)))); + assert_eq!("0.123456789".parse::(), Ok(Decimal(123456789))); + assert_eq!("0.012345678".parse::(), Ok(Decimal(12345678))); + assert_eq!("0.000000001".parse::(), Ok(Decimal(1))); + + assert_eq!("0.0000000001".parse::(), Err(ParseDecimalError::PrecisionLoss(10))); + assert_eq!( + format!("{}.0", u64::MAX).parse::(), + Err(ParseDecimalError::Overflow(u64::MAX, 0)) + ); + assert_eq!( + format!("{}.0", u64::MAX / Uint64_9::SCALING_FACTOR).parse::(), + Ok(Decimal(u64::MAX / Uint64_9::SCALING_FACTOR * Uint64_9::SCALING_FACTOR)) + ); + assert_eq!(format!("18446744073.709551615").parse::(), Ok(Decimal::max()),); + assert_eq!( + format!("18446744073.709551616").parse::(), + Err(ParseDecimalError::Overflow(18446744073, 709551616)), + ); + } + + #[test] + fn int64_9_to_string() { + assert_eq!(Int64_9::ONE.to_string(), "1.000000000"); + assert_eq!(Int64_9::from_scaled(123, 9).to_string(), "0.000000123"); + assert_eq!((Int64_9::ONE + Int64_9::from_scaled(123, 9)).to_string(), "1.000000123"); + assert_eq!((-Int64_9::ONE).to_string(), "-1.000000000"); + assert_eq!((-Int64_9::from_scaled(123, 9)).to_string(), "-0.000000123"); + assert_eq!((-Int64_9::ONE + -Int64_9::from_scaled(123, 9)).to_string(), "-1.000000123"); + } + + #[test] + fn int64_9_from_str() { + assert_eq!("".parse::(), Err(ParseDecimalError::MissingDecimalPoint)); + expect![[r#" + Err( + ParseInt( + ParseIntError { + kind: Empty, + }, + ), + ) + "#]] + .assert_debug_eq(&"1.".parse::()); + assert_eq!("1.0".parse::(), Ok(Int64_9::ONE)); + assert_eq!("0.1".parse::(), Ok(Decimal(10i64.pow(8)))); + assert_eq!("0.123456789".parse::(), Ok(Decimal(123456789))); + assert_eq!("0.012345678".parse::(), Ok(Decimal(12345678))); + assert_eq!("0.000000001".parse::(), Ok(Decimal(1))); + assert_eq!("0.0000000001".parse::(), Err(ParseDecimalError::PrecisionLoss(10))); + assert_eq!("-1.0".parse::(), Ok(-Int64_9::ONE)); + assert_eq!("-0.1".parse::(), Ok(-Decimal(10i64.pow(8)))); + assert_eq!("-0.123456789".parse::(), Ok(-Decimal(123456789))); + assert_eq!("-0.012345678".parse::(), Ok(-Decimal(12345678))); + assert_eq!("-0.000000001".parse::(), Ok(-Decimal(1))); + assert_eq!("-0.0000000001".parse::(), Err(ParseDecimalError::PrecisionLoss(10))); + } + + // TODO: Round trip fuzz test does not cover strings with precision greater/less + // than target precision. + + #[test] + fn uint64_9_round_trip() { + decimal_round_trip::<9, u64>(); + } + + #[test] + fn int64_9_round_trip() { + decimal_round_trip::<9, i64>(); + } + + #[test] + fn uint128_18_round_trip() { + decimal_round_trip::<9, u64>(); + } + + #[test] + fn int128_18_round_trip() { + decimal_round_trip::<9, i64>(); + } + + fn decimal_round_trip() + where + I: Integer + Arbitrary, + { + let mut runner = TestRunner::default(); + let input = Decimal::arbitrary(); + + runner + .run(&input, |decimal: Decimal| { + let round_trip = decimal.to_string().parse().unwrap(); + + assert_eq!(decimal, round_trip); + + Ok(()) + }) + .unwrap(); + } + + #[test] + fn uint64_9_parse_no_panic() { + decimal_parse_no_panic::<9, u64>(); + } + + #[test] + fn int64_9_parse_no_panic() { + decimal_parse_no_panic::<9, i64>(); + } + + #[test] + fn uint128_18_parse_no_panic() { + decimal_parse_no_panic::<9, u64>(); + } + + #[test] + fn int128_18_parse_no_panic() { + decimal_parse_no_panic::<9, i64>(); + } + + fn decimal_parse_no_panic() + where + I: Integer, + { + proptest!(|(decimal_s: String)| { + let _ = decimal_s.parse::>(); + }); + } + + #[test] + fn uint64_9_parse_numeric_no_panic() { + decimal_parse_numeric_no_panic::<9, u64>(); + } + + #[test] + fn int64_9_parse_numeric_no_panic() { + decimal_parse_numeric_no_panic::<9, i64>(); + } + + #[test] + fn uint128_18_parse_numeric_no_panic() { + decimal_parse_numeric_no_panic::<9, u64>(); + } + + #[test] + fn int128_18_parse_numeric_no_panic() { + decimal_parse_numeric_no_panic::<9, i64>(); + } + + fn decimal_parse_numeric_no_panic() + where + I: Integer, + { + proptest!(|(decimal_s in "[0-9]{0,24}\\.[0-9]{0,24}")| { + let _ = decimal_s.parse::>(); + }); + } +} diff --git a/src/foreign_traits/arbitrary.rs b/src/foreign_traits/arbitrary.rs new file mode 100644 index 0000000..3dc20ff --- /dev/null +++ b/src/foreign_traits/arbitrary.rs @@ -0,0 +1,22 @@ +use std::fmt::Debug; + +use proptest::arbitrary::Mapped; +use proptest::prelude::{any, Arbitrary, Strategy}; + +use crate::{Decimal, Integer}; + +impl Arbitrary for Decimal +where + I: Integer + Arbitrary + Debug, +{ + type Parameters = (); + type Strategy = Mapped; + + fn arbitrary() -> Self::Strategy { + Self::arbitrary_with(()) + } + + fn arbitrary_with(_: Self::Parameters) -> Self::Strategy { + any::().prop_map(|integer| Decimal(integer)) + } +} diff --git a/src/foreign_traits/mod.rs b/src/foreign_traits/mod.rs new file mode 100644 index 0000000..fc14df3 --- /dev/null +++ b/src/foreign_traits/mod.rs @@ -0,0 +1,2 @@ +#[cfg(test)] +mod arbitrary; diff --git a/src/full_mul_div.rs b/src/full_mul_div.rs new file mode 100644 index 0000000..527d3d1 --- /dev/null +++ b/src/full_mul_div.rs @@ -0,0 +1,137 @@ +use ruint::aliases::U256; +use ruint::Uint; + +pub trait FullMulDiv { + /// Implements `a * b / c` with full width on the intermediate `a * b` + /// state. + fn full_mul_div(self, rhs: Self, div: Self) -> Self; +} + +macro_rules! impl_primitive { + ($primary:ty, $intermediate:ty) => { + impl FullMulDiv for $primary { + fn full_mul_div(self, rhs: Self, div: Self) -> Self { + let numer = (<$intermediate>::from(self)) + .checked_mul(<$intermediate>::from(rhs)) + .unwrap(); + let denom = <$intermediate>::from(div); + let out = numer.checked_div(denom).unwrap(); + + out.try_into().unwrap() + } + } + }; +} + +impl_primitive!(u8, u16); +impl_primitive!(i8, i16); +impl_primitive!(u16, u32); +impl_primitive!(i16, i32); +impl_primitive!(u32, u64); +impl_primitive!(i32, i64); +impl_primitive!(u64, u128); +impl_primitive!(i64, i128); + +impl FullMulDiv for u128 { + fn full_mul_div(self, rhs: Self, div: Self) -> Self { + let out: U256 = Uint::from(self) + .checked_mul(Uint::from(rhs)) + .unwrap() + .checked_div(Uint::from(div)) + .unwrap(); + + out.try_into().unwrap() + } +} + +impl FullMulDiv for i128 { + fn full_mul_div(self, rhs: Self, div: Self) -> Self { + // If we can compute the output using only an i128, then we should. + if let Some(out) = self + .checked_mul(rhs) + // NB: Panic early on division by 0. + .map(|numer| numer.checked_div(div).unwrap()) + { + return out; + } + + // Determine the sign of the output. Signum returns -1, 0, +1, therefore + // overflow is not possible. Additionally, if `self` or `rhs` are zero then we + // will have already returned. If `div` is zero then our next checked_div will + // catch this (and panic). + #[allow(clippy::arithmetic_side_effects)] + let sign = self.signum() * rhs.signum() * div.signum(); + + // Get the unsigned u256 representation of the integer (we'll later recover the + // signed representation using two's complement). + let this = U256::from(self.unsigned_abs()); + let rhs = U256::from(rhs.unsigned_abs()); + let div = U256::from(div.unsigned_abs()); + + // Compute the unsigned output. + let unsigned = this.checked_mul(rhs).unwrap().checked_div(div).unwrap(); + + // Convert back to the signed output. + match sign { + 1 => i128::try_from(unsigned).unwrap(), + -1 => { + // Take two's complement (!unsigned + 1). + // https://en.wikipedia.org/wiki/Two%27s_complement. + let unsigned = u128::try_from(unsigned).unwrap(); + let twos_complement = (!unsigned).overflowing_add(1).0; + + i128::from_le_bytes(twos_complement.to_le_bytes()) + } + _ => unreachable!(), + } + } +} + +// TODO: Fuzz test u128 & i128 full mul div implementations against a reference +// implementation. + +#[cfg(test)] +mod tests { + use malachite::Integer; + use proptest::prelude::*; + + use super::*; + + #[test] + fn u128_full_mul_div() { + proptest!(|(a: u128, b: u128, div: u128)| { + if div == 0 { + return Ok(()); + } + + // Compute reference value. + let reference = Integer::from(a) * Integer::from(b) / Integer::from(div); + println!("{reference}"); + + // If the output fits in a u128 then ours should match. + match u128::try_from(&reference) { + Ok(reference) => assert_eq!(u128::full_mul_div(a, b, div), reference), + Err(_) => {} + }; + }); + } + + #[test] + fn i128_full_mul_div() { + proptest!(|(a: i128, b: i128, div: i128)| { + if div == 0 { + return Ok(()); + } + + // Compute reference value. + let reference = Integer::from(a) * Integer::from(b) / Integer::from(div); + println!("{reference}"); + + // If the output fits in an i128 then ours should match. + match i128::try_from(&reference) { + Ok(reference) => assert_eq!(i128::full_mul_div(a, b, div), reference), + Err(_) => {} + }; + }); + } +} diff --git a/src/integer.rs b/src/integer.rs new file mode 100644 index 0000000..24a0c6e --- /dev/null +++ b/src/integer.rs @@ -0,0 +1,72 @@ +use std::fmt::Display; +use std::num::ParseIntError; +use std::ops::{Not, Shr}; +use std::str::FromStr; + +use num_traits::{ + Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedSub, ConstOne, + ConstZero, One, Pow, WrappingAdd, +}; + +use crate::cheats::Cheats; +use crate::full_mul_div::FullMulDiv; + +pub trait Primitive: + // `num-traits` + ConstZero + + ConstOne + + One + + Bounded + // `std` + + CheckedAdd + + WrappingAdd + + CheckedSub + + CheckedMul + + CheckedDiv + + CheckedRem + + Not + + Pow + + Shr + + Clone + + Copy + + PartialEq + + Eq + + PartialOrd + + Ord + + Display + + FromStr +{ +} + +impl Primitive for T where + T: ConstZero + + ConstOne + + One + + Bounded + + CheckedAdd + + WrappingAdd + + CheckedSub + + CheckedMul + + CheckedDiv + + CheckedRem + + Not + + Pow + + Shr + + Clone + + Copy + + PartialEq + + Eq + + PartialOrd + + Ord + + Display + + FromStr +{ +} + +pub trait Integer: Cheats + FullMulDiv + Primitive {} + +impl Integer for I where I: Cheats + FullMulDiv + Primitive {} + +pub trait SignedInteger: Integer + CheckedNeg {} + +impl SignedInteger for I where I: Integer + CheckedNeg {} diff --git a/src/lib.rs b/src/lib.rs index e225daf..9b1ddac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,20 @@ -mod const_traits; +/// Some balanced [`Decimal`] variants. +mod aliases; +/// Stuff that should be done generically if I had more time. +mod cheats; +/// Casts between cost-decimals. +mod conversion; +/// Core decimal type & operations. mod decimal; +/// [`Display`] and [`FromStr`] implementation. +mod display; +/// Implementations of foreign traits. +mod foreign_traits; +/// Full multiplication implementations for underlying integers. +mod full_mul_div; +/// Trait definition for underlying integer. +mod integer; +pub use aliases::*; pub use decimal::*; +pub use integer::*;