diff --git a/Cargo.lock b/Cargo.lock index 8cf8b42bd41d6..82a5ab2229879 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -92,6 +92,11 @@ dependencies = [ "xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "arbitrary" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "arc-swap" version = "0.3.11" @@ -890,6 +895,17 @@ dependencies = [ "static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "fixed-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-hex 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "fixedbitset" version = "0.1.9" @@ -1263,6 +1279,16 @@ dependencies = [ "hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "honggfuzz" +version = "0.5.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "http" version = "0.1.18" @@ -2165,6 +2191,15 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "memoffset" version = "0.5.1" @@ -2995,6 +3030,16 @@ dependencies = [ "uint 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "primitive-types" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fixed-hash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "impl-codec 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "uint 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "proc-macro-crate" version = "0.1.4" @@ -3812,6 +3857,30 @@ dependencies = [ "trybuild 1.0.17 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "sr-arithmetic" +version = "2.0.0" +dependencies = [ + "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-std 2.0.0", +] + +[[package]] +name = "sr-arithmetic-fuzzer" +version = "2.0.0" +dependencies = [ + "honggfuzz 0.5.45 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-arithmetic 2.0.0", +] + [[package]] name = "sr-io" version = "2.0.0" @@ -3833,15 +3902,13 @@ name = "sr-primitives" version = "2.0.0" dependencies = [ "impl-trait-for-tuples 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "integer-sqrt 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "paste 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", - "primitive-types 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", + "sr-arithmetic 2.0.0", "sr-io 2.0.0", "sr-std 2.0.0", "substrate-application-crypto 2.0.0", @@ -6696,6 +6763,7 @@ dependencies = [ "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" "checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" +"checksum arbitrary 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" "checksum arc-swap 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "bc4662175ead9cd84451d5c35070517777949a2ed84551764129cedb88384841" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" @@ -6788,6 +6856,7 @@ dependencies = [ "checksum fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1ee15a7050e5580b3712877157068ea713b245b080ff302ae2ca973cfcd9baa" "checksum finality-grandpa 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9681c1f75941ea47584573dd2bc10558b2067d460612945887e00744e43393be" "checksum fixed-hash 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "516877b7b9a1cc2d0293cbce23cd6203f0edbfd4090e6ca4489fecb5aa73050e" +"checksum fixed-hash 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6357b15872f8126e4ea7cf79d579473f132ccd2de239494ad1bf4aa892faea68" "checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" "checksum flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ad3c5233c9a940c8719031b423d7e6c16af66e031cb0420b0896f5245bf181d3" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" @@ -6832,6 +6901,7 @@ dependencies = [ "checksum hmac 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5dcb5e64cda4c23119ab41ba960d1e170a774c8e4b9d9e6a9bc18aabf5e59695" "checksum hmac-drbg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4fe727d41d2eec0a6574d887914347e5ff96a3b87177817e2a9820c5c87fecc2" "checksum hmac-drbg 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e570451493f10f6581b48cdd530413b63ea9e780f544bfd3bdcaa0d89d1a7b" +"checksum honggfuzz 0.5.45 (registry+https://github.com/rust-lang/crates.io-index)" = "24c27b4aa3049d6d10d8e33d52c9d03ca9aec18f8a449b246f8c4a5b0c10fb34" "checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" "checksum http-body 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6741c859c1b2463a423a1dbce98d418e6c3c3fc720fb0d45528657320920292d" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" @@ -6908,6 +6978,7 @@ dependencies = [ "checksum malloc_size_of_derive 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "35adee9ed962cf7d07d62cb58bc45029f3227f5b5b86246caa8632f06c187bc3" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" +"checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" "checksum memory-db 0.15.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ef49315991403ba5fa225a70399df5e115f57b274cb0b1b4bcd6e734fa5bd783" "checksum memory_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d96e3f3c0b6325d8ccd83c33b28acb183edcb6c67938ba104ec546854b0882" @@ -6970,6 +7041,7 @@ dependencies = [ "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" "checksum primitive-types 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "83ef7b3b965c0eadcb6838f34f827e1dfb2939bdd5ebd43f9647e009b12b0371" +"checksum primitive-types 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97b5a08dda18910f056e5c2060c034e77cab18e0bd7d895e44f03207af4c71d5" "checksum proc-macro-crate 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" "checksum proc-macro-error 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aeccfe4d5d8ea175d5f0e4a2ad0637e0f4121d63bd99d356fb1f39ab2e7c6097" "checksum proc-macro-hack 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "114cdf1f426eb7f550f01af5f53a33c0946156f6814aec939b3bd77e844f9a9d" diff --git a/Cargo.toml b/Cargo.toml index 5b012be0e98b3..9d01fabc1e373 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,8 @@ members = [ "core/service/test", "core/session", "core/sr-api-macros", + "core/sr-arithmetic", + "core/sr-arithmetic/fuzzer", "core/sr-io", "core/sr-primitives", "core/sr-staking-primitives", diff --git a/core/sr-arithmetic/Cargo.toml b/core/sr-arithmetic/Cargo.toml new file mode 100644 index 0000000000000..d98404db9492e --- /dev/null +++ b/core/sr-arithmetic/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "sr-arithmetic" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +num-traits = { version = "0.2.8", default-features = false } +serde = { version = "1.0.101", optional = true, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } +rstd = { package = "sr-std", path = "../sr-std", default-features = false } +integer-sqrt = "0.1.2" + +[dev-dependencies] +primitive-types = "0.6.0" +rand = "0.7.2" + +[features] +bench = [] +default = ["std"] +std = [ + "serde", + "num-traits/std", + "rstd/std", + "codec/std", +] diff --git a/core/sr-arithmetic/fuzzer/.gitignore b/core/sr-arithmetic/fuzzer/.gitignore new file mode 100644 index 0000000000000..3ebcb104d4a50 --- /dev/null +++ b/core/sr-arithmetic/fuzzer/.gitignore @@ -0,0 +1,2 @@ +hfuzz_target +hfuzz_workspace diff --git a/core/sr-arithmetic/fuzzer/Cargo.toml b/core/sr-arithmetic/fuzzer/Cargo.toml new file mode 100644 index 0000000000000..8838e60436f4a --- /dev/null +++ b/core/sr-arithmetic/fuzzer/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "sr-arithmetic-fuzzer" +version = "2.0.0" +authors = ["Parity Technologies "] +edition = "2018" + +[dependencies] +sr-arithmetic = { path = ".." } +honggfuzz = "0.5" +primitive-types = "0.6" +num-bigint = "0.2" +num-traits = "0.2" + +[[bin]] +name = "biguint" +path = "src/biguint.rs" + +[[bin]] +name = "rational128" +path = "src/rational128.rs" diff --git a/core/sr-arithmetic/fuzzer/src/biguint.rs b/core/sr-arithmetic/fuzzer/src/biguint.rs new file mode 100644 index 0000000000000..bd270a97cac71 --- /dev/null +++ b/core/sr-arithmetic/fuzzer/src/biguint.rs @@ -0,0 +1,181 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! # Running +//! Running this fuzzer can be done with `cargo hfuzz run biguint`. `honggfuzz` CLI options can +//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads. +//! +//! # Debugging a panic +//! Once a panic is found, it can be debugged with +//! `cargo hfuzz run-debug biguint hfuzz_workspace/biguint/*.fuzz`. +//! +//! # More infomation +//! More information about `honggfuzz` can be found +//! [here](https://docs.rs/honggfuzz/). + +use honggfuzz::fuzz; +use sr_arithmetic::biguint::{BigUint, Single}; +use std::convert::TryFrom; + +fn main() { + loop { + fuzz!(|data: (Vec, Vec, bool)| { + let (mut digits_u, mut digits_v, return_remainder) = data; + + let mut u = BigUint::from_limbs(&digits_u); + let mut v = BigUint::from_limbs(&digits_v); + + u.lstrip(); + v.lstrip(); + + let ue = u128::try_from(u.clone()); + let ve = u128::try_from(v.clone()); + + digits_u.reverse(); + digits_v.reverse(); + + let num_u = num_bigint::BigUint::new(digits_u.clone()); + let num_v = num_bigint::BigUint::new(digits_v.clone()); + + if check_digit_lengths(&u, &v, 4) { + assert_eq!(u.cmp(&v), ue.cmp(&ve)); + assert_eq!(u.eq(&v), ue.eq(&ve)); + } + + if check_digit_lengths(&u, &v, 3) { + let expected = ue.unwrap() + ve.unwrap(); + let t = u.clone().add(&v); + assert_eq!( + u128::try_from(t.clone()).unwrap(), expected, + "{:?} + {:?} ===> {:?} != {:?}", u, v, t, expected, + ); + } + + if check_digit_lengths(&u, &v, 4) { + let expected = ue.unwrap().checked_sub(ve.unwrap()); + let t = u.clone().sub(&v); + if expected.is_none() { + assert!(t.is_err()) + } else { + let t = t.unwrap(); + let expected = expected.unwrap(); + assert_eq!( + u128::try_from(t.clone()).unwrap(), expected, + "{:?} - {:?} ===> {:?} != {:?}", u, v, t, expected, + ); + } + } + + if check_digit_lengths(&u, &v, 2) { + let expected = ue.unwrap() * ve.unwrap(); + let t = u.clone().mul(&v); + assert_eq!( + u128::try_from(t.clone()).unwrap(), expected, + "{:?} * {:?} ===> {:?} != {:?}", u, v, t, expected, + ); + } + + if check_digit_lengths(&u, &v, 4) { + let (ue, ve) = (ue.unwrap(), ve.unwrap()); + if ve == 0 { + return; + } + let (q, r) = (ue / ve, ue % ve); + if let Some((qq, rr)) = u.clone().div(&v, true) { + assert_eq!( + u128::try_from(qq.clone()).unwrap(), q, + "{:?} / {:?} ===> {:?} != {:?}", u, v, qq, q, + ); + assert_eq!( + u128::try_from(rr.clone()).unwrap(), r, + "{:?} % {:?} ===> {:?} != {:?}", u, v, rr, r, + ); + } else if v.len() == 1 { + let qq = u.clone().div_unit(ve as Single); + assert_eq!( + u128::try_from(qq.clone()).unwrap(), q, + "[single] {:?} / {:?} ===> {:?} != {:?}", u, v, qq, q, + ); + } else if v.msb() != 0 && u.msb() != 0 && u.len() > v.len() { + panic!("div returned none for an unexpected reason"); + } + } + + // Test against num_bigint + + // Equality + + assert_eq!(u.cmp(&v), num_u.cmp(&num_v)); + + // Addition + + let w = u.clone().add(&v); + let num_w = num_u.clone() + &num_v; + + assert_biguints_eq(&w, &num_w); + + // Subtraction + + if let Ok(w) = u.clone().sub(&v) { + let num_w = num_u.clone() - &num_v; + + assert_biguints_eq(&w, &num_w); + } + + // Multiplication + + let w = u.clone().mul(&v); + let num_w = num_u.clone() * &num_v; + + assert_biguints_eq(&w, &num_w); + + // Division + + if v.len() == 1 && v.get(0) != 0 { + let w = u.clone().div_unit(v.get(0)); + let num_w = num_u.clone() / &num_v; + assert_biguints_eq(&w, &num_w); + } else if u.len() > v.len() && v.len() > 0 { + let num_remainder = num_u.clone() % num_v.clone(); + + let (w, remainder) = u.clone().div(&v, return_remainder).unwrap(); + let num_w = num_u.clone() / &num_v; + + assert_biguints_eq(&w, &num_w); + + if return_remainder { + assert_biguints_eq(&remainder, &num_remainder); + } + } + }); + } +} + +fn check_digit_lengths(u: &BigUint, v: &BigUint, max_limbs: usize) -> bool { + 1 <= u.len() && u.len() <= max_limbs && 1 <= v.len() && v.len() <= max_limbs +} + +fn assert_biguints_eq(a: &BigUint, b: &num_bigint::BigUint) { + let mut a = a.clone(); + a.lstrip(); + + // `num_bigint::BigUint` doesn't expose it's internals, so we need to convert into that to + // compare. + let limbs = (0 .. a.len()).map(|i| a.get(i)).collect(); + let num_a = num_bigint::BigUint::new(limbs); + + assert!(&num_a == b, "\narithmetic: {:?}\nnum-bigint: {:?}", a, b); +} diff --git a/core/sr-arithmetic/fuzzer/src/rational128.rs b/core/sr-arithmetic/fuzzer/src/rational128.rs new file mode 100644 index 0000000000000..b2a00d754527f --- /dev/null +++ b/core/sr-arithmetic/fuzzer/src/rational128.rs @@ -0,0 +1,77 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! # Running +//! Running this fuzzer can be done with `cargo hfuzz run rational128`. `honggfuzz` CLI options can +//! be used by setting `HFUZZ_RUN_ARGS`, such as `-n 4` to use 4 threads. +//! +//! # Debugging a panic +//! Once a panic is found, it can be debugged with +//! `cargo hfuzz run-debug rational128 hfuzz_workspace/rational128/*.fuzz`. +//! +//! # More infomation +//! More information about `honggfuzz` can be found +//! [here](https://docs.rs/honggfuzz/). + +use honggfuzz::fuzz; +use sr_arithmetic::{helpers_128bit::multiply_by_rational, traits::Zero}; + +fn main() { + loop { + fuzz!(|data: ([u8; 16], [u8; 16], [u8; 16])| { + let (a_bytes, b_bytes, c_bytes) = data; + let (a, b, c) = ( + u128::from_be_bytes(a_bytes), + u128::from_be_bytes(b_bytes), + u128::from_be_bytes(c_bytes), + ); + + println!("++ Equation: {} * {} / {}", a, b, c); + + // The point of this fuzzing is to make sure that `multiply_by_rational` is 100% + // accurate as long as the value fits in a u128. + if let Ok(result) = multiply_by_rational(a, b, c) { + let truth = mul_div(a, b, c); + + if result != truth && result != truth + 1 { + println!("++ Expected {}", truth); + println!("+++++++ Got {}", result); + panic!(); + } + } + }) + } +} + +fn mul_div(a: u128, b: u128, c: u128) -> u128 { + use primitive_types::U256; + if a.is_zero() { + return Zero::zero(); + } + let c = c.max(1); + + // e for extended + let ae: U256 = a.into(); + let be: U256 = b.into(); + let ce: U256 = c.into(); + + let r = ae * be / ce; + if r > u128::max_value().into() { + a + } else { + r.as_u128() + } +} diff --git a/core/sr-arithmetic/src/biguint.rs b/core/sr-arithmetic/src/biguint.rs new file mode 100644 index 0000000000000..d90ad4862ba10 --- /dev/null +++ b/core/sr-arithmetic/src/biguint.rs @@ -0,0 +1,809 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Infinite precision unsigned integer for substrate runtime. + +use num_traits::Zero; +use rstd::{cmp::Ordering, ops, prelude::*, cell::RefCell, convert::TryFrom}; + +// A sensible value for this would be half of the dword size of the host machine. Since the +// runtime is compiled to 32bit webassembly, using 32 and 64 for single and double respectively +// should yield the most performance. TODO #3745 we could benchmark this and verify. +/// Representation of a single limb. +pub type Single = u32; +/// Representation of two limbs. +pub type Double = u64; +/// Difference in the number of bits of [`Single`] and [`Double`]. +const SHIFT: usize = 32; +/// short form of _Base_. Analogous to the value 10 in base-10 decimal numbers. +const B: Double = Single::max_value() as Double + 1; + +/// Splits a [`Double`] limb number into a tuple of two [`Single`] limb numbers. +pub fn split(a: Double) -> (Single, Single) { + let al = a as Single; + let ah = (a >> SHIFT) as Single; + (ah, al) +} + +/// Assumed as a given primitive. +/// +/// Multiplication of two singles, which at most yields 1 double. +pub fn mul_single(a: Single, b: Single) -> Double { + let a: Double = a.into(); + let b: Double = b.into(); + a * b +} + +/// Assumed as a given primitive. +/// +/// Addition of two singles, which at most takes a single limb of result and a carry, +/// returned as a tuple respectively. +pub fn add_single(a: Single, b: Single) -> (Single, Single) { + let a: Double = a.into(); + let b: Double = b.into(); + let q = a + b; + let (carry, r) = split(q); + (r, carry) +} + +/// Assumed as a given primitive. +/// +/// Division of double by a single limb. Always returns a double limb of quotient and a single +/// limb of remainder. +fn div_single(a: Double, b: Single) -> (Double, Single) { + let b: Double = b.into(); + let q = a / b; + let r = a % b; + // both conversions are trivially safe. + (q, r as Single) +} + +/// Simple wrapper around an infinitely large integer, represented as limbs of [`Single`]. +#[derive(Clone, Default)] +pub struct BigUint { + /// digits (limbs) of this number (sorted as msb -> lsd). + pub(crate) digits: Vec, +} + +impl BigUint { + /// Create a new instance with `size` limbs. This prevents any number with zero limbs to be + /// created. + /// + /// The behavior of the type is undefined with zero limbs. + pub fn with_capacity(size: usize) -> Self { + Self { digits: vec![0; size.max(1)] } + } + + /// Raw constructor from custom limbs. If `limbs` is empty, `Zero::zero()` implementation is + /// used. + pub fn from_limbs(limbs: &[Single]) -> Self { + if !limbs.is_empty() { + Self { digits: limbs.to_vec() } + } else { + Zero::zero() + } + } + + /// Number of limbs. + pub fn len(&self) -> usize { self.digits.len() } + + /// A naive getter for limb at `index`. Note that the order is lsb -> msb. + /// + /// #### Panics + /// + /// This panics if index is out of range. + pub fn get(&self, index: usize) -> Single { + self.digits[self.len() - 1 - index] + } + + /// A naive getter for limb at `index`. Note that the order is lsb -> msb. + pub fn checked_get(&self, index: usize) -> Option { + let i = self.len().checked_sub(1)?; + let j = i.checked_sub(index)?; + self.digits.get(j).cloned() + } + + /// A naive setter for limb at `index`. Note that the order is lsb -> msb. + /// + /// #### Panics + /// + /// This panics if index is out of range. + pub fn set(&mut self, index: usize, value: Single) { + let len = self.digits.len(); + self.digits[len - 1 - index] = value; + } + + /// returns the least significant limb of the number. + /// + /// #### Panics + /// + /// While the constructor of the type prevents this, this can panic if `self` has no digits. + pub fn lsb(&self) -> Single { + self.digits[self.len() - 1] + } + + /// returns the most significant limb of the number. + /// + /// #### Panics + /// + /// While the constructor of the type prevents this, this can panic if `self` has no digits. + pub fn msb(&self) -> Single { + self.digits[0] + } + + /// Strips zeros from the left side (the most significant limbs) of `self`, if any. + pub fn lstrip(&mut self) { + // by definition, a big-int number should never have leading zero limbs. This function + // has the ability to cause this. There is nothing to do if the number already has 1 + // limb only. call it a day and return. + if self.len().is_zero() { return; } + let index = self.digits.iter().position(|&elem| elem != 0).unwrap_or(0); + + if index > 0 { + self.digits = self.digits[index..].to_vec() + } + } + + /// Zero-pad `self` from left to reach `size` limbs. Will not make any difference if `self` + /// is already bigger than `size` limbs. + pub fn lpad(&mut self, size: usize) { + let n = self.len(); + if n >= size { return; } + let pad = size - n; + let mut new_digits = (0..pad).map(|_| 0).collect::>(); + new_digits.extend(self.digits.iter()); + self.digits = new_digits; + } + + /// Adds `self` with `other`. self and other do not have to have any particular size. Given + /// that the `n = max{size(self), size(other)}`, it will produce a number with `n + 1` + /// limbs. + /// + /// This function does not strip the output and returns the original allocated `n + 1` + /// limbs. The caller may strip the output if desired. + /// + /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. + pub fn add(self, other: &Self) -> Self { + let n = self.len().max(other.len()); + let mut k: Double = 0; + let mut w = Self::with_capacity(n + 1); + + for j in 0..n { + let u = Double::from(self.checked_get(j).unwrap_or(0)); + let v = Double::from(other.checked_get(j).unwrap_or(0)); + let s = u + v + k; + w.set(j, (s % B) as Single); + k = s / B; + } + // k is always 0 or 1. + w.set(n, k as Single); + w + } + + /// Subtracts `other` from `self`. self and other do not have to have any particular size. + /// Given that the `n = max{size(self), size(other)}`, it will produce a number of size `n`. + /// + /// If `other` is bigger than `self`, `Err(B - borrow)` is returned. + /// + /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. + pub fn sub(self, other: &Self) -> Result { + let n = self.len().max(other.len()); + let mut k = 0; + let mut w = Self::with_capacity(n); + for j in 0..n { + let s = { + let u = Double::from(self.checked_get(j).unwrap_or(0)); + let v = Double::from(other.checked_get(j).unwrap_or(0)); + let mut needs_borrow = false; + let mut t = 0; + + if let Some(v) = u.checked_sub(v) { + if let Some(v2) = v.checked_sub(k) { + t = v2 % B; + k = 0; + } else { + needs_borrow = true; + } + } else { + needs_borrow = true; + } + if needs_borrow { + t = u + B - v - k; + k = 1; + } + t + }; + // PROOF: t either comes from `v2 % B`, or from `u + B - v - k`. The former is + // trivial. The latter will not overflow this branch will only happen if the sum of + // `u - v - k` part has been negative, hence `u + B - v - k < b`. + w.set(j, s as Single); + } + + if k.is_zero() { + Ok(w) + } else { + Err(w) + } + } + + /// Multiplies n-limb number `self` with m-limb number `other`. + /// + /// The resulting number will always have `n + m` limbs. + /// + /// This function does not strip the output and returns the original allocated `n + m` + /// limbs. The caller may strip the output if desired. + /// + /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. + pub fn mul(self, other: &Self) -> Self { + let n = self.len(); + let m = other.len(); + let mut w = Self::with_capacity(m + n); + + for j in 0..n { + if self.get(j) == 0 { + // Note: `with_capacity` allocates with 0. Explicitly set j + m to zero if + // otherwise. + continue; + } + + let mut k = 0; + for i in 0..m { + // PROOF: (B−1) × (B−1) + (B−1) + (B−1) = B^2 −1 < B^2. addition is safe. + let t = + mul_single(self.get(j), other.get(i)) + + Double::from(w.get(i + j)) + + Double::from(k); + w.set(i + j, (t % B) as Single); + // PROOF: (B^2 - 1) / B < B. conversion is safe. + k = (t / B) as Single; + } + w.set(j + m, k); + } + w + } + + /// Divides `self` by a single limb `other`. This can be used in cases where the original + /// division cannot work due to the divisor (`other`) being just one limb. + /// + /// Invariant: `other` cannot be zero. + pub fn div_unit(self, mut other: Single) -> Self { + other = other.max(1); + let n = self.len(); + let mut out = Self::with_capacity(n); + let mut r: Single = 0; + // PROOF: (B-1) * B + (B-1) still fits in double + let with_r = |x: Double, r: Single| { Double::from(r) * B + x }; + for d in (0..n).rev() { + let (q, rr) = div_single(with_r(self.get(d).into(), r), other) ; + out.set(d, q as Single); + r = rr; + } + out + } + + /// Divides an `n + m` limb self by a `n` limb `other`. The result is a `m + 1` limb + /// quotient and a `n` limb remainder, if enabled by passing `true` in `rem` argument, both + /// in the form of an option's `Ok`. + /// + /// - requires `other` to be stripped and have no leading zeros. + /// - requires `self` to be stripped and have no leading zeros. + /// - requires `other` to have at least two limbs. + /// - requires `self` to have a greater length compared to `other`. + /// + /// All arguments are examined without being stripped for the above conditions. If any of + /// the above fails, `None` is returned.` + /// + /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. + pub fn div(self, other: &Self, rem: bool) -> Option<(Self, Self)> { + if other.len() <= 1 + || other.msb() == 0 + || self.msb() == 0 + || self.len() <= other.len() + { + return None + } + let n = other.len(); + let m = self.len() - n; + + let mut q = Self::with_capacity(m + 1); + let mut r = Self::with_capacity(n); + + // PROOF: 0 <= normalizer_bits < SHIFT 0 <= normalizer < B. all conversions are + // safe. + let normalizer_bits = other.msb().leading_zeros() as Single; + let normalizer = (2 as Single).pow(normalizer_bits as u32) as Single; + + // step D1. + let mut self_norm = self.mul(&Self::from(normalizer)); + let mut other_norm = other.clone().mul(&Self::from(normalizer)); + + // defensive only; the mul implementation should always create this. + self_norm.lpad(n + m + 1); + other_norm.lstrip(); + + // step D2. + for j in (0..=m).rev() { + // step D3.0 Find an estimate of q[j], named qhat. + let (qhat, rhat) = { + // PROOF: this always fits into `Double`. In the context of Single = u8, and + // Double = u16, think of 255 * 256 + 255 which is just u16::max_value(). + let dividend = + Double::from(self_norm.get(j + n)) + * B + + Double::from(self_norm.get(j + n - 1)); + let divisor = other_norm.get(n - 1); + div_single(dividend, divisor) + }; + + // D3.1 test qhat + // replace qhat and rhat with RefCells. This helps share state with the closure + let qhat = RefCell::new(qhat); + let rhat = RefCell::new(Double::from(rhat)); + + let test = || { + // decrease qhat if it is bigger than the base (B) + let qhat_local = *qhat.borrow(); + let rhat_local = *rhat.borrow(); + let predicate_1 = qhat_local >= B; + let predicate_2 = { + let lhs = qhat_local * Double::from(other_norm.get(n - 2)); + let rhs = B * rhat_local + Double::from(self_norm.get(j + n - 2)); + lhs > rhs + }; + if predicate_1 || predicate_2 { + *qhat.borrow_mut() -= 1; + *rhat.borrow_mut() += Double::from(other_norm.get(n - 1)); + true + } else { + false + } + }; + + test(); + while (*rhat.borrow() as Double) < B { + if !test() { break; } + } + + let qhat = qhat.into_inner(); + // we don't need rhat anymore. just let it go out of scope when it does. + + // step D4 + let lhs = Self { digits: (j..=j+n).rev().map(|d| self_norm.get(d)).collect() }; + let rhs = other_norm.clone().mul(&Self::from(qhat)); + + let maybe_sub = lhs.sub(&rhs); + let mut negative = false; + let sub = match maybe_sub { + Ok(t) => t, + Err(t) => { negative = true; t } + }; + (j..=j+n).for_each(|d| { self_norm.set(d, sub.get(d - j)); }); + + // step D5 + // PROOF: the `test()` specifically decreases qhat until it is below `B`. conversion + // is safe. + q.set(j, qhat as Single); + + // step D6: add back if negative happened. + if negative { + q.set(j, q.get(j) - 1); + let u = Self { digits: (j..=j+n).rev().map(|d| self_norm.get(d)).collect() }; + let r = other_norm.clone().add(&u); + (j..=j+n).rev().for_each(|d| { self_norm.set(d, r.get(d - j)); }) + } + } + + // if requested, calculate remainder. + if rem { + // undo the normalization. + if normalizer_bits > 0 { + let s = SHIFT as u32; + let nb = normalizer_bits; + for d in 0..n-1 { + let v = self_norm.get(d) >> nb + | self_norm.get(d + 1).overflowing_shl(s - nb).0; + r.set(d, v); + } + r.set(n - 1, self_norm.get(n - 1) >> normalizer_bits); + } else { + r = self_norm; + } + } + + Some((q, r)) + } +} + +#[cfg(feature = "std")] +impl rstd::fmt::Debug for BigUint { + fn fmt(&self, f: &mut rstd::fmt::Formatter<'_>) -> rstd::fmt::Result { + write!( + f, + "BigUint {{ {:?} ({:?})}}", + self.digits, + u128::try_from(self.clone()).unwrap_or(0), + ) + } +} + +impl PartialEq for BigUint { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for BigUint {} + +impl Ord for BigUint { + fn cmp(&self, other: &Self) -> Ordering { + let lhs_first = self.digits.iter().position(|&e| e != 0); + let rhs_first = other.digits.iter().position(|&e| e != 0); + + match (lhs_first, rhs_first) { + // edge cases that should not happen. This basically means that one or both were + // zero. + (None, None) => Ordering::Equal, + (Some(_), None) => Ordering::Greater, + (None, Some(_)) => Ordering::Less, + (Some(lhs_idx), Some(rhs_idx)) => { + let lhs = &self.digits[lhs_idx..]; + let rhs = &other.digits[rhs_idx..]; + let len_cmp = lhs.len().cmp(&rhs.len()); + match len_cmp { + Ordering::Equal => lhs.cmp(rhs), + _ => len_cmp, + } + } + } + } +} + +impl PartialOrd for BigUint { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl ops::Add for BigUint { + type Output = Self; + fn add(self, rhs: Self) -> Self::Output { + self.add(&rhs) + } +} + +impl ops::Sub for BigUint { + type Output = Self; + fn sub(self, rhs: Self) -> Self::Output { + self.sub(&rhs).unwrap_or_else(|e| e) + } +} + +impl ops::Mul for BigUint { + type Output = Self; + fn mul(self, rhs: Self) -> Self::Output { + self.mul(&rhs) + } +} + +impl Zero for BigUint { + fn zero() -> Self { + Self { digits: vec![Zero::zero()] } + } + + fn is_zero(&self) -> bool { + self.digits.iter().all(|d| d.is_zero()) + } +} + +macro_rules! impl_try_from_number_for { + ($([$type:ty, $len:expr]),+) => { + $( + impl TryFrom for $type { + type Error = &'static str; + fn try_from(mut value: BigUint) -> Result<$type, Self::Error> { + value.lstrip(); + let error_message = concat!("cannot fit a number into ", stringify!($type)); + if value.len() * SHIFT > $len { + Err(error_message) + } else { + let mut acc: $type = Zero::zero(); + for (i, d) in value.digits.iter().rev().cloned().enumerate() { + let d: $type = d.into(); + acc += d << (SHIFT * i); + } + Ok(acc) + } + } + } + )* + }; +} +// can only be implemented for sizes bigger than two limb. +impl_try_from_number_for!([u128, 128], [u64, 64]); + +macro_rules! impl_from_for_smaller_than_word { + ($($type:ty),+) => { + $(impl From<$type> for BigUint { + fn from(a: $type) -> Self { + Self { digits: vec! [a.into()] } + } + })* + } +} +impl_from_for_smaller_than_word!(u8, u16, Single); + +impl From for BigUint { + fn from(a: Double) -> Self { + let (ah, al) = split(a); + Self { digits: vec![ah, al] } + } +} + +#[cfg(test)] +pub mod tests { + use super::*; + #[cfg(feature = "bench")] + use test::Bencher; + + fn with_limbs(n: usize) -> BigUint { + BigUint { digits: vec![1; n] } + } + + #[test] + fn split_works() { + let a = SHIFT / 2; + let b = SHIFT * 3 / 2; + let num: Double = 1 << a | 1 << b; + // example when `Single = u8` + // assert_eq!(num, 0b_0001_0000_0001_0000) + assert_eq!(split(num), (1 << a, 1 << a)); + } + + #[test] + fn strip_works() { + let mut a = BigUint::from_limbs(&[0, 1, 0]); + a.lstrip(); + assert_eq!(a, BigUint { digits: vec![1, 0] }); + + let mut a = BigUint::from_limbs(&[0, 0, 1]); + a.lstrip(); + assert_eq!(a, BigUint { digits: vec![1] }); + + let mut a = BigUint::from_limbs(&[0, 0]); + a.lstrip(); + assert_eq!(a, BigUint { digits: vec![0] }); + + let mut a = BigUint::from_limbs(&[0, 0, 0]); + a.lstrip(); + assert_eq!(a, BigUint { digits: vec![0] }); + } + + #[test] + fn lpad_works() { + let mut a = BigUint::from_limbs(&[0, 1, 0]); + a.lpad(2); + assert_eq!(a.digits, vec![0, 1, 0]); + + let mut a = BigUint::from_limbs(&[0, 1, 0]); + a.lpad(3); + assert_eq!(a.digits, vec![0, 1, 0]); + + let mut a = BigUint::from_limbs(&[0, 1, 0]); + a.lpad(4); + assert_eq!(a.digits, vec![0, 0, 1, 0]); + } + + #[test] + fn equality_works() { + assert_eq!( + BigUint { digits: vec![1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, + true, + ); + assert_eq!( + BigUint { digits: vec![3, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, + false, + ); + assert_eq!( + BigUint { digits: vec![0, 1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, + true, + ); + } + + #[test] + fn ordering_works() { + assert!(BigUint { digits: vec![0] } < BigUint { digits: vec![1] }); + assert!(BigUint { digits: vec![0] } == BigUint { digits: vec![0] }); + assert!(BigUint { digits: vec![] } == BigUint { digits: vec![0] }); + assert!(BigUint { digits: vec![] } == BigUint { digits: vec![] }); + assert!(BigUint { digits: vec![] } < BigUint { digits: vec![1] }); + + assert!(BigUint { digits: vec![1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }); + assert!(BigUint { digits: vec![0, 1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }); + + assert!(BigUint { digits: vec![1, 2, 4] } > BigUint { digits: vec![1, 2, 3] }); + assert!(BigUint { digits: vec![0, 1, 2, 4] } > BigUint { digits: vec![1, 2, 3] }); + assert!(BigUint { digits: vec![1, 2, 1, 0] } > BigUint { digits: vec![1, 2, 3] }); + + assert!(BigUint { digits: vec![0, 1, 2, 1] } < BigUint { digits: vec![1, 2, 3] }); + } + + #[test] + fn can_try_build_numbers_from_types() { + use rstd::convert::TryFrom; + assert_eq!(u64::try_from(with_limbs(1)).unwrap(), 1); + assert_eq!(u64::try_from(with_limbs(2)).unwrap(), u32::max_value() as u64 + 2); + assert_eq!( + u64::try_from(with_limbs(3)).unwrap_err(), + "cannot fit a number into u64", + ); + assert_eq!( + u128::try_from(with_limbs(3)).unwrap(), + u32::max_value() as u128 + u64::max_value() as u128 + 3 + ); + } + + #[test] + fn zero_works() { + assert_eq!(BigUint::zero(), BigUint { digits: vec![0] }); + assert_eq!(BigUint { digits: vec![0, 1, 0] }.is_zero(), false); + assert_eq!(BigUint { digits: vec![0, 0, 0] }.is_zero(), true); + + let a = BigUint::zero(); + let b = BigUint::zero(); + let c = a * b; + assert_eq!(c.digits, vec![0, 0]); + } + + #[test] + fn sub_negative_works() { + assert_eq!( + BigUint::from(10 as Single).sub(&BigUint::from(5 as Single)).unwrap(), + BigUint::from(5 as Single) + ); + assert_eq!( + BigUint::from(10 as Single).sub(&BigUint::from(10 as Single)).unwrap(), + BigUint::from(0 as Single) + ); + assert_eq!( + BigUint::from(10 as Single).sub(&BigUint::from(13 as Single)).unwrap_err(), + BigUint::from((B - 3) as Single), + ); + } + + #[test] + fn mul_always_appends_one_digit() { + let a = BigUint::from(10 as Single); + let b = BigUint::from(4 as Single); + assert_eq!(a.len(), 1); + assert_eq!(b.len(), 1); + + let n = a.mul(&b); + + assert_eq!(n.len(), 2); + assert_eq!(n.digits, vec![0, 40]); + } + + #[test] + fn div_conditions_work() { + let a = BigUint { digits: vec![2] }; + let b = BigUint { digits: vec![1, 2] }; + let c = BigUint { digits: vec![1, 1, 2] }; + let d = BigUint { digits: vec![0, 2] }; + let e = BigUint { digits: vec![0, 1, 1, 2] }; + + assert!(a.clone().div(&b, true).is_none()); + assert!(c.clone().div(&a, true).is_none()); + assert!(c.clone().div(&d, true).is_none()); + assert!(e.clone().div(&a, true).is_none()); + + assert!(c.clone().div(&b, true).is_some()); + } + + #[test] + fn div_unit_works() { + let a = BigUint { digits: vec![100] }; + let b = BigUint { digits: vec![1, 100] }; + + assert_eq!(a.clone().div_unit(1), a); + assert_eq!(a.clone().div_unit(0), a); + assert_eq!(a.clone().div_unit(2), BigUint::from(50 as Single)); + assert_eq!(a.clone().div_unit(7), BigUint::from(14 as Single)); + + assert_eq!(b.clone().div_unit(1), b); + assert_eq!(b.clone().div_unit(0), b); + assert_eq!(b.clone().div_unit(2), BigUint::from(((B + 100) / 2) as Single)); + assert_eq!(b.clone().div_unit(7), BigUint::from(((B + 100) / 7) as Single)); + + } + + #[cfg(feature = "bench")] + fn random_big_uint(size: usize) -> BigUint { + use rand::Rng; + let mut rng = rand::thread_rng(); + let digits = (0..size).map(|_| rng.gen_range(0, Single::max_value())).collect(); + BigUint { digits } + } + + #[cfg(feature = "bench")] + #[bench] + fn bench_addition_2_digit(bencher: &mut Bencher) { + let a = random_big_uint(2); + let b = random_big_uint(2); + bencher.iter(|| { + let _ = a.clone().add(&b); + }); + } + + #[cfg(feature = "bench")] + #[bench] + fn bench_addition_4_digit(bencher: &mut Bencher) { + let a = random_big_uint(4); + let b = random_big_uint(4); + bencher.iter(|| { + let _ = a.clone().add(&b); + }); + } + + #[cfg(feature = "bench")] + #[bench] + fn bench_subtraction_2_digit(bencher: &mut Bencher) { + let a = random_big_uint(2); + let b = random_big_uint(2); + bencher.iter(|| { + let _ = a.clone().sub(&b); + }); + } + + #[cfg(feature = "bench")] + #[bench] + fn bench_subtraction_4_digit(bencher: &mut Bencher) { + let a = random_big_uint(4); + let b = random_big_uint(4); + bencher.iter(|| { + let _ = a.clone().sub(&b); + }); + } + + #[cfg(feature = "bench")] + #[bench] + fn bench_multiplication_2_digit(bencher: &mut Bencher) { + let a = random_big_uint(2); + let b = random_big_uint(2); + bencher.iter(|| { + let _ = a.clone().mul(&b); + }); + } + + #[cfg(feature = "bench")] + #[bench] + fn bench_multiplication_4_digit(bencher: &mut Bencher) { + let a = random_big_uint(4); + let b = random_big_uint(4); + bencher.iter(|| { + let _ = a.clone().mul(&b); + }); + } + + #[cfg(feature = "bench")] + #[bench] + fn bench_division_4_digit(bencher: &mut Bencher) { + let a = random_big_uint(4); + let b = random_big_uint(2); + bencher.iter(|| { + let _ = a.clone().div(&b, true); + }); + } +} diff --git a/core/sr-arithmetic/src/fixed64.rs b/core/sr-arithmetic/src/fixed64.rs new file mode 100644 index 0000000000000..7dfc8414d26b7 --- /dev/null +++ b/core/sr-arithmetic/src/fixed64.rs @@ -0,0 +1,233 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use rstd::{ + ops, prelude::*, + convert::{TryFrom, TryInto}, +}; +use codec::{Encode, Decode}; +use crate::{ + Perbill, + traits::{ + SaturatedConversion, CheckedSub, CheckedAdd, Bounded, UniqueSaturatedInto, Saturating + } +}; + +/// An unsigned fixed point number. Can hold any value in the range [-9_223_372_036, 9_223_372_036] +/// with fixed point accuracy of one billion. +#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Fixed64(i64); + +/// The accuracy of the `Fixed64` type. +const DIV: i64 = 1_000_000_000; + +impl Fixed64 { + /// creates self from a natural number. + /// + /// Note that this might be lossy. + pub fn from_natural(int: i64) -> Self { + Self(int.saturating_mul(DIV)) + } + + /// Return the accuracy of the type. Given that this function returns the value `X`, it means + /// that an instance composed of `X` parts (`Fixed64::from_parts(X)`) is equal to `1`. + pub fn accuracy() -> i64 { + DIV + } + + /// Raw constructor. Equal to `parts / 1_000_000_000`. + pub fn from_parts(parts: i64) -> Self { + Self(parts) + } + + /// creates self from a rational number. Equal to `n/d`. + /// + /// Note that this might be lossy. + pub fn from_rational(n: i64, d: u64) -> Self { + Self( + (i128::from(n).saturating_mul(i128::from(DIV)) / i128::from(d).max(1)) + .try_into() + .unwrap_or_else(|_| Bounded::max_value()) + ) + } + + /// Performs a saturated multiply and accumulate by unsigned number. + /// + /// Returns a saturated `int + (self * int)`. + pub fn saturated_multiply_accumulate(self, int: N) -> N + where + N: TryFrom + From + UniqueSaturatedInto + Bounded + Clone + Saturating + + ops::Rem + ops::Div + ops::Mul + + ops::Add, + { + let div = DIV as u64; + let positive = self.0 > 0; + // safe to convert as absolute value. + let parts = self.0.checked_abs().map(|v| v as u64).unwrap_or(i64::max_value() as u64 + 1); + + + // will always fit. + let natural_parts = parts / div; + // might saturate. + let natural_parts: N = natural_parts.saturated_into(); + // fractional parts can always fit into u32. + let perbill_parts = (parts % div) as u32; + + let n = int.clone().saturating_mul(natural_parts); + let p = Perbill::from_parts(perbill_parts) * int.clone(); + + // everything that needs to be either added or subtracted from the original weight. + let excess = n.saturating_add(p); + + if positive { + int.saturating_add(excess) + } else { + int.saturating_sub(excess) + } + } +} + +impl Saturating for Fixed64 { + fn saturating_add(self, rhs: Self) -> Self { + Self(self.0.saturating_add(rhs.0)) + } + fn saturating_mul(self, rhs: Self) -> Self { + Self(self.0.saturating_mul(rhs.0) / DIV) + } + fn saturating_sub(self, rhs: Self) -> Self { + Self(self.0.saturating_sub(rhs.0)) + } +} + +/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait +/// for safe addition. +impl ops::Add for Fixed64 { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0) + } +} + +/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait +/// for safe subtraction. +impl ops::Sub for Fixed64 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } +} + +impl CheckedSub for Fixed64 { + fn checked_sub(&self, rhs: &Self) -> Option { + self.0.checked_sub(rhs.0).map(Self) + } +} + +impl CheckedAdd for Fixed64 { + fn checked_add(&self, rhs: &Self) -> Option { + self.0.checked_add(rhs.0).map(Self) + } +} + +#[cfg(feature = "std")] +impl rstd::fmt::Debug for Fixed64 { + fn fmt(&self, f: &mut rstd::fmt::Formatter<'_>) -> rstd::fmt::Result { + write!(f, "Fixed64({},{})", self.0 / DIV, (self.0 % DIV) / 1000) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn max() -> Fixed64 { + Fixed64::from_parts(i64::max_value()) + } + + #[test] + fn fixed64_semantics() { + assert_eq!(Fixed64::from_rational(5, 2).0, 5 * 1_000_000_000 / 2); + assert_eq!(Fixed64::from_rational(5, 2), Fixed64::from_rational(10, 4)); + assert_eq!(Fixed64::from_rational(5, 0), Fixed64::from_rational(5, 1)); + + // biggest value that can be created. + assert_ne!(max(), Fixed64::from_natural(9_223_372_036)); + assert_eq!(max(), Fixed64::from_natural(9_223_372_037)); + } + + #[test] + fn fixed_64_growth_decrease_curve() { + let test_set = vec![0u32, 1, 10, 1000, 1_000_000_000]; + + // negative (1/2) + let mut fm = Fixed64::from_rational(-1, 2); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i) as i32, i as i32 - i as i32 / 2); + }); + + // unit (1) multiplier + fm = Fixed64::from_parts(0); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i), i); + }); + + // i.5 multiplier + fm = Fixed64::from_rational(1, 2); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i), i * 3 / 2); + }); + + // dual multiplier + fm = Fixed64::from_rational(1, 1); + test_set.clone().into_iter().for_each(|i| { + assert_eq!(fm.saturated_multiply_accumulate(i), i * 2); + }); + } + + macro_rules! saturating_mul_acc_test { + ($num_type:tt) => { + assert_eq!( + Fixed64::from_rational(100, 1).saturated_multiply_accumulate(10 as $num_type), + 1010, + ); + assert_eq!( + Fixed64::from_rational(100, 2).saturated_multiply_accumulate(10 as $num_type), + 510, + ); + assert_eq!( + Fixed64::from_rational(100, 3).saturated_multiply_accumulate(0 as $num_type), + 0, + ); + assert_eq!( + Fixed64::from_rational(5, 1).saturated_multiply_accumulate($num_type::max_value()), + $num_type::max_value() + ); + assert_eq!( + max().saturated_multiply_accumulate($num_type::max_value()), + $num_type::max_value() + ); + } + } + + #[test] + fn fixed64_multiply_accumulate_works() { + saturating_mul_acc_test!(u32); + saturating_mul_acc_test!(u64); + saturating_mul_acc_test!(u128); + } +} diff --git a/core/sr-arithmetic/src/helpers_128bit.rs b/core/sr-arithmetic/src/helpers_128bit.rs new file mode 100644 index 0000000000000..10cc94ae777e6 --- /dev/null +++ b/core/sr-arithmetic/src/helpers_128bit.rs @@ -0,0 +1,112 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Some helper functions to work with 128bit numbers. Note that the functionality provided here is +//! only sensible to use with 128bit numbers because for smaller sizes, you can always rely on +//! assumptions of a bigger type (u128) being available, or simply create a per-thing and use the +//! multiplication implementation provided there. + +use crate::biguint; +use num_traits::Zero; +use rstd::{cmp::{min, max}, convert::TryInto, mem}; + +/// Helper gcd function used in Rational128 implementation. +pub fn gcd(a: u128, b: u128) -> u128 { + match ((a, b), (a & 1, b & 1)) { + ((x, y), _) if x == y => y, + ((0, x), _) | ((x, 0), _) => x, + ((x, y), (0, 1)) | ((y, x), (1, 0)) => gcd(x >> 1, y), + ((x, y), (0, 0)) => gcd(x >> 1, y >> 1) << 1, + ((x, y), (1, 1)) => { + let (x, y) = (min(x, y), max(x, y)); + gcd((y - x) >> 1, x) + }, + _ => unreachable!(), + } +} + +/// split a u128 into two u64 limbs +pub fn split(a: u128) -> (u64, u64) { + let al = a as u64; + let ah = (a >> 64) as u64; + (ah, al) +} + +/// Convert a u128 to a u32 based biguint. +pub fn to_big_uint(x: u128) -> biguint::BigUint { + let (xh, xl) = split(x); + let (xhh, xhl) = biguint::split(xh); + let (xlh, xll) = biguint::split(xl); + let mut n = biguint::BigUint::from_limbs(&[xhh, xhl, xlh, xll]); + n.lstrip(); + n +} + +/// Safely and accurately compute `a * b / c`. The approach is: +/// - Simply try `a * b / c`. +/// - Else, convert them both into big numbers and re-try. `Err` is returned if the result +/// cannot be safely casted back to u128. +/// +/// Invariant: c must be greater than or equal to 1. +pub fn multiply_by_rational(mut a: u128, mut b: u128, mut c: u128) -> Result { + if a.is_zero() || b.is_zero() { return Ok(Zero::zero()); } + c = c.max(1); + + // a and b are interchangeable by definition in this function. It always helps to assume the + // bigger of which is being multiplied by a `0 < b/c < 1`. Hence, a should be the bigger and + // b the smaller one. + if b > a { + mem::swap(&mut a, &mut b); + } + + // Attempt to perform the division first + if a % c == 0 { + a /= c; + c = 1; + } else if b % c == 0 { + b /= c; + c = 1; + } + + if let Some(x) = a.checked_mul(b) { + // This is the safest way to go. Try it. + Ok(x / c) + } else { + let a_num = to_big_uint(a); + let b_num = to_big_uint(b); + let c_num = to_big_uint(c); + + let mut ab = a_num * b_num; + ab.lstrip(); + let mut q = if c_num.len() == 1 { + // PROOF: if `c_num.len() == 1` then `c` fits in one limb. + ab.div_unit(c as biguint::Single) + } else { + // PROOF: both `ab` and `c` cannot have leading zero limbs; if length of `c` is 1, + // the previous branch would handle. Also, if ab for sure has a bigger size than + // c, because `a.checked_mul(b)` has failed, hence ab must be at least one limb + // bigger than c. In this case, returning zero is defensive-only and div should + // always return Some. + let (mut q, r) = ab.div(&c_num, true).unwrap_or((Zero::zero(), Zero::zero())); + let r: u128 = r.try_into() + .expect("reminder of div by c is always less than c; qed"); + if r > (c / 2) { q = q.add(&to_big_uint(1)); } + q + }; + q.lstrip(); + q.try_into().map_err(|_| "result cannot fit in u128") + } +} diff --git a/core/sr-arithmetic/src/lib.rs b/core/sr-arithmetic/src/lib.rs new file mode 100644 index 0000000000000..847ca9e797cb0 --- /dev/null +++ b/core/sr-arithmetic/src/lib.rs @@ -0,0 +1,48 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Minimal fixed point arithmetic primitives and types for runtime. + +#![cfg_attr(not(feature = "std"), no_std)] + +// to allow benchmarking +#![cfg_attr(feature = "bench", feature(test))] +#[cfg(feature = "bench")] extern crate test; + +/// Copied from `sr-primitives` and documented there. +#[cfg(test)] +macro_rules! assert_eq_error_rate { + ($x:expr, $y:expr, $error:expr $(,)?) => { + assert!( + ($x) >= (($y) - ($error)) && ($x) <= (($y) + ($error)), + "{:?} != {:?} (with error rate {:?})", + $x, + $y, + $error, + ); + }; +} + +pub mod biguint; +pub mod helpers_128bit; +pub mod traits; +mod per_things; +mod fixed64; +mod rational128; + +pub use fixed64::Fixed64; +pub use per_things::{Percent, Permill, Perbill, Perquintill}; +pub use rational128::Rational128; diff --git a/core/sr-arithmetic/src/per_things.rs b/core/sr-arithmetic/src/per_things.rs new file mode 100644 index 0000000000000..afd8100a843e7 --- /dev/null +++ b/core/sr-arithmetic/src/per_things.rs @@ -0,0 +1,519 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +#[cfg(feature = "std")] +use serde::{Serialize, Deserialize}; + +use rstd::{ops, prelude::*, convert::TryInto}; +use codec::{Encode, Decode, CompactAs}; +use crate::traits::{SaturatedConversion, UniqueSaturatedInto, Saturating}; + +macro_rules! implement_per_thing { + ($name:ident, $test_mod:ident, [$($test_units:tt),+], $max:tt, $type:ty, $upper_type:ty, $title:expr $(,)?) => { + /// A fixed point representation of a number between in the range [0, 1]. + /// + #[doc = $title] + #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Ord, PartialOrd))] + #[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, CompactAs)] + pub struct $name($type); + + impl $name { + /// Nothing. + pub fn zero() -> Self { Self(0) } + + /// `true` if this is nothing. + pub fn is_zero(&self) -> bool { self.0 == 0 } + + /// Everything. + pub fn one() -> Self { Self($max) } + + /// Consume self and deconstruct into a raw numeric type. + pub fn deconstruct(self) -> $type { self.0 } + + /// Return the scale at which this per-thing is working. + pub const fn accuracy() -> $type { $max } + + /// From an explicitly defined number of parts per maximum of the type. + /// + /// This can be called at compile time. + pub const fn from_parts(parts: $type) -> Self { + Self([parts, $max][(parts > $max) as usize]) + } + + /// Converts from a percent. Equal to `x / 100`. + /// + /// This can be created at compile time. + pub const fn from_percent(x: $type) -> Self { + Self([x, 100][(x > 100) as usize] * ($max / 100)) + } + + /// Return the product of multiplication of this value by itself. + pub fn square(self) -> Self { + // both can be safely casted and multiplied. + let p: $upper_type = self.0 as $upper_type * self.0 as $upper_type; + let q: $upper_type = <$upper_type>::from($max) * <$upper_type>::from($max); + Self::from_rational_approximation(p, q) + } + + /// Converts a fraction into `Permill`. + #[cfg(feature = "std")] + pub fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as $type) } + + /// Approximate the fraction `p/q` into a per-thing fraction. This will never overflow. + /// + /// The computation of this approximation is performed in the generic type `N`. Given + /// `M` as the data type that can hold the maximum value of this per-thing (e.g. u32 for + /// perbill), this can only work if `N == M` or `N: From + TryInto`. + pub fn from_rational_approximation(p: N, q: N) -> Self + where N: Clone + Ord + From<$type> + TryInto<$type> + ops::Div + { + // q cannot be zero. + let q = q.max((1 as $type).into()); + // p should not be bigger than q. + let p = p.min(q.clone()); + + let factor = (q.clone() / $max.into()).max((1 as $type).into()); + + // q cannot overflow: (q / (q/$max)) < 2 * $max. p < q hence p also cannot overflow. + // this implies that $type must be able to fit 2 * $max. + let q_reduce: $type = (q / factor.clone()) + .try_into() + .map_err(|_| "Failed to convert") + .expect( + "q / (q/$max) < (2 * $max). Macro prevents any type being created that \ + does not satisfy this; qed" + ); + let p_reduce: $type = (p / factor.clone()) + .try_into() + .map_err(|_| "Failed to convert") + .expect( + "q / (q/$max) < (2 * $max). Macro prevents any type being created that \ + does not satisfy this; qed" + ); + + // `p_reduced` and `q_reduced` are withing $type. Mul by another $max will always + // fit in $upper_type. This is guaranteed by the macro tests. + let part = + p_reduce as $upper_type + * <$upper_type>::from($max) + / q_reduce as $upper_type; + + $name(part as $type) + } + } + + impl Saturating for $name { + fn saturating_add(self, rhs: Self) -> Self { + // defensive-only: since `$max * 2 < $type::max_value()`, this can never overflow. + Self::from_parts(self.0.saturating_add(rhs.0)) + } + fn saturating_sub(self, rhs: Self) -> Self { + Self::from_parts(self.0.saturating_sub(rhs.0)) + } + fn saturating_mul(self, rhs: Self) -> Self { + let a = self.0 as $upper_type; + let b = rhs.0 as $upper_type; + let m = <$upper_type>::from($max); + let parts = a * b / m; + // This will always fit into $type. + Self::from_parts(parts as $type) + } + } + + impl ops::Div for $name { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + let p = self.0; + let q = rhs.0; + Self::from_rational_approximation(p, q) + } + } + + /// Overflow-prune multiplication. + /// + /// tailored to be used with a balance type. + impl ops::Mul for $name + where + N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem + + ops::Div + ops::Mul + ops::Add, + { + type Output = N; + fn mul(self, b: N) -> Self::Output { + let maximum: N = $max.into(); + let upper_max: $upper_type = $max.into(); + let part: N = self.0.into(); + + let rem_multiplied_divided = { + let rem = b.clone().rem(maximum.clone()); + + // `rem_sized` is inferior to $max, thus it fits into $type. This is assured by + // a test. + let rem_sized = rem.saturated_into::<$type>(); + + // `self` and `rem_sized` are inferior to $max, thus the product is less than + // $max^2 and fits into $upper_type. This is assured by a test. + let rem_multiplied_upper = rem_sized as $upper_type * self.0 as $upper_type; + + // `rem_multiplied_upper` is less than $max^2 therefore divided by $max it fits + // in $type. remember that $type always fits $max. + let mut rem_multiplied_divided_sized = + (rem_multiplied_upper / upper_max) as $type; + // fix a tiny rounding error + if rem_multiplied_upper % upper_max > upper_max / 2 { + rem_multiplied_divided_sized += 1; + } + + // `rem_multiplied_divided_sized` is inferior to b, thus it can be converted + // back to N type + rem_multiplied_divided_sized.into() + }; + + (b / maximum) * part + rem_multiplied_divided + } + } + + #[cfg(test)] + mod $test_mod { + use codec::{Encode, Decode}; + use super::{$name, Saturating}; + use crate::traits::Zero; + + + #[test] + fn macro_expanded_correctly() { + // needed for the `from_percent` to work. + assert!($max >= 100); + assert!($max % 100 == 0); + + // needed for `from_rational_approximation` + assert!(2 * $max < <$type>::max_value()); + assert!(<$upper_type>::from($max) < <$upper_type>::max_value()); + + // for something like percent they can be the same. + assert!((<$type>::max_value() as $upper_type) <= <$upper_type>::max_value()); + assert!(<$upper_type>::from($max).checked_mul($max.into()).is_some()); + } + + #[derive(Encode, Decode, PartialEq, Eq, Debug)] + struct WithCompact { + data: T, + } + + #[test] + fn has_compact() { + let data = WithCompact { data: $name(1) }; + let encoded = data.encode(); + assert_eq!(data, WithCompact::<$name>::decode(&mut &encoded[..]).unwrap()); + } + + #[test] + fn compact_encoding() { + let tests = [ + // assume all per_things have the size u8 at least. + (0 as $type, 1usize), + (1 as $type, 1usize), + (63, 1), + (64, 2), + (65, 2), + (<$type>::max_value(), <$type>::max_value().encode().len() + 1) + ]; + for &(n, l) in &tests { + let compact: codec::Compact<$name> = $name(n).into(); + let encoded = compact.encode(); + assert_eq!(encoded.len(), l); + let decoded = >::decode(&mut & encoded[..]) + .unwrap(); + let per_thingy: $name = decoded.into(); + assert_eq!(per_thingy, $name(n)); + } + } + + #[test] + fn per_thing_api_works() { + // some really basic stuff + assert_eq!($name::zero(), $name::from_parts(Zero::zero())); + assert_eq!($name::one(), $name::from_parts($max)); + assert_eq!($name::accuracy(), $max); + assert_eq!($name::from_percent(0), $name::from_parts(Zero::zero())); + assert_eq!($name::from_percent(10), $name::from_parts($max / 10)); + assert_eq!($name::from_percent(100), $name::from_parts($max)); + } + + macro_rules! per_thing_mul_test { + ($num_type:tt) => { + // multiplication from all sort of from_percent + assert_eq!( + $name::from_percent(100) * $num_type::max_value(), + $num_type::max_value() + ); + assert_eq_error_rate!( + $name::from_percent(99) * $num_type::max_value(), + ((Into::::into($num_type::max_value()) * 99u32) / 100u32).as_u128() as $num_type, + 1, + ); + assert_eq!( + $name::from_percent(50) * $num_type::max_value(), + $num_type::max_value() / 2, + ); + assert_eq_error_rate!( + $name::from_percent(1) * $num_type::max_value(), + $num_type::max_value() / 100, + 1, + ); + assert_eq!($name::from_percent(0) * $num_type::max_value(), 0); + + // // multiplication with bounds + assert_eq!($name::one() * $num_type::max_value(), $num_type::max_value()); + assert_eq!($name::zero() * $num_type::max_value(), 0); + } + } + + #[test] + fn per_thing_mul_works() { + use primitive_types::U256; + + // accuracy test + assert_eq!($name::from_rational_approximation(1 as $type, 3) * 30 as $type, 10); + + $(per_thing_mul_test!($test_units);)* + } + + #[test] + fn per_thing_mul_rounds_to_nearest_number() { + assert_eq!($name::from_percent(33) * 10u64, 3); + assert_eq!($name::from_percent(34) * 10u64, 3); + assert_eq!($name::from_percent(35) * 10u64, 3); + assert_eq!($name::from_percent(36) * 10u64, 4); + assert_eq!($name::from_percent(36) * 10u64, 4); + } + + #[test] + fn per_thing_multiplication_with_large_number() { + use primitive_types::U256; + let max_minus_one = $max - 1; + assert_eq_error_rate!( + $name::from_parts(max_minus_one) * std::u128::MAX, + ((Into::::into(std::u128::MAX) * max_minus_one) / $max).as_u128(), + 1, + ); + } + + macro_rules! per_thing_from_rationale_approx_test { + ($num_type:tt) => { + // within accuracy boundary + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 0), + $name::one(), + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 1), + $name::one(), + ); + assert_eq_error_rate!( + $name::from_rational_approximation(1 as $num_type, 3).0, + $name::from_parts($max / 3).0, + 2 + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 10), + $name::from_percent(10), + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 4), + $name::from_percent(25), + ); + assert_eq!( + $name::from_rational_approximation(1 as $num_type, 4), + $name::from_rational_approximation(2 as $num_type, 8), + ); + // no accurate anymore but won't overflow. + assert_eq!( + $name::from_rational_approximation( + $num_type::max_value() - 1, + $num_type::max_value() + ), + $name::one(), + ); + assert_eq_error_rate!( + $name::from_rational_approximation( + $num_type::max_value() / 3, + $num_type::max_value() + ).0, + $name::from_parts($max / 3).0, + 2 + ); + assert_eq!( + $name::from_rational_approximation(1, $num_type::max_value()), + $name::zero(), + ); + }; + } + + #[test] + fn per_thing_from_rationale_approx_works() { + // This is just to make sure something like Percent which _might_ get built from a + // u8 does not overflow in the context of this test. + let max_value = <$upper_type>::from($max); + // almost at the edge + assert_eq!( + $name::from_rational_approximation($max - 1, $max + 1), + $name::from_parts($max - 2), + ); + assert_eq!( + $name::from_rational_approximation(1, $max-1), + $name::from_parts(1), + ); + assert_eq!( + $name::from_rational_approximation(1, $max), + $name::from_parts(1), + ); + assert_eq!( + $name::from_rational_approximation(2, 2 * $max - 1), + $name::from_parts(1), + ); + assert_eq!( + $name::from_rational_approximation(1, $max+1), + $name::zero(), + ); + assert_eq!( + $name::from_rational_approximation(3 * max_value / 2, 3 * max_value), + $name::from_percent(50), + ); + $(per_thing_from_rationale_approx_test!($test_units);)* + } + + #[test] + fn per_things_mul_operates_in_output_type() { + // assert_eq!($name::from_percent(50) * 100u32, 50u32); + assert_eq!($name::from_percent(50) * 100u64, 50u64); + assert_eq!($name::from_percent(50) * 100u128, 50u128); + } + + #[test] + fn per_thing_saturating_op_works() { + assert_eq!( + $name::from_percent(50).saturating_add($name::from_percent(40)), + $name::from_percent(90) + ); + assert_eq!( + $name::from_percent(50).saturating_add($name::from_percent(50)), + $name::from_percent(100) + ); + assert_eq!( + $name::from_percent(60).saturating_add($name::from_percent(50)), + $name::from_percent(100) + ); + + assert_eq!( + $name::from_percent(60).saturating_sub($name::from_percent(50)), + $name::from_percent(10) + ); + assert_eq!( + $name::from_percent(60).saturating_sub($name::from_percent(60)), + $name::from_percent(0) + ); + assert_eq!( + $name::from_percent(60).saturating_sub($name::from_percent(70)), + $name::from_percent(0) + ); + + assert_eq!( + $name::from_percent(50).saturating_mul($name::from_percent(50)), + $name::from_percent(25) + ); + assert_eq!( + $name::from_percent(20).saturating_mul($name::from_percent(20)), + $name::from_percent(4) + ); + assert_eq!( + $name::from_percent(10).saturating_mul($name::from_percent(10)), + $name::from_percent(1) + ); + } + + #[test] + fn per_thing_square_works() { + assert_eq!($name::from_percent(100).square(), $name::from_percent(100)); + assert_eq!($name::from_percent(50).square(), $name::from_percent(25)); + assert_eq!($name::from_percent(10).square(), $name::from_percent(1)); + assert_eq!( + $name::from_percent(2).square(), + $name::from_parts((4 * <$upper_type>::from($max) / 100 / 100) as $type) + ); + } + + #[test] + fn per_things_div_works() { + // normal + assert_eq!($name::from_percent(10) / $name::from_percent(20), + $name::from_percent(50) + ); + assert_eq!($name::from_percent(10) / $name::from_percent(10), + $name::from_percent(100) + ); + assert_eq!($name::from_percent(10) / $name::from_percent(0), + $name::from_percent(100) + ); + + // will not overflow + assert_eq!($name::from_percent(10) / $name::from_percent(5), + $name::from_percent(100) + ); + assert_eq!($name::from_percent(100) / $name::from_percent(50), + $name::from_percent(100) + ); + } + } + }; +} + +implement_per_thing!( + Percent, + test_per_cent, + [u32, u64, u128], + 100u8, + u8, + u16, + "_Percent_", +); +implement_per_thing!( + Permill, + test_permill, + [u32, u64, u128], + 1_000_000u32, + u32, + u64, + "_Parts per Million_", +); +implement_per_thing!( + Perbill, + test_perbill, + [u32, u64, u128], + 1_000_000_000u32, + u32, + u64, + "_Parts per Billion_", +); +implement_per_thing!( + Perquintill, + test_perquintill, + [u64, u128], + 1_000_000_000_000_000_000u64, + u64, + u128, + "_Parts per Quintillion_", +); diff --git a/core/sr-arithmetic/src/rational128.rs b/core/sr-arithmetic/src/rational128.rs new file mode 100644 index 0000000000000..706d6a5eba83c --- /dev/null +++ b/core/sr-arithmetic/src/rational128.rs @@ -0,0 +1,384 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +use rstd::{cmp::Ordering, prelude::*}; +use crate::helpers_128bit; +use num_traits::Zero; + +/// A wrapper for any rational number with a 128 bit numerator and denominator. +#[derive(Clone, Copy, Default, Eq)] +#[cfg_attr(feature = "std", derive(Debug))] +pub struct Rational128(u128, u128); + +impl Rational128 { + /// Nothing. + pub fn zero() -> Self { + Self(0, 1) + } + + /// If it is zero or not + pub fn is_zero(&self) -> bool { + self.0.is_zero() + } + + /// Build from a raw `n/d`. + pub fn from(n: u128, d: u128) -> Self { + Self(n, d.max(1)) + } + + /// Build from a raw `n/d`. This could lead to / 0 if not properly handled. + pub fn from_unchecked(n: u128, d: u128) -> Self { + Self(n, d) + } + + /// Return the numerator. + pub fn n(&self) -> u128 { + self.0 + } + + /// Return the denominator. + pub fn d(&self) -> u128 { + self.1 + } + + /// Convert `self` to a similar rational number where denominator is the given `den`. + // + /// This only returns if the result is accurate. `Err` is returned if the result cannot be + /// accurately calculated. + pub fn to_den(self, den: u128) -> Result { + if den == self.1 { + Ok(self) + } else { + helpers_128bit::multiply_by_rational(self.0, den, self.1).map(|n| Self(n, den)) + } + } + + /// Get the least common divisor of `self` and `other`. + /// + /// This only returns if the result is accurate. `Err` is returned if the result cannot be + /// accurately calculated. + pub fn lcm(&self, other: &Self) -> Result { + // this should be tested better: two large numbers that are almost the same. + if self.1 == other.1 { return Ok(self.1) } + let g = helpers_128bit::gcd(self.1, other.1); + helpers_128bit::multiply_by_rational(self.1 , other.1, g) + } + + /// A saturating add that assumes `self` and `other` have the same denominator. + pub fn lazy_saturating_add(self, other: Self) -> Self { + if other.is_zero() { + self + } else { + Self(self.0.saturating_add(other.0) ,self.1) + } + } + + /// A saturating subtraction that assumes `self` and `other` have the same denominator. + pub fn lazy_saturating_sub(self, other: Self) -> Self { + if other.is_zero() { + self + } else { + Self(self.0.saturating_sub(other.0) ,self.1) + } + } + + /// Addition. Simply tries to unify the denominators and add the numerators. + /// + /// Overflow might happen during any of the steps. Error is returned in such cases. + pub fn checked_add(self, other: Self) -> Result { + let lcm = self.lcm(&other).map_err(|_| "failed to scale to denominator")?; + let self_scaled = self.to_den(lcm).map_err(|_| "failed to scale to denominator")?; + let other_scaled = other.to_den(lcm).map_err(|_| "failed to scale to denominator")?; + let n = self_scaled.0.checked_add(other_scaled.0) + .ok_or("overflow while adding numerators")?; + Ok(Self(n, self_scaled.1)) + } + + /// Subtraction. Simply tries to unify the denominators and subtract the numerators. + /// + /// Overflow might happen during any of the steps. None is returned in such cases. + pub fn checked_sub(self, other: Self) -> Result { + let lcm = self.lcm(&other).map_err(|_| "failed to scale to denominator")?; + let self_scaled = self.to_den(lcm).map_err(|_| "failed to scale to denominator")?; + let other_scaled = other.to_den(lcm).map_err(|_| "failed to scale to denominator")?; + + let n = self_scaled.0.checked_sub(other_scaled.0) + .ok_or("overflow while subtracting numerators")?; + Ok(Self(n, self_scaled.1)) + } +} + +impl PartialOrd for Rational128 { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Rational128 { + fn cmp(&self, other: &Self) -> Ordering { + // handle some edge cases. + if self.1 == other.1 { + self.0.cmp(&other.0) + } else if self.1.is_zero() { + Ordering::Greater + } else if other.1.is_zero() { + Ordering::Less + } else { + // Don't even compute gcd. + let self_n = helpers_128bit::to_big_uint(self.0) * helpers_128bit::to_big_uint(other.1); + let other_n = helpers_128bit::to_big_uint(other.0) * helpers_128bit::to_big_uint(self.1); + self_n.cmp(&other_n) + } + } +} + +impl PartialEq for Rational128 { + fn eq(&self, other: &Self) -> bool { + // handle some edge cases. + if self.1 == other.1 { + self.0.eq(&other.0) + } else { + let self_n = helpers_128bit::to_big_uint(self.0) * helpers_128bit::to_big_uint(other.1); + let other_n = helpers_128bit::to_big_uint(other.0) * helpers_128bit::to_big_uint(self.1); + self_n.eq(&other_n) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use super::helpers_128bit::*; + + const MAX128: u128 = u128::max_value(); + const MAX64: u128 = u64::max_value() as u128; + const MAX64_2: u128 = 2 * u64::max_value() as u128; + + fn r(p: u128, q: u128) -> Rational128 { + Rational128(p, q) + } + + fn mul_div(a: u128, b: u128, c: u128) -> u128 { + use primitive_types::U256; + if a.is_zero() { return Zero::zero(); } + let c = c.max(1); + + // e for extended + let ae: U256 = a.into(); + let be: U256 = b.into(); + let ce: U256 = c.into(); + + let r = ae * be / ce; + if r > u128::max_value().into() { + a + } else { + r.as_u128() + } + } + + #[test] + fn truth_value_function_works() { + assert_eq!( + mul_div(2u128.pow(100), 8, 4), + 2u128.pow(101) + ); + assert_eq!( + mul_div(2u128.pow(100), 4, 8), + 2u128.pow(99) + ); + + // and it returns a if result cannot fit + assert_eq!(mul_div(MAX128 - 10, 2, 1), MAX128 - 10); + } + + #[test] + fn to_denom_works() { + // simple up and down + assert_eq!(r(1, 5).to_den(10), Ok(r(2, 10))); + assert_eq!(r(4, 10).to_den(5), Ok(r(2, 5))); + + // up and down with large numbers + assert_eq!(r(MAX128 - 10, MAX128).to_den(10), Ok(r(10, 10))); + assert_eq!(r(MAX128 / 2, MAX128).to_den(10), Ok(r(5, 10))); + + // large to perbill. This is very well needed for phragmen. + assert_eq!( + r(MAX128 / 2, MAX128).to_den(1000_000_000), + Ok(r(500_000_000, 1000_000_000)) + ); + + // large to large + assert_eq!(r(MAX128 / 2, MAX128).to_den(MAX128/2), Ok(r(MAX128/4, MAX128/2))); + } + + #[test] + fn gdc_works() { + assert_eq!(gcd(10, 5), 5); + assert_eq!(gcd(7, 22), 1); + } + + #[test] + fn lcm_works() { + // simple stuff + assert_eq!(r(3, 10).lcm(&r(4, 15)).unwrap(), 30); + assert_eq!(r(5, 30).lcm(&r(1, 7)).unwrap(), 210); + assert_eq!(r(5, 30).lcm(&r(1, 10)).unwrap(), 30); + + // large numbers + assert_eq!( + r(1_000_000_000, MAX128).lcm(&r(7_000_000_000, MAX128-1)), + Err("result cannot fit in u128"), + ); + assert_eq!( + r(1_000_000_000, MAX64).lcm(&r(7_000_000_000, MAX64-1)), + Ok(340282366920938463408034375210639556610), + ); + assert!(340282366920938463408034375210639556610 < MAX128); + assert!(340282366920938463408034375210639556610 == MAX64 * (MAX64 - 1)); + } + + #[test] + fn add_works() { + // works + assert_eq!(r(3, 10).checked_add(r(1, 10)).unwrap(), r(2, 5)); + assert_eq!(r(3, 10).checked_add(r(3, 7)).unwrap(), r(51, 70)); + + // errors + assert_eq!( + r(1, MAX128).checked_add(r(1, MAX128-1)), + Err("failed to scale to denominator"), + ); + assert_eq!( + r(7, MAX128).checked_add(r(MAX128, MAX128)), + Err("overflow while adding numerators"), + ); + assert_eq!( + r(MAX128, MAX128).checked_add(r(MAX128, MAX128)), + Err("overflow while adding numerators"), + ); + } + + #[test] + fn sub_works() { + // works + assert_eq!(r(3, 10).checked_sub(r(1, 10)).unwrap(), r(1, 5)); + assert_eq!(r(6, 10).checked_sub(r(3, 7)).unwrap(), r(12, 70)); + + // errors + assert_eq!( + r(2, MAX128).checked_sub(r(1, MAX128-1)), + Err("failed to scale to denominator"), + ); + assert_eq!( + r(7, MAX128).checked_sub(r(MAX128, MAX128)), + Err("overflow while subtracting numerators"), + ); + assert_eq!( + r(1, 10).checked_sub(r(2,10)), + Err("overflow while subtracting numerators"), + ); + } + + #[test] + fn ordering_and_eq_works() { + assert!(r(1, 2) > r(1, 3)); + assert!(r(1, 2) > r(2, 6)); + + assert!(r(1, 2) < r(6, 6)); + assert!(r(2, 1) > r(2, 6)); + + assert!(r(5, 10) == r(1, 2)); + assert!(r(1, 2) == r(1, 2)); + + assert!(r(1, 1490000000000200000) > r(1, 1490000000000200001)); + } + + #[test] + fn multiply_by_rational_works() { + assert_eq!(multiply_by_rational(7, 2, 3).unwrap(), 7 * 2 / 3); + assert_eq!(multiply_by_rational(7, 20, 30).unwrap(), 7 * 2 / 3); + assert_eq!(multiply_by_rational(20, 7, 30).unwrap(), 7 * 2 / 3); + + assert_eq!( + // MAX128 % 3 == 0 + multiply_by_rational(MAX128, 2, 3).unwrap(), + MAX128 / 3 * 2, + ); + assert_eq!( + // MAX128 % 7 == 3 + multiply_by_rational(MAX128, 5, 7).unwrap(), + (MAX128 / 7 * 5) + (3 * 5 / 7), + ); + assert_eq!( + // MAX128 % 7 == 3 + multiply_by_rational(MAX128, 11 , 13).unwrap(), + (MAX128 / 13 * 11) + (8 * 11 / 13), + ); + assert_eq!( + // MAX128 % 1000 == 455 + multiply_by_rational(MAX128, 555, 1000).unwrap(), + (MAX128 / 1000 * 555) + (455 * 555 / 1000), + ); + + assert_eq!( + multiply_by_rational(2 * MAX64 - 1, MAX64, MAX64).unwrap(), + 2 * MAX64 - 1, + ); + assert_eq!( + multiply_by_rational(2 * MAX64 - 1, MAX64 - 1, MAX64).unwrap(), + 2 * MAX64 - 3, + ); + + assert_eq!( + multiply_by_rational(MAX64 + 100, MAX64_2, MAX64_2 / 2).unwrap(), + (MAX64 + 100) * 2, + ); + assert_eq!( + multiply_by_rational(MAX64 + 100, MAX64_2 / 100, MAX64_2 / 200).unwrap(), + (MAX64 + 100) * 2, + ); + + assert_eq!( + multiply_by_rational(2u128.pow(66) - 1, 2u128.pow(65) - 1, 2u128.pow(65)).unwrap(), + 73786976294838206461, + ); + assert_eq!( + multiply_by_rational(1_000_000_000, MAX128 / 8, MAX128 / 2).unwrap(), + 250000000, + ); + } + + #[test] + fn multiply_by_rational_a_b_are_interchangeable() { + assert_eq!( + multiply_by_rational(10, MAX128, MAX128 / 2), + Ok(20), + ); + assert_eq!( + multiply_by_rational(MAX128, 10, MAX128 / 2), + Ok(20), + ); + } + + #[test] + #[ignore] + fn multiply_by_rational_fuzzed_equation() { + assert_eq!( + multiply_by_rational(154742576605164960401588224, 9223376310179529214, 549756068598), + Ok(2596149632101417846585204209223679) + ); + } +} diff --git a/core/sr-arithmetic/src/traits.rs b/core/sr-arithmetic/src/traits.rs new file mode 100644 index 0000000000000..d02425066ff9d --- /dev/null +++ b/core/sr-arithmetic/src/traits.rs @@ -0,0 +1,143 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Primitives for the runtime modules. + +use rstd::{self, convert::{TryFrom, TryInto}}; +use codec::HasCompact; +pub use integer_sqrt::IntegerSquareRoot; +pub use num_traits::{ + Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, + CheckedShl, CheckedShr +}; +use rstd::ops::{ + Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, + RemAssign, Shl, Shr +}; + +/// A meta trait for arithmetic. +/// +/// Arithmetic types do all the usual stuff you'd expect numbers to do. They are guaranteed to +/// be able to represent at least `u32` values without loss, hence the trait implies `From` +/// and smaller ints. All other conversions are fallible. +pub trait SimpleArithmetic: + Zero + One + IntegerSquareRoot + + From + From + From + TryInto + TryInto + TryInto + + TryFrom + TryInto + TryFrom + TryInto + TryFrom + TryInto + + UniqueSaturatedInto + UniqueSaturatedInto + UniqueSaturatedInto + + UniqueSaturatedFrom + UniqueSaturatedInto + UniqueSaturatedFrom + UniqueSaturatedInto + + Add + AddAssign + + Sub + SubAssign + + Mul + MulAssign + + Div + DivAssign + + Rem + RemAssign + + Shl + Shr + + CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv + + Saturating + PartialOrd + Ord + Bounded + + HasCompact + Sized +{} +impl + From + From + TryInto + TryInto + TryInto + + TryFrom + TryInto + TryFrom + TryInto + TryFrom + TryInto + + UniqueSaturatedInto + UniqueSaturatedInto + UniqueSaturatedInto + + UniqueSaturatedFrom + UniqueSaturatedInto + UniqueSaturatedFrom + + UniqueSaturatedInto + UniqueSaturatedFrom + UniqueSaturatedInto + + Add + AddAssign + + Sub + SubAssign + + Mul + MulAssign + + Div + DivAssign + + Rem + RemAssign + + Shl + Shr + + CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv + + Saturating + PartialOrd + Ord + Bounded + + HasCompact + Sized +> SimpleArithmetic for T {} + +/// Just like `From` except that if the source value is too big to fit into the destination type +/// then it'll saturate the destination. +pub trait UniqueSaturatedFrom: Sized { + /// Convert from a value of `T` into an equivalent instance of `Self`. + fn unique_saturated_from(t: T) -> Self; +} + +/// Just like `Into` except that if the source value is too big to fit into the destination type +/// then it'll saturate the destination. +pub trait UniqueSaturatedInto: Sized { + /// Consume self to return an equivalent value of `T`. + fn unique_saturated_into(self) -> T; +} + +impl + Bounded + Sized> UniqueSaturatedFrom for S { + fn unique_saturated_from(t: T) -> Self { + S::try_from(t).unwrap_or_else(|_| Bounded::max_value()) + } +} + +impl + Sized> UniqueSaturatedInto for S { + fn unique_saturated_into(self) -> T { + self.try_into().unwrap_or_else(|_| Bounded::max_value()) + } +} + +/// Simple trait to use checked mul and max value to give a saturated mul operation over +/// supported types. +pub trait Saturating { + /// Saturated addition - if the product can't fit in the type then just use max-value. + fn saturating_add(self, o: Self) -> Self; + + /// Saturated subtraction - if the product can't fit in the type then just use max-value. + fn saturating_sub(self, o: Self) -> Self; + + /// Saturated multiply - if the product can't fit in the type then just use max-value. + fn saturating_mul(self, o: Self) -> Self; +} + +impl Saturating for T { + fn saturating_add(self, o: Self) -> Self { + ::saturating_add(self, o) + } + fn saturating_sub(self, o: Self) -> Self { + ::saturating_sub(self, o) + } + fn saturating_mul(self, o: Self) -> Self { + self.checked_mul(&o).unwrap_or_else(Bounded::max_value) + } +} + +/// Convenience type to work around the highly unergonomic syntax needed +/// to invoke the functions of overloaded generic traits, in this case +/// `SaturatedFrom` and `SaturatedInto`. +pub trait SaturatedConversion { + /// Convert from a value of `T` into an equivalent instance of `Self`. + /// + /// This just uses `UniqueSaturatedFrom` internally but with this + /// variant you can provide the destination type using turbofish syntax + /// in case Rust happens not to assume the correct type. + fn saturated_from(t: T) -> Self where Self: UniqueSaturatedFrom { + >::unique_saturated_from(t) + } + + /// Consume self to return an equivalent value of `T`. + /// + /// This just uses `UniqueSaturatedInto` internally but with this + /// variant you can provide the destination type using turbofish syntax + /// in case Rust happens not to assume the correct type. + fn saturated_into(self) -> T where Self: UniqueSaturatedInto { + >::unique_saturated_into(self) + } +} +impl SaturatedConversion for T {} diff --git a/core/sr-primitives/Cargo.toml b/core/sr-primitives/Cargo.toml index ac6e8685e2baf..249b89acee965 100644 --- a/core/sr-primitives/Cargo.toml +++ b/core/sr-primitives/Cargo.toml @@ -5,12 +5,11 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -num-traits = { version = "0.2.8", default-features = false } -integer-sqrt = "0.1.2" serde = { version = "1.0.101", optional = true, features = ["derive"] } codec = { package = "parity-scale-codec", version = "1.0.0", default-features = false, features = ["derive"] } primitives = { package = "substrate-primitives", path = "../primitives", default-features = false } app-crypto = { package = "substrate-application-crypto", path = "../application-crypto", default-features = false } +arithmetic = { package = "sr-arithmetic", path = "../sr-arithmetic", default-features = false } rstd = { package = "sr-std", path = "../sr-std", default-features = false } runtime_io = { package = "sr-io", path = "../sr-io", default-features = false } log = { version = "0.4.8", optional = true } @@ -20,7 +19,6 @@ impl-trait-for-tuples = "0.1.2" [dev-dependencies] serde_json = "1.0.41" -primitive-types = "0.5.1" rand = "0.7.2" substrate-offchain = { path = "../offchain" } @@ -28,7 +26,6 @@ substrate-offchain = { path = "../offchain" } bench = [] default = ["std"] std = [ - "num-traits/std", "serde", "log", "rstd/std", @@ -36,5 +33,6 @@ std = [ "codec/std", "primitives/std", "app-crypto/std", + "arithmetic/std", "rand", ] diff --git a/core/sr-primitives/src/lib.rs b/core/sr-primitives/src/lib.rs index 7032560d0f6b1..590b6fedd7f04 100644 --- a/core/sr-primitives/src/lib.rs +++ b/core/sr-primitives/src/lib.rs @@ -51,7 +51,6 @@ pub mod testing; pub mod curve; pub mod generic; pub mod offchain; -pub mod sr_arithmetic; pub mod traits; pub mod transaction_validity; pub mod weights; @@ -64,14 +63,14 @@ pub use primitives::{TypeId, crypto::{key_types, KeyTypeId, CryptoType}}; pub use app_crypto::RuntimeAppPublic; /// Re-export top-level arithmetic stuff. -pub use sr_arithmetic::{ +pub use arithmetic::{ Perquintill, Perbill, Permill, Percent, Rational128, Fixed64 }; /// Re-export 128 bit helpers. -pub use sr_arithmetic::helpers_128bit; -/// Re-export big_uint stiff. -pub use sr_arithmetic::biguint; +pub use arithmetic::helpers_128bit; +/// Re-export big_uint stuff. +pub use arithmetic::biguint; /// An abstraction over justification for a block's validity under a consensus algorithm. /// diff --git a/core/sr-primitives/src/sr_arithmetic.rs b/core/sr-primitives/src/sr_arithmetic.rs deleted file mode 100644 index 5f5b5dc1e523b..0000000000000 --- a/core/sr-primitives/src/sr_arithmetic.rs +++ /dev/null @@ -1,2203 +0,0 @@ -// Copyright 2019 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Minimal fixed point arithmetic primitives and types for runtime. - -#[cfg(feature = "std")] -use crate::serde::{Serialize, Deserialize}; - -use rstd::{ - ops, cmp::Ordering, prelude::*, - convert::{TryFrom, TryInto}, -}; -use codec::{Encode, Decode}; -use crate::traits::{ - SaturatedConversion, CheckedSub, CheckedAdd, Bounded, UniqueSaturatedInto, Saturating, Zero, -}; - -macro_rules! implement_per_thing { - ($name:ident, $test_mod:ident, [$($test_units:tt),+], $max:tt, $type:ty, $upper_type:ty, $title:expr $(,)?) => { - /// A fixed point representation of a number between in the range [0, 1]. - /// - #[doc = $title] - #[cfg_attr(feature = "std", derive(Serialize, Deserialize, Debug, Ord, PartialOrd))] - #[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq)] - pub struct $name($type); - - impl $name { - /// Nothing. - pub fn zero() -> Self { Self(0) } - - /// `true` if this is nothing. - pub fn is_zero(&self) -> bool { self.0 == 0 } - - /// Everything. - pub fn one() -> Self { Self($max) } - - /// Consume self and deconstruct into a raw numeric type. - pub fn deconstruct(self) -> $type { self.0 } - - /// Return the scale at which this per-thing is working. - pub const fn accuracy() -> $type { $max } - - /// From an explicitly defined number of parts per maximum of the type. - /// - /// This can be called at compile time. - pub const fn from_parts(parts: $type) -> Self { - Self([parts, $max][(parts > $max) as usize]) - } - - /// Converts from a percent. Equal to `x / 100`. - /// - /// This can be created at compile time. - pub const fn from_percent(x: $type) -> Self { - Self([x, 100][(x > 100) as usize] * ($max / 100)) - } - - /// Return the product of multiplication of this value by itself. - pub fn square(self) -> Self { - // both can be safely casted and multiplied. - let p: $upper_type = self.0 as $upper_type * self.0 as $upper_type; - let q: $upper_type = $max as $upper_type * $max as $upper_type; - Self::from_rational_approximation(p, q) - } - - /// Converts a fraction into `Permill`. - #[cfg(feature = "std")] - pub fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as $type) } - - /// Approximate the fraction `p/q` into a per-thing fraction. This will never overflow. - /// - /// The computation of this approximation is performed in the generic type `N`. Given - /// `M` as the data type that can hold the maximum value of this per-thing (e.g. u32 for - /// perbill), this can only work if `N == M` or `N: From + TryInto`. - pub fn from_rational_approximation(p: N, q: N) -> Self - where N: Clone + Ord + From<$type> + TryInto<$type> + ops::Div - { - // q cannot be zero. - let q = q.max((1 as $type).into()); - // p should not be bigger than q. - let p = p.min(q.clone()); - - let factor = (q.clone() / $max.into()).max((1 as $type).into()); - - // q cannot overflow: (q / (q/$max)) < 2 * $max. p < q hence p also cannot overflow. - // this implies that $type must be able to fit 2 * $max. - let q_reduce: $type = (q / factor.clone()) - .try_into() - .map_err(|_| "Failed to convert") - .expect( - "q / (q/$max) < (2 * $max). Macro prevents any type being created that \ - does not satisfy this; qed" - ); - let p_reduce: $type = (p / factor.clone()) - .try_into() - .map_err(|_| "Failed to convert") - .expect( - "q / (q/$max) < (2 * $max). Macro prevents any type being created that \ - does not satisfy this; qed" - ); - - // `p_reduced` and `q_reduced` are withing $type. Mul by another $max will always - // fit in $upper_type. This is guaranteed by the macro tests. - let part = - p_reduce as $upper_type - * ($max as $upper_type) - / q_reduce as $upper_type; - - $name(part as $type) - } - } - - impl Saturating for $name { - fn saturating_add(self, rhs: Self) -> Self { - // defensive-only: since `$max * 2 < $type::max_value()`, this can never overflow. - Self::from_parts(self.0.saturating_add(rhs.0)) - } - fn saturating_sub(self, rhs: Self) -> Self { - Self::from_parts(self.0.saturating_sub(rhs.0)) - } - fn saturating_mul(self, rhs: Self) -> Self { - let a = self.0 as $upper_type; - let b = rhs.0 as $upper_type; - let m = $max as $upper_type; - let parts = a * b / m; - // This will always fit into $type. - Self::from_parts(parts as $type) - } - } - - impl ops::Div for $name { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - let p = self.0; - let q = rhs.0; - Self::from_rational_approximation(p, q) - } - } - - /// Overflow-prune multiplication. - /// - /// tailored to be used with a balance type. - impl ops::Mul for $name - where - N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem - + ops::Div + ops::Mul + ops::Add, - { - type Output = N; - fn mul(self, b: N) -> Self::Output { - let maximum: N = $max.into(); - let upper_max: $upper_type = $max.into(); - let part: N = self.0.into(); - - let rem_multiplied_divided = { - let rem = b.clone().rem(maximum.clone()); - - // `rem_sized` is inferior to $max, thus it fits into $type. This is assured by - // a test. - let rem_sized = rem.saturated_into::<$type>(); - - // `self` and `rem_sized` are inferior to $max, thus the product is less than - // $max^2 and fits into $upper_type. This is assured by a test. - let rem_multiplied_upper = rem_sized as $upper_type * self.0 as $upper_type; - - // `rem_multiplied_upper` is less than $max^2 therefore divided by $max it fits - // in $type. remember that $type always fits $max. - let mut rem_multiplied_divided_sized = - (rem_multiplied_upper / upper_max) as $type; - // fix a tiny rounding error - if rem_multiplied_upper % upper_max > upper_max / 2 { - rem_multiplied_divided_sized += 1; - } - - // `rem_multiplied_divided_sized` is inferior to b, thus it can be converted - // back to N type - rem_multiplied_divided_sized.into() - }; - - (b / maximum) * part + rem_multiplied_divided - } - } - - impl codec::CompactAs for $name { - type As = $type; - fn encode_as(&self) -> &$type { - &self.0 - } - fn decode_from(x: $type) -> Self { - Self(x) - } - } - - impl From> for $name { - fn from(x: codec::Compact<$name>) -> Self { - x.0 - } - } - - #[cfg(test)] - mod $test_mod { - use codec::{Encode, Decode}; - use super::{$name, Saturating}; - use crate::{assert_eq_error_rate, traits::Zero}; - - - #[test] - fn macro_expanded_correctly() { - // needed for the `from_percent` to work. - assert!($max >= 100); - assert!($max % 100 == 0); - - // needed for `from_rational_approximation` - assert!(2 * $max < <$type>::max_value()); - assert!(($max as $upper_type) < <$upper_type>::max_value()); - - // for something like percent they can be the same. - assert!((<$type>::max_value() as $upper_type) <= <$upper_type>::max_value()); - assert!(($max as $upper_type).checked_mul($max.into()).is_some()); - } - - #[derive(Encode, Decode, PartialEq, Eq, Debug)] - struct WithCompact { - data: T, - } - - #[test] - fn has_compact() { - let data = WithCompact { data: $name(1) }; - let encoded = data.encode(); - assert_eq!(data, WithCompact::<$name>::decode(&mut &encoded[..]).unwrap()); - } - - #[test] - fn compact_encoding() { - let tests = [ - // assume all per_things have the size u8 at least. - (0 as $type, 1usize), - (1 as $type, 1usize), - (63, 1), - (64, 2), - (65, 2), - (<$type>::max_value(), <$type>::max_value().encode().len() + 1) - ]; - for &(n, l) in &tests { - let compact: crate::codec::Compact<$name> = $name(n).into(); - let encoded = compact.encode(); - assert_eq!(encoded.len(), l); - let decoded = >::decode(&mut & encoded[..]) - .unwrap(); - let per_thingy: $name = decoded.into(); - assert_eq!(per_thingy, $name(n)); - } - } - - #[test] - fn per_thing_api_works() { - // some really basic stuff - assert_eq!($name::zero(), $name::from_parts(Zero::zero())); - assert_eq!($name::one(), $name::from_parts($max)); - assert_eq!($name::accuracy(), $max); - assert_eq!($name::from_percent(0), $name::from_parts(Zero::zero())); - assert_eq!($name::from_percent(10), $name::from_parts($max / 10)); - assert_eq!($name::from_percent(100), $name::from_parts($max)); - } - - macro_rules! per_thing_mul_test { - ($num_type:tt) => { - // multiplication from all sort of from_percent - assert_eq!( - $name::from_percent(100) * $num_type::max_value(), - $num_type::max_value() - ); - assert_eq_error_rate!( - $name::from_percent(99) * $num_type::max_value(), - ((Into::::into($num_type::max_value()) * 99u32) / 100u32).as_u128() as $num_type, - 1, - ); - assert_eq!( - $name::from_percent(50) * $num_type::max_value(), - $num_type::max_value() / 2, - ); - assert_eq_error_rate!( - $name::from_percent(1) * $num_type::max_value(), - $num_type::max_value() / 100, - 1, - ); - assert_eq!($name::from_percent(0) * $num_type::max_value(), 0); - - // // multiplication with bounds - assert_eq!($name::one() * $num_type::max_value(), $num_type::max_value()); - assert_eq!($name::zero() * $num_type::max_value(), 0); - } - } - - #[test] - fn per_thing_mul_works() { - use primitive_types::U256; - - // accuracy test - assert_eq!($name::from_rational_approximation(1 as $type, 3) * 30 as $type, 10); - - $(per_thing_mul_test!($test_units);)* - } - - #[test] - fn per_thing_mul_rounds_to_nearest_number() { - assert_eq!($name::from_percent(33) * 10u64, 3); - assert_eq!($name::from_percent(34) * 10u64, 3); - assert_eq!($name::from_percent(35) * 10u64, 3); - assert_eq!($name::from_percent(36) * 10u64, 4); - assert_eq!($name::from_percent(36) * 10u64, 4); - } - - #[test] - fn per_thing_multiplication_with_large_number() { - use primitive_types::U256; - let max_minus_one = $max - 1; - assert_eq_error_rate!( - $name::from_parts(max_minus_one) * std::u128::MAX, - ((Into::::into(std::u128::MAX) * max_minus_one) / $max).as_u128(), - 1, - ); - } - - macro_rules! per_thing_from_rationale_approx_test { - ($num_type:tt) => { - // within accuracy boundary - assert_eq!( - $name::from_rational_approximation(1 as $num_type, 0), - $name::one(), - ); - assert_eq!( - $name::from_rational_approximation(1 as $num_type, 1), - $name::one(), - ); - assert_eq_error_rate!( - $name::from_rational_approximation(1 as $num_type, 3).0, - $name::from_parts($max / 3).0, - 2 - ); - assert_eq!( - $name::from_rational_approximation(1 as $num_type, 10), - $name::from_percent(10), - ); - assert_eq!( - $name::from_rational_approximation(1 as $num_type, 4), - $name::from_percent(25), - ); - assert_eq!( - $name::from_rational_approximation(1 as $num_type, 4), - $name::from_rational_approximation(2 as $num_type, 8), - ); - // no accurate anymore but won't overflow. - assert_eq!( - $name::from_rational_approximation( - $num_type::max_value() - 1, - $num_type::max_value() - ), - $name::one(), - ); - assert_eq_error_rate!( - $name::from_rational_approximation( - $num_type::max_value() / 3, - $num_type::max_value() - ).0, - $name::from_parts($max / 3).0, - 2 - ); - assert_eq!( - $name::from_rational_approximation(1, $num_type::max_value()), - $name::zero(), - ); - }; - } - - #[test] - fn per_thing_from_rationale_approx_works() { - // This is just to make sure something like Percent which _might_ get built from a - // u8 does not overflow in the context of this test. - let max_value = $max as $upper_type; - // almost at the edge - assert_eq!( - $name::from_rational_approximation($max - 1, $max + 1), - $name::from_parts($max - 2), - ); - assert_eq!( - $name::from_rational_approximation(1, $max-1), - $name::from_parts(1), - ); - assert_eq!( - $name::from_rational_approximation(1, $max), - $name::from_parts(1), - ); - assert_eq!( - $name::from_rational_approximation(2, 2 * $max - 1), - $name::from_parts(1), - ); - assert_eq!( - $name::from_rational_approximation(1, $max+1), - $name::zero(), - ); - assert_eq!( - $name::from_rational_approximation(3 * max_value / 2, 3 * max_value), - $name::from_percent(50), - ); - $(per_thing_from_rationale_approx_test!($test_units);)* - } - - #[test] - fn per_things_mul_operates_in_output_type() { - // assert_eq!($name::from_percent(50) * 100u32, 50u32); - assert_eq!($name::from_percent(50) * 100u64, 50u64); - assert_eq!($name::from_percent(50) * 100u128, 50u128); - } - - #[test] - fn per_thing_saturating_op_works() { - assert_eq!( - $name::from_percent(50).saturating_add($name::from_percent(40)), - $name::from_percent(90) - ); - assert_eq!( - $name::from_percent(50).saturating_add($name::from_percent(50)), - $name::from_percent(100) - ); - assert_eq!( - $name::from_percent(60).saturating_add($name::from_percent(50)), - $name::from_percent(100) - ); - - assert_eq!( - $name::from_percent(60).saturating_sub($name::from_percent(50)), - $name::from_percent(10) - ); - assert_eq!( - $name::from_percent(60).saturating_sub($name::from_percent(60)), - $name::from_percent(0) - ); - assert_eq!( - $name::from_percent(60).saturating_sub($name::from_percent(70)), - $name::from_percent(0) - ); - - assert_eq!( - $name::from_percent(50).saturating_mul($name::from_percent(50)), - $name::from_percent(25) - ); - assert_eq!( - $name::from_percent(20).saturating_mul($name::from_percent(20)), - $name::from_percent(4) - ); - assert_eq!( - $name::from_percent(10).saturating_mul($name::from_percent(10)), - $name::from_percent(1) - ); - } - - #[test] - fn per_thing_square_works() { - assert_eq!($name::from_percent(100).square(), $name::from_percent(100)); - assert_eq!($name::from_percent(50).square(), $name::from_percent(25)); - assert_eq!($name::from_percent(10).square(), $name::from_percent(1)); - assert_eq!( - $name::from_percent(2).square(), - $name::from_parts((4 * ($max as $upper_type) / 100 / 100) as $type) - ); - } - - #[test] - fn per_things_div_works() { - // normal - assert_eq!($name::from_percent(10) / $name::from_percent(20), - $name::from_percent(50) - ); - assert_eq!($name::from_percent(10) / $name::from_percent(10), - $name::from_percent(100) - ); - assert_eq!($name::from_percent(10) / $name::from_percent(0), - $name::from_percent(100) - ); - - // will not overflow - assert_eq!($name::from_percent(10) / $name::from_percent(5), - $name::from_percent(100) - ); - assert_eq!($name::from_percent(100) / $name::from_percent(50), - $name::from_percent(100) - ); - } - } - }; -} - -implement_per_thing!( - Percent, - test_per_cent, - [u32, u64, u128], - 100u8, - u8, - u16, - "_Percent_", -); -implement_per_thing!( - Permill, - test_permill, - [u32, u64, u128], - 1_000_000u32, - u32, - u64, - "_Parts per Million_", -); -implement_per_thing!( - Perbill, - test_perbill, - [u32, u64, u128], - 1_000_000_000u32, - u32, - u64, - "_Parts per Billion_", -); -implement_per_thing!( - Perquintill, - test_perquintill, - [u64, u128], - 1_000_000_000_000_000_000u64, - u64, - u128, - "_Parts per Quintillion_", -); - -/// An unsigned fixed point number. Can hold any value in the range [-9_223_372_036, 9_223_372_036] -/// with fixed point accuracy of one billion. -#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] -pub struct Fixed64(i64); - -/// The accuracy of the `Fixed64` type. -const DIV: i64 = 1_000_000_000; - -impl Fixed64 { - /// creates self from a natural number. - /// - /// Note that this might be lossy. - pub fn from_natural(int: i64) -> Self { - Self(int.saturating_mul(DIV)) - } - - /// Return the accuracy of the type. Given that this function returns the value `X`, it means - /// that an instance composed of `X` parts (`Fixed64::from_parts(X)`) is equal to `1`. - pub fn accuracy() -> i64 { - DIV - } - - /// Raw constructor. Equal to `parts / 1_000_000_000`. - pub fn from_parts(parts: i64) -> Self { - Self(parts) - } - - /// creates self from a rational number. Equal to `n/d`. - /// - /// Note that this might be lossy. - pub fn from_rational(n: i64, d: u64) -> Self { - Self( - ((n as i128).saturating_mul(DIV as i128) / (d as i128).max(1)) - .try_into() - .unwrap_or(Bounded::max_value()) - ) - } - - /// Performs a saturated multiply and accumulate by unsigned number. - /// - /// Returns a saturated `int + (self * int)`. - pub fn saturated_multiply_accumulate(&self, int: N) -> N - where - N: TryFrom + From + UniqueSaturatedInto + Bounded + Clone + Saturating + - ops::Rem + ops::Div + ops::Mul + - ops::Add, - { - let div = DIV as u64; - let positive = self.0 > 0; - // safe to convert as absolute value. - let parts = self.0.checked_abs().map(|v| v as u64).unwrap_or(i64::max_value() as u64 + 1); - - - // will always fit. - let natural_parts = parts / div; - // might saturate. - let natural_parts: N = natural_parts.saturated_into(); - // fractional parts can always fit into u32. - let perbill_parts = (parts % div) as u32; - - let n = int.clone().saturating_mul(natural_parts); - let p = Perbill::from_parts(perbill_parts) * int.clone(); - - // everything that needs to be either added or subtracted from the original weight. - let excess = n.saturating_add(p); - - if positive { - int.saturating_add(excess) - } else { - int.saturating_sub(excess) - } - } -} - -impl Saturating for Fixed64 { - fn saturating_add(self, rhs: Self) -> Self { - Self(self.0.saturating_add(rhs.0)) - } - fn saturating_mul(self, rhs: Self) -> Self { - Self(self.0.saturating_mul(rhs.0) / DIV) - } - fn saturating_sub(self, rhs: Self) -> Self { - Self(self.0.saturating_sub(rhs.0)) - } -} - -/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait -/// for safe addition. -impl ops::Add for Fixed64 { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - Self(self.0 + rhs.0) - } -} - -/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait -/// for safe subtraction. -impl ops::Sub for Fixed64 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) - } -} - -impl CheckedSub for Fixed64 { - fn checked_sub(&self, rhs: &Self) -> Option { - if let Some(v) = self.0.checked_sub(rhs.0) { - Some(Self(v)) - } else { - None - } - } -} - -impl CheckedAdd for Fixed64 { - fn checked_add(&self, rhs: &Self) -> Option { - if let Some(v) = self.0.checked_add(rhs.0) { - Some(Self(v)) - } else { - None - } - } -} - -#[cfg(feature = "std")] -impl rstd::fmt::Debug for Fixed64 { - fn fmt(&self, f: &mut rstd::fmt::Formatter<'_>) -> rstd::fmt::Result { - write!(f, "Fixed64({},{})", self.0 / DIV, (self.0 % DIV) / 1000) - } -} - -/// Infinite precision unsigned integer for substrate runtime. -pub mod biguint { - use super::Zero; - use rstd::{cmp::Ordering, ops, prelude::*, cell::RefCell, convert::TryFrom}; - - // A sensible value for this would be half of the dword size of the host machine. Since the - // runtime is compiled to 32bit webassembly, using 32 and 64 for single and double respectively - // should yield the most performance. TODO #3745 we could benchmark this and verify. - /// Representation of a single limb. - pub type Single = u32; - /// Representation of two limbs. - pub type Double = u64; - /// Difference in the number of bits of [`Single`] and [`Double`]. - const SHIFT: usize = 32; - /// short form of _Base_. Analogous to the value 10 in base-10 decimal numbers. - const B: Double = Single::max_value() as Double + 1; - - /// Splits a [`Double`] limb number into a tuple of two [`Single`] limb numbers. - pub fn split(a: Double) -> (Single, Single) { - let al = a as Single; - let ah = (a >> SHIFT) as Single; - (ah, al) - } - - /// Assumed as a given primitive. - /// - /// Multiplication of two singles, which at most yields 1 double. - pub fn mul_single(a: Single, b: Single) -> Double { - let a: Double = a.into(); - let b: Double = b.into(); - let r = a * b; - r - } - - /// Assumed as a given primitive. - /// - /// Addition of two singles, which at most takes a single limb of result and a carry, - /// returned as a tuple respectively. - pub fn add_single(a: Single, b: Single) -> (Single, Single) { - let a: Double = a.into(); - let b: Double = b.into(); - let q = a + b; - let (carry, r) = split(q); - (r, carry) - } - - /// Assumed as a given primitive. - /// - /// Division of double by a single limb. Always returns a double limb of quotient and a single - /// limb of remainder. - fn div_single(a: Double, b: Single) -> (Double, Single) { - let b: Double = b.into(); - let q = a / b; - let r = a % b; - // both conversions are trivially safe. - (q, r as Single) - } - - /// Simple wrapper around an infinitely large integer, represented as limbs of [`Single`]. - #[derive(Clone, Default)] - pub struct BigUint { - /// digits (limbs) of this number (sorted as msb -> lsd). - pub(crate) digits: Vec, - } - - impl BigUint { - /// Create a new instance with `size` limbs. This prevents any number with zero limbs to be - /// created. - /// - /// The behavior of the type is undefined with zero limbs. - pub fn with_capacity(size: usize) -> Self { - Self { digits: vec![0; size.max(1)] } - } - - /// Raw constructor from custom limbs. If `limbs` is empty, `Zero::zero()` implementation is - /// used. - pub fn from_limbs(limbs: &[Single]) -> Self { - if limbs.len() > 0 { - Self { digits: limbs.to_vec() } - } else { - Zero::zero() - } - } - - /// Number of limbs. - pub fn len(&self) -> usize { self.digits.len() } - - /// A naive getter for limb at `index`. Note that the order is lsb -> msb. - /// - /// #### Panics - /// - /// This panics if index is out of range. - pub fn get(&self, index: usize) -> Single { - self.digits[self.len() - 1 - index] - } - - /// A naive getter for limb at `index`. Note that the order is lsb -> msb. - pub fn checked_get(&self, index: usize) -> Option { - if let Some(i) = self.len().checked_sub(1) { - if let Some(j) = i.checked_sub(index) { - return self.digits.get(j).cloned(); - } - } - return None; - } - - /// A naive setter for limb at `index`. Note that the order is lsb -> msb. - /// - /// #### Panics - /// - /// This panics if index is out of range. - pub fn set(&mut self, index: usize, value: Single) { - let len = self.digits.len(); - self.digits[len - 1 - index] = value; - } - - /// returns the least significant limb of the number. - /// - /// #### Panics - /// - /// While the constructor of the type prevents this, this can panic if `self` has no digits. - pub fn lsb(&self) -> Single { - self.digits[self.len() - 1] - } - - /// returns the most significant limb of the number. - /// - /// #### Panics - /// - /// While the constructor of the type prevents this, this can panic if `self` has no digits. - pub fn msb(&self) -> Single { - self.digits[0] - } - - /// Strips zeros from the left side of `self`, if any. - pub fn lstrip(&mut self) { - // by definition, a big-int number should never have leading zero limbs. This function - // has the ability to cause this. There is nothing to do if the number already has 1 - // limb only. call it a day and return. - if self.len().is_zero() { return; } - let mut index = 0; - for elem in self.digits.iter() { - if *elem != 0 { break } else { index += 1 } - } - if index > 0 { - self.digits = self.digits[index..].to_vec() - } - } - - /// Zero-pad `self` from left to reach `size` limbs. Will not make any difference if `self` - /// is already bigger than `size` limbs. - pub fn lpad(&mut self, size: usize) { - let n = self.len(); - if n >= size { return; } - let pad = size - n; - let mut new_digits = (0..pad).map(|_| 0).collect::>(); - new_digits.extend(self.digits.iter()); - self.digits = new_digits; - } - - /// Adds `self` with `other`. self and other do not have to have any particular size. Given - /// that the `n = max{size(self), size(other)}`, it will produce a number with `n + 1` - /// limbs. - /// - /// This function does not strip the output and returns the original allocated `n + 1` - /// limbs. The caller may strip the output if desired. - /// - /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. - pub fn add(self, other: &Self) -> Self { - let n = self.len().max(other.len()); - let mut k: Double = 0; - let mut w = Self::with_capacity(n + 1); - - for j in 0..n { - let u = Double::from(self.checked_get(j).unwrap_or(0)); - let v = Double::from(other.checked_get(j).unwrap_or(0)); - let s = u + v + k; - w.set(j, (s % B) as Single); - k = s / B; - } - // k is always 0 or 1. - w.set(n, k as Single); - w - } - - /// Subtracts `other` from `self`. self and other do not have to have any particular size. - /// Given that the `n = max{size(self), size(other)}`, it will produce a number of size `n`. - /// - /// If `other` is bigger than `self`, `Err(B - borrow)` is returned. - /// - /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. - pub fn sub(self, other: &Self) -> Result { - let n = self.len().max(other.len()); - let mut k = 0; - let mut w = Self::with_capacity(n); - for j in 0..n { - let s = { - let u = Double::from(self.checked_get(j).unwrap_or(0)); - let v = Double::from(other.checked_get(j).unwrap_or(0)); - let mut needs_borrow = false; - let mut t = 0; - - if let Some(v) = u.checked_sub(v) { - if let Some(v2) = v.checked_sub(k) { - t = v2 % B; - k = 0; - } else { - needs_borrow = true; - } - } else { - needs_borrow = true; - } - if needs_borrow { - t = u + B - v - k; - k = 1; - } - t - }; - // PROOF: t either comes from `v2 % B`, or from `u + B - v - k`. The former is - // trivial. The latter will not overflow this branch will only happen if the sum of - // `u - v - k` part has been negative, hence `u + B - v - k < b`. - w.set(j, s as Single); - } - - if k.is_zero() { - Ok(w) - } else { - Err(w) - } - } - - /// Multiplies n-limb number `self` with m-limb number `other`. - /// - /// The resulting number will always have `n + m` limbs. - /// - /// This function does not strip the output and returns the original allocated `n + m` - /// limbs. The caller may strip the output if desired. - /// - /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. - pub fn mul(self, other: &Self) -> Self { - let n = self.len(); - let m = other.len(); - let mut w = Self::with_capacity(m + n); - - for j in 0..n { - if self.get(j) == 0 { - // Note: `with_capacity` allocates with 0. Explicitly set j + m to zero if - // otherwise. - continue; - } - - let mut k = 0; - for i in 0..m { - // PROOF: (B−1) × (B−1) + (B−1) + (B−1) = B^2 −1 < B^2. addition is safe. - let t = - mul_single(self.get(j), other.get(i)) - + Double::from(w.get(i + j)) - + Double::from(k); - w.set(i + j, (t % B) as Single); - // PROOF: (B^2 - 1) / B < B. conversion is safe. - k = (t / B) as Single; - } - w.set(j + m, k); - } - w - } - - /// Divides `self` by a single limb `other`. This can be used in cases where the original - /// division cannot work due to the divisor (`other`) being just one limb. - /// - /// Invariant: `other` cannot be zero. - pub fn div_unit(self, mut other: Single) -> Self { - other = other.max(1); - let n = self.len(); - let mut out = Self::with_capacity(n); - let mut r: Single = 0; - // PROOF: (B-1) * B + (B-1) still fits in double - let with_r = |x: Double, r: Single| { r as Double * B + x }; - for d in (0..=n-1).rev() { - let (q, rr) = div_single(with_r(self.get(d).into(), r), other) ; - out.set(d, q as Single); - r = rr; - } - out - } - - /// Divides an `n + m` limb self by a `n` limb `other`. The result is a `m + 1` limb - /// quotient and a `n` limb remainder, if enabled by passing `true` in `rem` argument, both - /// in the form of an option's `Ok`. - /// - /// - requires `other` to be stripped and have no leading zeros. - /// - requires `self` to be stripped and have no leading zeros. - /// - requires `other` to have at least two limbs. - /// - requires `self` to have a greater length compared to `other`. - /// - /// All arguments are examined without being stripped for the above conditions. If any of - /// the above fails, `None` is returned.` - /// - /// Taken from "The Art of Computer Programming" by D.E. Knuth, vol 2, chapter 4. - pub fn div(self, other: &Self, rem: bool) -> Option<(Self, Self)> { - if other.len() <= 1 - || other.msb() == 0 - || self.msb() == 0 - || self.len() <= other.len() - { - return None - } - let n = other.len(); - let m = self.len() - n; - - let mut q = Self::with_capacity(m + 1); - let mut r = Self::with_capacity(n); - - // PROOF: 0 <= normalizer_bits < SHIFT 0 <= normalizer < B. all conversions are - // safe. - let normalizer_bits = other.msb().leading_zeros() as Single; - let normalizer = (2 as Single).pow(normalizer_bits as u32) as Single; - - // step D1. - let mut self_norm = self.mul(&Self::from(normalizer)); - let mut other_norm = other.clone().mul(&Self::from(normalizer)); - - // defensive only; the mul implementation should always create this. - self_norm.lpad(n + m + 1); - other_norm.lstrip(); - - // step D2. - for j in (0..=m).rev() { - // step D3.0 Find an estimate of q[j], named qhat. - let (qhat, rhat) = { - // PROOF: this always fits into `Double`. In the context of Single = u8, and - // Double = u16, think of 255 * 256 + 255 which is just u16::max_value(). - let dividend = - Double::from(self_norm.get(j + n)) - * B - + Double::from(self_norm.get(j + n - 1)); - let divisor = other_norm.get(n - 1); - div_single(dividend, divisor.into()) - }; - - // D3.1 test qhat - // replace qhat and rhat with RefCells. This helps share state with the closure - let qhat = RefCell::new(qhat); - let rhat = RefCell::new(rhat as Double); - - let test = || { - // decrease qhat if it is bigger than the base (B) - let qhat_local = *qhat.borrow(); - let rhat_local = *rhat.borrow(); - let predicate_1 = qhat_local >= B; - let predicate_2 = { - let lhs = qhat_local * other_norm.get(n - 2) as Double; - let rhs = B * rhat_local + self_norm.get(j + n - 2) as Double; - lhs > rhs - }; - if predicate_1 || predicate_2 { - *qhat.borrow_mut() -= 1; - *rhat.borrow_mut() += other_norm.get(n - 1) as Double; - true - } else { - false - } - }; - - test(); - while (*rhat.borrow() as Double) < B { - if !test() { break; } - } - - let qhat = qhat.into_inner(); - // we don't need rhat anymore. just let it go out of scope when it does. - - // step D4 - let lhs = Self { digits: (j..=j+n).rev().map(|d| self_norm.get(d)).collect() }; - let rhs = other_norm.clone().mul(&Self::from(qhat)); - - let maybe_sub = lhs.sub(&rhs); - let mut negative = false; - let sub = match maybe_sub { - Ok(t) => t, - Err(t) => { negative = true; t } - }; - (j..=j+n).for_each(|d| { self_norm.set(d, sub.get(d - j)); }); - - // step D5 - // PROOF: the `test()` specifically decreases qhat until it is below `B`. conversion - // is safe. - q.set(j, qhat as Single); - - // step D6: add back if negative happened. - if negative { - q.set(j, q.get(j) - 1); - let u = Self { digits: (j..=j+n).rev().map(|d| self_norm.get(d)).collect() }; - let r = other_norm.clone().add(&u); - (j..=j+n).rev().for_each(|d| { self_norm.set(d, r.get(d - j)); }) - } - } - - // if requested, calculate remainder. - if rem { - // undo the normalization. - if normalizer_bits > 0 { - let s = SHIFT as u32; - let nb = normalizer_bits; - for d in 0..n-1 { - let v = self_norm.get(d) >> nb - | self_norm.get(d + 1).overflowing_shl(s - nb).0; - r.set(d, v); - } - r.set(n - 1, self_norm.get(n - 1) >> normalizer_bits); - } else { - r = self_norm; - } - } - - Some((q, r)) - } - } - - #[cfg(feature = "std")] - impl rstd::fmt::Debug for BigUint { - fn fmt(&self, f: &mut rstd::fmt::Formatter<'_>) -> rstd::fmt::Result { - write!( - f, - "BigUint {{ {:?} ({:?})}}", - self.digits, - u128::try_from(self.clone()).unwrap_or_else(|_| 0), - ) - } - } - - impl PartialEq for BigUint { - fn eq(&self, other: &Self) -> bool { - // sadly, we have to reallocate here as strip mutably uses self. - let mut lhs = self.clone(); - let mut rhs = other.clone(); - lhs.lstrip(); - rhs.lstrip(); - lhs.digits.eq(&rhs.digits) - } - } - - impl Eq for BigUint {} - - impl Ord for BigUint { - fn cmp(&self, other: &Self) -> Ordering { - let lhs_first = self.digits.iter().position(|&e| e != 0); - let rhs_first = other.digits.iter().position(|&e| e != 0); - - match (lhs_first, rhs_first) { - // edge cases that should not happen. This basically means that one or both were - // zero. - (None, None) => Ordering::Equal, - (Some(_), None) => Ordering::Greater, - (None, Some(_)) => Ordering::Less, - (Some(lhs_idx), Some(rhs_idx)) => { - let lhs = &self.digits[lhs_idx..]; - let rhs = &other.digits[rhs_idx..]; - let len_cmp = lhs.len().cmp(&rhs.len()); - match len_cmp { - Ordering::Equal => lhs.cmp(rhs), - _ => len_cmp, - } - } - } - } - } - - impl PartialOrd for BigUint { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - impl ops::Add for BigUint { - type Output = Self; - fn add(self, rhs: Self) -> Self::Output { - self.add(&rhs) - } - } - - impl ops::Sub for BigUint { - type Output = Self; - fn sub(self, rhs: Self) -> Self::Output { - self.sub(&rhs).unwrap_or_else(|e| e) - } - } - - impl ops::Mul for BigUint { - type Output = Self; - fn mul(self, rhs: Self) -> Self::Output { - self.mul(&rhs) - } - } - - impl Zero for BigUint { - fn zero() -> Self { - Self { digits: vec![Zero::zero()] } - } - - fn is_zero(&self) -> bool { - self.digits.iter().all(|d| d.is_zero()) - } - } - - macro_rules! impl_try_from_number_for { - ($([$type:ty, $len:expr]),+) => { - $( - impl TryFrom for $type { - type Error = &'static str; - fn try_from(mut value: BigUint) -> Result<$type, Self::Error> { - value.lstrip(); - let error_message = concat!("cannot fit a number into ", stringify!($type)); - if value.len() * SHIFT > $len { - Err(error_message) - } else { - let mut acc: $type = Zero::zero(); - for (i, d) in value.digits.iter().rev().cloned().enumerate() { - let d: $type = d.into(); - acc += d << (SHIFT * i); - } - Ok(acc) - } - } - } - )* - }; - } - // can only be implemented for sizes bigger than two limb. - impl_try_from_number_for!([u128, 128], [u64, 64]); - - macro_rules! impl_from_for_smaller_than_word { - ($($type:ty),+) => { - $(impl From<$type> for BigUint { - fn from(a: $type) -> Self { - Self { digits: vec! [a.into()] } - } - })* - } - } - impl_from_for_smaller_than_word!(u8, u16, Single); - - impl From for BigUint { - fn from(a: Double) -> Self { - let (ah, al) = split(a); - Self { digits: vec![ah, al] } - } - } - - #[cfg(test)] - pub mod tests_biguint { - use super::*; - use rand::Rng; - #[cfg(feature = "bench")] - use test::Bencher; - - // TODO move into a proper fuzzer #3745 - const FUZZ_COUNT: usize = 100_000; - - pub fn random_big_uint(size: usize) -> BigUint { - let mut rng = rand::thread_rng(); - let digits = (0..size).map(|_| rng.gen_range(0, Single::max_value())).collect(); - BigUint { digits } - } - - fn run_with_data_set( - count: usize, - limbs_ub_1: usize, - limbs_ub_2: usize, - exact: bool, - assertion: F, - ) where - F: Fn(BigUint, BigUint) -> () - { - let mut rng = rand::thread_rng(); - for _ in 0..count { - let digits_len_1 = if exact { limbs_ub_1 } else { rng.gen_range(1, limbs_ub_1) }; - let digits_len_2 = if exact { limbs_ub_2 } else { rng.gen_range(1, limbs_ub_2) }; - - let u = random_big_uint(digits_len_1); - let v = random_big_uint(digits_len_2); - assertion(u, v); - } - } - - fn with_limbs(n: usize) -> BigUint { - BigUint { digits: vec![1; n] } - } - - #[test] - fn split_works() { - let a = SHIFT / 2; - let b = SHIFT * 3 / 2; - let num: Double = 1 << a | 1 << b; - // example when `Single = u8` - // assert_eq!(num, 0b_0001_0000_0001_0000) - assert_eq!(split(num), (1 << a, 1 << a)); - } - - #[test] - fn strip_works() { - let mut a = BigUint::from_limbs(&[0, 1, 0]); - a.lstrip(); - assert_eq!(a, BigUint { digits: vec![1, 0] }); - - let mut a = BigUint::from_limbs(&[0, 0, 1]); - a.lstrip(); - assert_eq!(a, BigUint { digits: vec![1] }); - - let mut a = BigUint::from_limbs(&[0, 0]); - a.lstrip(); - assert_eq!(a, BigUint { digits: vec![0] }); - - let mut a = BigUint::from_limbs(&[0, 0, 0]); - a.lstrip(); - assert_eq!(a, BigUint { digits: vec![0] }); - } - - #[test] - fn lpad_works() { - let mut a = BigUint::from_limbs(&[0, 1, 0]); - a.lpad(2); - assert_eq!(a.digits, vec![0, 1, 0]); - - let mut a = BigUint::from_limbs(&[0, 1, 0]); - a.lpad(3); - assert_eq!(a.digits, vec![0, 1, 0]); - - let mut a = BigUint::from_limbs(&[0, 1, 0]); - a.lpad(4); - assert_eq!(a.digits, vec![0, 0, 1, 0]); - } - - #[test] - fn equality_works() { - assert_eq!( - BigUint { digits: vec![1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, - true, - ); - assert_eq!( - BigUint { digits: vec![3, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, - false, - ); - assert_eq!( - BigUint { digits: vec![0, 1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }, - true, - ); - } - - #[test] - fn ordering_works() { - assert!(BigUint { digits: vec![0] } < BigUint { digits: vec![1] }); - assert!(BigUint { digits: vec![0] } == BigUint { digits: vec![0] }); - assert!(BigUint { digits: vec![] } == BigUint { digits: vec![0] }); - assert!(BigUint { digits: vec![] } == BigUint { digits: vec![] }); - assert!(BigUint { digits: vec![] } < BigUint { digits: vec![1] }); - - assert!(BigUint { digits: vec![1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }); - assert!(BigUint { digits: vec![0, 1, 2, 3] } == BigUint { digits: vec![1, 2, 3] }); - - assert!(BigUint { digits: vec![1, 2, 4] } > BigUint { digits: vec![1, 2, 3] }); - assert!(BigUint { digits: vec![0, 1, 2, 4] } > BigUint { digits: vec![1, 2, 3] }); - assert!(BigUint { digits: vec![1, 2, 1, 0] } > BigUint { digits: vec![1, 2, 3] }); - - assert!(BigUint { digits: vec![0, 1, 2, 1] } < BigUint { digits: vec![1, 2, 3] }); - } - - #[test] - fn basic_random_ord_eq_works() { - type S = u128; - run_with_data_set(FUZZ_COUNT, 4, 4, false, |u, v| { - let ue = S::try_from(u.clone()).unwrap(); - let ve = S::try_from(v.clone()).unwrap(); - assert_eq!(u.cmp(&v), ue.cmp(&ve)); - assert_eq!(u.eq(&v), ue.eq(&ve)); - }) - } - - #[test] - fn can_try_build_numbers_from_types() { - use rstd::convert::TryFrom; - assert_eq!(u64::try_from(with_limbs(1)).unwrap(), 1); - assert_eq!(u64::try_from(with_limbs(2)).unwrap(), u32::max_value() as u64 + 2); - assert_eq!( - u64::try_from(with_limbs(3)).unwrap_err(), - "cannot fit a number into u64", - ); - assert_eq!( - u128::try_from(with_limbs(3)).unwrap(), - u32::max_value() as u128 + u64::max_value() as u128 + 3 - ); - } - - #[test] - fn zero_works() { - assert_eq!(BigUint::zero(), BigUint { digits: vec![0] }); - assert_eq!(BigUint { digits: vec![0, 1, 0] }.is_zero(), false); - assert_eq!(BigUint { digits: vec![0, 0, 0] }.is_zero(), true); - - let a = BigUint::zero(); - let b = BigUint::zero(); - let c = a * b; - assert_eq!(c.digits, vec![0, 0]); - } - - #[test] - fn basic_random_add_works() { - type S = u128; - run_with_data_set(FUZZ_COUNT, 3, 3, false, |u, v| { - let expected = S::try_from(u.clone()).unwrap() + S::try_from(v.clone()).unwrap(); - let t = u.clone().add(&v); - assert_eq!( - S::try_from(t.clone()).unwrap(), expected, - "{:?} + {:?} ===> {:?} != {:?}", u, v, t, expected, - ); - }) - } - - #[test] - fn sub_negative_works() { - assert_eq!( - BigUint::from(10 as Single).sub(&BigUint::from(5 as Single)).unwrap(), - BigUint::from(5 as Single) - ); - assert_eq!( - BigUint::from(10 as Single).sub(&BigUint::from(10 as Single)).unwrap(), - BigUint::from(0 as Single) - ); - assert_eq!( - BigUint::from(10 as Single).sub(&BigUint::from(13 as Single)).unwrap_err(), - BigUint::from((B - 3) as Single), - ); - } - - #[test] - fn basic_random_sub_works() { - type S = u128; - run_with_data_set(FUZZ_COUNT, 4, 4, false, |u, v| { - let expected = S::try_from(u.clone()).unwrap() - .checked_sub(S::try_from(v.clone()).unwrap()); - let t = u.clone().sub(&v); - if expected.is_none() { - assert!(t.is_err()) - } else { - let t = t.unwrap(); - let expected = expected.unwrap(); - assert_eq!( - S::try_from(t.clone()).unwrap(), expected, - "{:?} - {:?} ===> {:?} != {:?}", u, v, t, expected, - ); - } - }) - } - - #[test] - fn basic_random_mul_works() { - type S = u128; - run_with_data_set(FUZZ_COUNT, 2, 2, false, |u, v| { - let expected = S::try_from(u.clone()).unwrap() * S::try_from(v.clone()).unwrap(); - let t = u.clone().mul(&v); - assert_eq!( - S::try_from(t.clone()).unwrap(), expected, - "{:?} * {:?} ===> {:?} != {:?}", u, v, t, expected, - ); - }) - } - - #[test] - fn mul_always_appends_one_digit() { - let a = BigUint::from(10 as Single); - let b = BigUint::from(4 as Single); - assert_eq!(a.len(), 1); - assert_eq!(b.len(), 1); - - let n = a.mul(&b); - - assert_eq!(n.len(), 2); - assert_eq!(n.digits, vec![0, 40]); - } - - #[test] - fn div_conditions_work() { - let a = BigUint { digits: vec![2] }; - let b = BigUint { digits: vec![1, 2] }; - let c = BigUint { digits: vec![1, 1, 2] }; - let d = BigUint { digits: vec![0, 2] }; - let e = BigUint { digits: vec![0, 1, 1, 2] }; - - assert!(a.clone().div(&b, true).is_none()); - assert!(c.clone().div(&a, true).is_none()); - assert!(c.clone().div(&d, true).is_none()); - assert!(e.clone().div(&a, true).is_none()); - - assert!(c.clone().div(&b, true).is_some()); - } - - #[test] - fn div_unit_works() { - let a = BigUint { digits: vec![100] }; - let b = BigUint { digits: vec![1, 100] }; - - assert_eq!(a.clone().div_unit(1), a); - assert_eq!(a.clone().div_unit(0), a); - assert_eq!(a.clone().div_unit(2), BigUint::from(50 as Single)); - assert_eq!(a.clone().div_unit(7), BigUint::from(14 as Single)); - - assert_eq!(b.clone().div_unit(1), b); - assert_eq!(b.clone().div_unit(0), b); - assert_eq!(b.clone().div_unit(2), BigUint::from(((B + 100) / 2) as Single)); - assert_eq!(b.clone().div_unit(7), BigUint::from(((B + 100) / 7) as Single)); - - } - - #[test] - fn basic_random_div_works() { - type S = u128; - run_with_data_set(FUZZ_COUNT, 4, 4, false, |u, v| { - let ue = S::try_from(u.clone()).unwrap(); - let ve = S::try_from(v.clone()).unwrap(); - let (q, r) = (ue / ve, ue % ve); - if let Some((qq, rr)) = u.clone().div(&v, true) { - assert_eq!( - S::try_from(qq.clone()).unwrap(), q, - "{:?} / {:?} ===> {:?} != {:?}", u, v, qq, q, - ); - assert_eq!( - S::try_from(rr.clone()).unwrap(), r, - "{:?} % {:?} ===> {:?} != {:?}", u, v, rr, r, - ); - } else if v.len() == 1 { - let qq = u.clone().div_unit(ve as Single); - assert_eq!( - S::try_from(qq.clone()).unwrap(), q, - "[single] {:?} / {:?} ===> {:?} != {:?}", u, v, qq, q, - ); - } else { - if v.msb() == 0 || v.msb() == 0 || u.len() <= v.len() {} // nada - else { panic!("div returned none for an unexpected reason"); } - } - }) - } - - #[cfg(feature = "bench")] - #[bench] - fn bench_addition_2_digit(bencher: &mut Bencher) { - let a = random_big_uint(2); - let b = random_big_uint(2); - bencher.iter(|| { - let _ = a.clone().add(&b); - }); - } - - #[cfg(feature = "bench")] - #[bench] - fn bench_addition_4_digit(bencher: &mut Bencher) { - let a = random_big_uint(4); - let b = random_big_uint(4); - bencher.iter(|| { - let _ = a.clone().add(&b); - }); - } - - #[cfg(feature = "bench")] - #[bench] - fn bench_subtraction_2_digit(bencher: &mut Bencher) { - let a = random_big_uint(2); - let b = random_big_uint(2); - bencher.iter(|| { - let _ = a.clone().sub(&b); - }); - } - - #[cfg(feature = "bench")] - #[bench] - fn bench_subtraction_4_digit(bencher: &mut Bencher) { - let a = random_big_uint(4); - let b = random_big_uint(4); - bencher.iter(|| { - let _ = a.clone().sub(&b); - }); - } - - #[cfg(feature = "bench")] - #[bench] - fn bench_multiplication_2_digit(bencher: &mut Bencher) { - let a = random_big_uint(2); - let b = random_big_uint(2); - bencher.iter(|| { - let _ = a.clone().mul(&b); - }); - } - - #[cfg(feature = "bench")] - #[bench] - fn bench_multiplication_4_digit(bencher: &mut Bencher) { - let a = random_big_uint(4); - let b = random_big_uint(4); - bencher.iter(|| { - let _ = a.clone().mul(&b); - }); - } - - #[cfg(feature = "bench")] - #[bench] - fn bench_division_4_digit(bencher: &mut Bencher) { - let a = random_big_uint(4); - let b = random_big_uint(2); - bencher.iter(|| { - let _ = a.clone().div(&b, true); - }); - } - } -} - -/// Some helper functions to work with 128bit numbers. Note that the functionality provided here is -/// only sensible to use with 128bit numbers because for smaller sizes, you can always rely on -/// assumptions of a bigger type (u128) being available, or simply create a per-thing and use the -/// multiplication implementation provided there. -pub mod helpers_128bit { - use crate::biguint; - use crate::traits::Zero; - use rstd::{cmp::{min, max}, convert::TryInto}; - - /// Helper gcd function used in Rational128 implementation. - pub fn gcd(a: u128, b: u128) -> u128 { - match ((a, b), (a & 1, b & 1)) { - ((x, y), _) if x == y => y, - ((0, x), _) | ((x, 0), _) => x, - ((x, y), (0, 1)) | ((y, x), (1, 0)) => gcd(x >> 1, y), - ((x, y), (0, 0)) => gcd(x >> 1, y >> 1) << 1, - ((x, y), (1, 1)) => { - let (x, y) = (min(x, y), max(x, y)); - gcd((y - x) >> 1, x) - }, - _ => unreachable!(), - } - } - - /// split a u128 into two u64 limbs - pub fn split(a: u128) -> (u64, u64) { - let al = a as u64; - let ah = (a >> 64) as u64; - (ah, al) - } - - /// Convert a u128 to a u32 based biguint. - pub fn to_big_uint(x: u128) -> biguint::BigUint { - let (xh, xl) = split(x); - let (xhh, xhl) = biguint::split(xh); - let (xlh, xll) = biguint::split(xl); - let mut n = biguint::BigUint::from_limbs(&[xhh, xhl, xlh, xll]); - n.lstrip(); - n - } - - /// Safely and accurately compute `a * b / c`. The approach is: - /// - Simply try `a * b / c`. - /// - Else, convert them both into big numbers and re-try. `Err` is returned if the result - /// cannot be safely casted back to u128. - /// - /// Invariant: c must be greater than or equal to 1. - pub fn multiply_by_rational(a: u128, b: u128, c: u128) -> Result { - if a.is_zero() || b.is_zero() { return Ok(Zero::zero()); } - let c = c.max(1); - - // a and b are interchangeable by definition in this function. It always helps to assume the - // bigger of which is being multiplied by a `0 < b/c < 1`. Hence, a should be the bigger and - // b the smaller one. - let t = a; - let a = a.max(b); - let b = t.min(b); - - if let Some(x) = a.checked_mul(b) { - // This is the safest way to go. Try it. - Ok(x / c) - } else { - let a_num = to_big_uint(a); - let b_num = to_big_uint(b); - let c_num = to_big_uint(c); - - let mut ab = a_num * b_num; - ab.lstrip(); - let mut q = if c_num.len() == 1 { - // PROOF: if `c_num.len() == 1` then `c` fits in one limb. - ab.div_unit(c as biguint::Single) - } else { - // PROOF: both `ab` and `c` cannot have leading zero limbs; if length of `c` is 1, - // the previous branch would handle. Also, if ab for sure has a bigger size than - // c, because `a.checked_mul(b)` has failed, hence ab must be at least one limb - // bigger than c. In this case, returning zero is defensive-only and div should - // always return Some. - let (mut q, r) = ab.div(&c_num, true).unwrap_or((Zero::zero(), Zero::zero())); - let r: u128 = r.try_into() - .expect("reminder of div by c is always less than c; qed"); - if r > (c / 2) { q = q.add(&to_big_uint(1)); } - q - }; - q.lstrip(); - q.try_into().map_err(|_| "result cannot fit in u128") - } - } -} - -/// A wrapper for any rational number with a 128 bit numerator and denominator. -#[derive(Clone, Copy, Default, Eq)] -#[cfg_attr(feature = "std", derive(Debug))] -pub struct Rational128(u128, u128); - -impl Rational128 { - /// Nothing. - pub fn zero() -> Self { - Self(0, 1) - } - - /// If it is zero or not - pub fn is_zero(&self) -> bool { - self.0.is_zero() - } - - /// Build from a raw `n/d`. - pub fn from(n: u128, d: u128) -> Self { - Self(n, d.max(1)) - } - - /// Build from a raw `n/d`. This could lead to / 0 if not properly handled. - pub fn from_unchecked(n: u128, d: u128) -> Self { - Self(n, d) - } - - /// Return the numerator. - pub fn n(&self) -> u128 { - self.0 - } - - /// Return the denominator. - pub fn d(&self) -> u128 { - self.1 - } - - /// Convert `self` to a similar rational number where denominator is the given `den`. - // - /// This only returns if the result is accurate. `Err` is returned if the result cannot be - /// accurately calculated. - pub fn to_den(self, den: u128) -> Result { - if den == self.1 { - Ok(self) - } else { - helpers_128bit::multiply_by_rational(self.0, den, self.1).map(|n| Self(n, den)) - } - } - - /// Get the least common divisor of `self` and `other`. - /// - /// This only returns if the result is accurate. `Err` is returned if the result cannot be - /// accurately calculated. - pub fn lcm(&self, other: &Self) -> Result { - // this should be tested better: two large numbers that are almost the same. - if self.1 == other.1 { return Ok(self.1) } - let g = helpers_128bit::gcd(self.1, other.1); - helpers_128bit::multiply_by_rational(self.1 , other.1, g) - } - - /// A saturating add that assumes `self` and `other` have the same denominator. - pub fn lazy_saturating_add(self, other: Self) -> Self { - if other.is_zero() { - self - } else { - Self(self.0.saturating_add(other.0) ,self.1) - } - } - - /// A saturating subtraction that assumes `self` and `other` have the same denominator. - pub fn lazy_saturating_sub(self, other: Self) -> Self { - if other.is_zero() { - self - } else { - Self(self.0.saturating_sub(other.0) ,self.1) - } - } - - /// Addition. Simply tries to unify the denominators and add the numerators. - /// - /// Overflow might happen during any of the steps. Error is returned in such cases. - pub fn checked_add(self, other: Self) -> Result { - let lcm = self.lcm(&other).map_err(|_| "failed to scale to denominator")?; - let self_scaled = self.to_den(lcm).map_err(|_| "failed to scale to denominator")?; - let other_scaled = other.to_den(lcm).map_err(|_| "failed to scale to denominator")?; - let n = self_scaled.0.checked_add(other_scaled.0) - .ok_or("overflow while adding numerators")?; - Ok(Self(n, self_scaled.1)) - } - - /// Subtraction. Simply tries to unify the denominators and subtract the numerators. - /// - /// Overflow might happen during any of the steps. None is returned in such cases. - pub fn checked_sub(self, other: Self) -> Result { - let lcm = self.lcm(&other).map_err(|_| "failed to scale to denominator")?; - let self_scaled = self.to_den(lcm).map_err(|_| "failed to scale to denominator")?; - let other_scaled = other.to_den(lcm).map_err(|_| "failed to scale to denominator")?; - - let n = self_scaled.0.checked_sub(other_scaled.0) - .ok_or("overflow while subtracting numerators")?; - Ok(Self(n, self_scaled.1)) - } -} - -impl PartialOrd for Rational128 { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for Rational128 { - fn cmp(&self, other: &Self) -> Ordering { - // handle some edge cases. - if self.1 == other.1 { - self.0.cmp(&other.0) - } else if self.1.is_zero() { - Ordering::Greater - } else if other.1.is_zero() { - Ordering::Less - } else { - // Don't even compute gcd. - let self_n = helpers_128bit::to_big_uint(self.0) * helpers_128bit::to_big_uint(other.1); - let other_n = helpers_128bit::to_big_uint(other.0) * helpers_128bit::to_big_uint(self.1); - self_n.cmp(&other_n) - } - } -} - -impl PartialEq for Rational128 { - fn eq(&self, other: &Self) -> bool { - // handle some edge cases. - if self.1 == other.1 { - self.0.eq(&other.0) - } else { - let self_n = helpers_128bit::to_big_uint(self.0) * helpers_128bit::to_big_uint(other.1); - let other_n = helpers_128bit::to_big_uint(other.0) * helpers_128bit::to_big_uint(self.1); - self_n.eq(&other_n) - } - } -} - -#[cfg(test)] -mod test_rational128 { - use super::*; - use super::helpers_128bit::*; - use rand::Rng; - - const MAX128: u128 = u128::max_value(); - const MAX64: u128 = u64::max_value() as u128; - const MAX64_2: u128 = 2 * u64::max_value() as u128; - - fn r(p: u128, q: u128) -> Rational128 { - Rational128(p, q) - } - - fn mul_div(a: u128, b: u128, c: u128) -> u128 { - use primitive_types::U256; - if a.is_zero() { return Zero::zero(); } - let c = c.max(1); - - // e for extended - let ae: U256 = a.into(); - let be: U256 = b.into(); - let ce: U256 = c.into(); - - let r = ae * be / ce; - if r > u128::max_value().into() { - a - } else { - r.as_u128() - } - } - - #[test] - fn truth_value_function_works() { - assert_eq!( - mul_div(2u128.pow(100), 8, 4), - 2u128.pow(101) - ); - assert_eq!( - mul_div(2u128.pow(100), 4, 8), - 2u128.pow(99) - ); - - // and it returns a if result cannot fit - assert_eq!(mul_div(MAX128 - 10, 2, 1), MAX128 - 10); - } - - #[test] - fn to_denom_works() { - // simple up and down - assert_eq!(r(1, 5).to_den(10), Ok(r(2, 10))); - assert_eq!(r(4, 10).to_den(5), Ok(r(2, 5))); - - // up and down with large numbers - assert_eq!(r(MAX128 - 10, MAX128).to_den(10), Ok(r(10, 10))); - assert_eq!(r(MAX128 / 2, MAX128).to_den(10), Ok(r(5, 10))); - - // large to perbill. This is very well needed for phragmen. - assert_eq!( - r(MAX128 / 2, MAX128).to_den(1000_000_000), - Ok(r(500_000_000, 1000_000_000)) - ); - - // large to large - assert_eq!(r(MAX128 / 2, MAX128).to_den(MAX128/2), Ok(r(MAX128/4, MAX128/2))); - } - - #[test] - fn gdc_works() { - assert_eq!(gcd(10, 5), 5); - assert_eq!(gcd(7, 22), 1); - } - - #[test] - fn lcm_works() { - // simple stuff - assert_eq!(r(3, 10).lcm(&r(4, 15)).unwrap(), 30); - assert_eq!(r(5, 30).lcm(&r(1, 7)).unwrap(), 210); - assert_eq!(r(5, 30).lcm(&r(1, 10)).unwrap(), 30); - - // large numbers - assert_eq!( - r(1_000_000_000, MAX128).lcm(&r(7_000_000_000, MAX128-1)), - Err("result cannot fit in u128"), - ); - assert_eq!( - r(1_000_000_000, MAX64).lcm(&r(7_000_000_000, MAX64-1)), - Ok(340282366920938463408034375210639556610), - ); - assert!(340282366920938463408034375210639556610 < MAX128); - assert!(340282366920938463408034375210639556610 == MAX64 * (MAX64 - 1)); - } - - #[test] - fn add_works() { - // works - assert_eq!(r(3, 10).checked_add(r(1, 10)).unwrap(), r(2, 5)); - assert_eq!(r(3, 10).checked_add(r(3, 7)).unwrap(), r(51, 70)); - - // errors - assert_eq!( - r(1, MAX128).checked_add(r(1, MAX128-1)), - Err("failed to scale to denominator"), - ); - assert_eq!( - r(7, MAX128).checked_add(r(MAX128, MAX128)), - Err("overflow while adding numerators"), - ); - assert_eq!( - r(MAX128, MAX128).checked_add(r(MAX128, MAX128)), - Err("overflow while adding numerators"), - ); - } - - #[test] - fn sub_works() { - // works - assert_eq!(r(3, 10).checked_sub(r(1, 10)).unwrap(), r(1, 5)); - assert_eq!(r(6, 10).checked_sub(r(3, 7)).unwrap(), r(12, 70)); - - // errors - assert_eq!( - r(2, MAX128).checked_sub(r(1, MAX128-1)), - Err("failed to scale to denominator"), - ); - assert_eq!( - r(7, MAX128).checked_sub(r(MAX128, MAX128)), - Err("overflow while subtracting numerators"), - ); - assert_eq!( - r(1, 10).checked_sub(r(2,10)), - Err("overflow while subtracting numerators"), - ); - } - - #[test] - fn ordering_and_eq_works() { - assert!(r(1, 2) > r(1, 3)); - assert!(r(1, 2) > r(2, 6)); - - assert!(r(1, 2) < r(6, 6)); - assert!(r(2, 1) > r(2, 6)); - - assert!(r(5, 10) == r(1, 2)); - assert!(r(1, 2) == r(1, 2)); - - assert!(r(1, 1490000000000200000) > r(1, 1490000000000200001)); - } - - #[test] - fn multiply_by_rational_works() { - assert_eq!(multiply_by_rational(7, 2, 3).unwrap(), 7 * 2 / 3); - assert_eq!(multiply_by_rational(7, 20, 30).unwrap(), 7 * 2 / 3); - assert_eq!(multiply_by_rational(20, 7, 30).unwrap(), 7 * 2 / 3); - - assert_eq!( - // MAX128 % 3 == 0 - multiply_by_rational(MAX128, 2, 3).unwrap(), - MAX128 / 3 * 2, - ); - assert_eq!( - // MAX128 % 7 == 3 - multiply_by_rational(MAX128, 5, 7).unwrap(), - (MAX128 / 7 * 5) + (3 * 5 / 7), - ); - assert_eq!( - // MAX128 % 7 == 3 - multiply_by_rational(MAX128, 11 , 13).unwrap(), - (MAX128 / 13 * 11) + (8 * 11 / 13), - ); - assert_eq!( - // MAX128 % 1000 == 455 - multiply_by_rational(MAX128, 555, 1000).unwrap(), - (MAX128 / 1000 * 555) + (455 * 555 / 1000), - ); - - assert_eq!( - multiply_by_rational(2 * MAX64 - 1, MAX64, MAX64).unwrap(), - 2 * MAX64 - 1, - ); - assert_eq!( - multiply_by_rational(2 * MAX64 - 1, MAX64 - 1, MAX64).unwrap(), - 2 * MAX64 - 3, - ); - - assert_eq!( - multiply_by_rational(MAX64 + 100, MAX64_2, MAX64_2 / 2).unwrap(), - (MAX64 + 100) * 2, - ); - assert_eq!( - multiply_by_rational(MAX64 + 100, MAX64_2 / 100, MAX64_2 / 200).unwrap(), - (MAX64 + 100) * 2, - ); - - assert_eq!( - multiply_by_rational(2u128.pow(66) - 1, 2u128.pow(65) - 1, 2u128.pow(65)).unwrap(), - 73786976294838206461, - ); - assert_eq!( - multiply_by_rational(1_000_000_000, MAX128 / 8, MAX128 / 2).unwrap(), - 250000000, - ); - } - - #[test] - fn multiply_by_rational_a_b_are_interchangeable() { - assert_eq!( - multiply_by_rational(10, MAX128, MAX128 / 2), - Ok(20), - ); - assert_eq!( - multiply_by_rational(MAX128, 10, MAX128 / 2), - Ok(20), - ); - } - - fn do_fuzz_multiply_by_rational( - iters: usize, - bits: u32, - maximum_error: u128, - do_print: bool, - bounded: bool, - mul_fn: F, - ) where F: Fn(u128, u128, u128) -> u128 { - let mut rng = rand::thread_rng(); - let mut average_diff = 0.0; - let upper_bound = 2u128.pow(bits); - - for _ in 0..iters { - let a = rng.gen_range(0u128, upper_bound); - let c = rng.gen_range(0u128, upper_bound); - let b = rng.gen_range( - 0u128, - if bounded { c } else { upper_bound } - ); - - let a: u128 = a.into(); - let b: u128 = b.into(); - let c: u128 = c.into(); - - let truth = mul_div(a, b, c); - let result = mul_fn(a, b, c); - let diff = truth.max(result) - truth.min(result); - let loss_ratio = diff as f64 / truth as f64; - average_diff += loss_ratio; - - if do_print && diff > maximum_error { - println!("++ Computed with more loss than expected: {} * {} / {}", a, b, c); - println!("++ Expected {}", truth); - println!("+++++++ Got {}", result); - } - } - - // print report - println!( - "## Fuzzed with {} numbers. Average error was {}", - iters, - average_diff / (iters as f64), - ); - } - - // TODO $# move into a proper fuzzer #3745 - const FUZZ_COUNT: usize = 100_000; - - #[test] - fn fuzz_multiply_by_rational_32() { - // if Err just return the truth value. We don't care about such cases. The point of this - // fuzzing is to make sure: if `multiply_by_rational` returns `Ok`, it must be 100% accurate - // returning `Err` is fine. - let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); - println!("\nInvariant: b < c"); - do_fuzz_multiply_by_rational(FUZZ_COUNT, 32, 0, false, true, f); - println!("every possibility"); - do_fuzz_multiply_by_rational(FUZZ_COUNT, 32, 0, false, false, f); - } - - #[test] - fn fuzz_multiply_by_rational_64() { - let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); - println!("\nInvariant: b < c"); - do_fuzz_multiply_by_rational(FUZZ_COUNT, 64, 0, false, true, f); - println!("every possibility"); - do_fuzz_multiply_by_rational(FUZZ_COUNT, 64, 0, false, false, f); - } - - #[test] - fn fuzz_multiply_by_rational_96() { - let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); - println!("\nInvariant: b < c"); - do_fuzz_multiply_by_rational(FUZZ_COUNT, 96, 0, false, true, f); - println!("every possibility"); - do_fuzz_multiply_by_rational(FUZZ_COUNT, 96, 0, false, false, f); - } - - #[test] - fn fuzz_multiply_by_rational_128() { - let f = |a, b, c| multiply_by_rational(a, b, c).unwrap_or(mul_div(a, b, c)); - println!("\nInvariant: b < c"); - do_fuzz_multiply_by_rational(FUZZ_COUNT, 127, 0, false, true, f); - println!("every possibility"); - do_fuzz_multiply_by_rational(FUZZ_COUNT, 127, 0, false, false, f); - } -} - -#[cfg(test)] -mod tests_fixed64 { - use super::*; - - fn max() -> Fixed64 { - Fixed64::from_parts(i64::max_value()) - } - - #[test] - fn fixed64_semantics() { - assert_eq!(Fixed64::from_rational(5, 2).0, 5 * 1_000_000_000 / 2); - assert_eq!(Fixed64::from_rational(5, 2), Fixed64::from_rational(10, 4)); - assert_eq!(Fixed64::from_rational(5, 0), Fixed64::from_rational(5, 1)); - - // biggest value that can be created. - assert_ne!(max(), Fixed64::from_natural(9_223_372_036)); - assert_eq!(max(), Fixed64::from_natural(9_223_372_037)); - } - - #[test] - fn fixed_64_growth_decrease_curve() { - let test_set = vec![0u32, 1, 10, 1000, 1_000_000_000]; - - // negative (1/2) - let mut fm = Fixed64::from_rational(-1, 2); - test_set.clone().into_iter().for_each(|i| { - assert_eq!(fm.saturated_multiply_accumulate(i) as i32, i as i32 - i as i32 / 2); - }); - - // unit (1) multiplier - fm = Fixed64::from_parts(0); - test_set.clone().into_iter().for_each(|i| { - assert_eq!(fm.saturated_multiply_accumulate(i), i); - }); - - // i.5 multiplier - fm = Fixed64::from_rational(1, 2); - test_set.clone().into_iter().for_each(|i| { - assert_eq!(fm.saturated_multiply_accumulate(i), i * 3 / 2); - }); - - // dual multiplier - fm = Fixed64::from_rational(1, 1); - test_set.clone().into_iter().for_each(|i| { - assert_eq!(fm.saturated_multiply_accumulate(i), i * 2); - }); - } - - macro_rules! saturating_mul_acc_test { - ($num_type:tt) => { - assert_eq!( - Fixed64::from_rational(100, 1).saturated_multiply_accumulate(10 as $num_type), - 1010, - ); - assert_eq!( - Fixed64::from_rational(100, 2).saturated_multiply_accumulate(10 as $num_type), - 510, - ); - assert_eq!( - Fixed64::from_rational(100, 3).saturated_multiply_accumulate(0 as $num_type), - 0, - ); - assert_eq!( - Fixed64::from_rational(5, 1).saturated_multiply_accumulate($num_type::max_value()), - $num_type::max_value() - ); - assert_eq!( - max().saturated_multiply_accumulate($num_type::max_value()), - $num_type::max_value() - ); - } - } - - #[test] - fn fixed64_multiply_accumulate_works() { - saturating_mul_acc_test!(u32); - saturating_mul_acc_test!(u64); - saturating_mul_acc_test!(u128); - } -} diff --git a/core/sr-primitives/src/traits.rs b/core/sr-primitives/src/traits.rs index a589f5f389d0b..a384c95e19c4d 100644 --- a/core/sr-primitives/src/traits.rs +++ b/core/sr-primitives/src/traits.rs @@ -24,20 +24,16 @@ use std::fmt::{Debug, Display}; #[cfg(feature = "std")] use serde::{Serialize, Deserialize, de::DeserializeOwned}; use primitives::{self, Hasher, Blake2Hasher, TypeId}; -use crate::codec::{Codec, Encode, Decode, HasCompact}; +use crate::codec::{Codec, Encode, Decode}; use crate::transaction_validity::{ ValidTransaction, TransactionValidity, TransactionValidityError, UnknownTransaction, }; use crate::generic::{Digest, DigestItem}; use crate::weights::DispatchInfo; -pub use integer_sqrt::IntegerSquareRoot; -pub use num_traits::{ +pub use arithmetic::traits::{ + SimpleArithmetic, UniqueSaturatedInto, UniqueSaturatedFrom, Saturating, SaturatedConversion, Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, - CheckedShl, CheckedShr -}; -use rstd::ops::{ - Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, - RemAssign, Shl, Shr + CheckedShl, CheckedShr, IntegerSquareRoot }; use app_crypto::AppKey; use impl_trait_for_tuples::impl_for_tuples; @@ -198,120 +194,6 @@ impl> Convert for ConvertInto { fn convert(a: A) -> B { a.into() } } -/// A meta trait for arithmetic. -/// -/// Arithmetic types do all the usual stuff you'd expect numbers to do. They are guaranteed to -/// be able to represent at least `u32` values without loss, hence the trait implies `From` -/// and smaller ints. All other conversions are fallible. -pub trait SimpleArithmetic: - Zero + One + IntegerSquareRoot + - From + From + From + TryInto + TryInto + TryInto + - TryFrom + TryInto + TryFrom + TryInto + TryFrom + TryInto + - UniqueSaturatedInto + UniqueSaturatedInto + UniqueSaturatedInto + - UniqueSaturatedFrom + UniqueSaturatedInto + UniqueSaturatedFrom + UniqueSaturatedInto + - Add + AddAssign + - Sub + SubAssign + - Mul + MulAssign + - Div + DivAssign + - Rem + RemAssign + - Shl + Shr + - CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv + - Saturating + PartialOrd + Ord + Bounded + - HasCompact + Sized -{} -impl + From + From + TryInto + TryInto + TryInto + - TryFrom + TryInto + TryFrom + TryInto + TryFrom + TryInto + - UniqueSaturatedInto + UniqueSaturatedInto + UniqueSaturatedInto + - UniqueSaturatedFrom + UniqueSaturatedInto + UniqueSaturatedFrom + - UniqueSaturatedInto + UniqueSaturatedFrom + UniqueSaturatedInto + - Add + AddAssign + - Sub + SubAssign + - Mul + MulAssign + - Div + DivAssign + - Rem + RemAssign + - Shl + Shr + - CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv + - Saturating + PartialOrd + Ord + Bounded + - HasCompact + Sized -> SimpleArithmetic for T {} - -/// Just like `From` except that if the source value is too big to fit into the destination type -/// then it'll saturate the destination. -pub trait UniqueSaturatedFrom: Sized { - /// Convert from a value of `T` into an equivalent instance of `Self`. - fn unique_saturated_from(t: T) -> Self; -} - -/// Just like `Into` except that if the source value is too big to fit into the destination type -/// then it'll saturate the destination. -pub trait UniqueSaturatedInto: Sized { - /// Consume self to return an equivalent value of `T`. - fn unique_saturated_into(self) -> T; -} - -impl + Bounded + Sized> UniqueSaturatedFrom for S { - fn unique_saturated_from(t: T) -> Self { - S::try_from(t).unwrap_or_else(|_| Bounded::max_value()) - } -} - -impl + Sized> UniqueSaturatedInto for S { - fn unique_saturated_into(self) -> T { - self.try_into().unwrap_or_else(|_| Bounded::max_value()) - } -} - -/// Simple trait to use checked mul and max value to give a saturated mul operation over -/// supported types. -pub trait Saturating { - /// Saturated addition - if the product can't fit in the type then just use max-value. - fn saturating_add(self, o: Self) -> Self; - - /// Saturated subtraction - if the product can't fit in the type then just use max-value. - fn saturating_sub(self, o: Self) -> Self; - - /// Saturated multiply - if the product can't fit in the type then just use max-value. - fn saturating_mul(self, o: Self) -> Self; -} - -impl Saturating for T { - fn saturating_add(self, o: Self) -> Self { - ::saturating_add(self, o) - } - fn saturating_sub(self, o: Self) -> Self { - ::saturating_sub(self, o) - } - fn saturating_mul(self, o: Self) -> Self { - self.checked_mul(&o).unwrap_or_else(Bounded::max_value) - } -} - -/// Convenience type to work around the highly unergonomic syntax needed -/// to invoke the functions of overloaded generic traits, in this case -/// `SaturatedFrom` and `SaturatedInto`. -pub trait SaturatedConversion { - /// Convert from a value of `T` into an equivalent instance of `Self`. - /// - /// This just uses `UniqueSaturatedFrom` internally but with this - /// variant you can provide the destination type using turbofish syntax - /// in case Rust happens not to assume the correct type. - fn saturated_from(t: T) -> Self where Self: UniqueSaturatedFrom { - >::unique_saturated_from(t) - } - - /// Consume self to return an equivalent value of `T`. - /// - /// This just uses `UniqueSaturatedInto` internally but with this - /// variant you can provide the destination type using turbofish syntax - /// in case Rust happens not to assume the correct type. - fn saturated_into(self) -> T where Self: UniqueSaturatedInto { - >::unique_saturated_into(self) - } -} -impl SaturatedConversion for T {} - /// Convenience type to work around the highly unergonomic syntax needed /// to invoke the functions of overloaded generic traits, in this case /// `TryFrom` and `TryInto`. diff --git a/core/sr-primitives/src/weights.rs b/core/sr-primitives/src/weights.rs index 2ffd4ee3917e1..b1cce0f77f4ff 100644 --- a/core/sr-primitives/src/weights.rs +++ b/core/sr-primitives/src/weights.rs @@ -24,7 +24,7 @@ //! (something that does not implement `Weighable`) is passed in. pub use crate::transaction_validity::TransactionPriority; -use crate::traits::Bounded; +use arithmetic::traits::Bounded; /// Numeric range of a transaction weight. pub type Weight = u32; diff --git a/node/runtime/src/lib.rs b/node/runtime/src/lib.rs index ac801fd39e976..69f812ce32994 100644 --- a/node/runtime/src/lib.rs +++ b/node/runtime/src/lib.rs @@ -85,7 +85,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. spec_version: 179, - impl_version: 179, + impl_version: 180, apis: RUNTIME_API_VERSIONS, };