diff --git a/Cargo.lock b/Cargo.lock index 6674b8d3abe6..ef0584f4ce54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2057,9 +2057,9 @@ dependencies = [ [[package]] name = "honggfuzz" -version = "0.5.47" +version = "0.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3de2c3273ef7735df1c5a72128ca85b1d20105b9aac643cdfd7a6e581311150" +checksum = "832bac18a82ec7d6c21887daa8616b238fe90d5d5e762d0d4b9372cdaa9e097f" dependencies = [ "arbitrary", "lazy_static", diff --git a/bin/node/executor/tests/basic.rs b/bin/node/executor/tests/basic.rs index 857f438e1c16..7799f0913a82 100644 --- a/bin/node/executor/tests/basic.rs +++ b/bin/node/executor/tests/basic.rs @@ -26,7 +26,7 @@ use frame_support::{ }; use sp_core::{NeverNativeValue, traits::Externalities, storage::well_known_keys}; use sp_runtime::{ - ApplyExtrinsicResult, Fixed128, + ApplyExtrinsicResult, Fixed128, FixedPointNumber, traits::Hash as HashT, transaction_validity::InvalidTransaction, }; @@ -61,7 +61,7 @@ fn transfer_fee(extrinsic: &E, fee_multiplier: Fixed128) -> Balance { let weight = default_transfer_call().get_dispatch_info().weight; let weight_fee = ::WeightToFee::calc(&weight); - base_fee + fee_multiplier.saturated_multiply_accumulate(length_fee + weight_fee) + base_fee + fee_multiplier.saturating_mul_acc_int(length_fee + weight_fee) } fn xt() -> UncheckedExtrinsic { diff --git a/bin/node/executor/tests/fees.rs b/bin/node/executor/tests/fees.rs index c4c3ca0bfc65..a4fc3930da28 100644 --- a/bin/node/executor/tests/fees.rs +++ b/bin/node/executor/tests/fees.rs @@ -22,7 +22,7 @@ use frame_support::{ weights::{GetDispatchInfo, constants::ExtrinsicBaseWeight, IdentityFee, WeightToFeePolynomial}, }; use sp_core::NeverNativeValue; -use sp_runtime::{Fixed128, Perbill}; +use sp_runtime::{FixedPointNumber, Fixed128, Perbill}; use node_runtime::{ CheckedExtrinsic, Call, Runtime, Balances, TransactionPayment, TransactionByteFee, @@ -39,7 +39,7 @@ fn fee_multiplier_increases_and_decreases_on_big_weight() { let mut t = new_test_ext(COMPACT_CODE, false); // initial fee multiplier must be zero - let mut prev_multiplier = Fixed128::from_parts(0); + let mut prev_multiplier = Fixed128::from_inner(0); t.execute_with(|| { assert_eq!(TransactionPayment::next_fee_multiplier(), prev_multiplier); diff --git a/bin/node/runtime/src/impls.rs b/bin/node/runtime/src/impls.rs index 85c28c96158b..0047ae5c1b6c 100644 --- a/bin/node/runtime/src/impls.rs +++ b/bin/node/runtime/src/impls.rs @@ -17,13 +17,10 @@ //! Some configurable implementations as associated type for the substrate runtime. -use core::num::NonZeroI128; use node_primitives::Balance; use sp_runtime::traits::{Convert, Saturating}; -use sp_runtime::{Fixed128, Perquintill}; -use frame_support::{ - traits::{OnUnbalanced, Currency, Get}, -}; +use sp_runtime::{FixedPointNumber, Fixed128, Perquintill}; +use frame_support::traits::{OnUnbalanced, Currency, Get}; use crate::{Balances, System, Authorship, MaximumBlockWeight, NegativeImbalance}; pub struct Author; @@ -69,18 +66,14 @@ impl> Convert for TargetedFeeAdjustment< // determines if the first_term is positive let positive = block_weight >= target_weight; let diff_abs = block_weight.max(target_weight) - block_weight.min(target_weight); - // safe, diff_abs cannot exceed u64 and it can always be computed safely even with the lossy - // `Fixed128::from_rational`. - let diff = Fixed128::from_rational( - diff_abs as i128, - NonZeroI128::new(max_weight.max(1) as i128).unwrap(), - ); + // safe, diff_abs cannot exceed u64. + let diff = Fixed128::saturating_from_rational(diff_abs, max_weight.max(1)); let diff_squared = diff.saturating_mul(diff); // 0.00004 = 4/100_000 = 40_000/10^9 - let v = Fixed128::from_rational(4, NonZeroI128::new(100_000).unwrap()); + let v = Fixed128::saturating_from_rational(4, 100_000); // 0.00004^2 = 16/10^10 Taking the future /2 into account... 8/10^10 - let v_squared_2 = Fixed128::from_rational(8, NonZeroI128::new(10_000_000_000).unwrap()); + let v_squared_2 = Fixed128::saturating_from_rational(8, 10_000_000_000u64); let first_term = v.saturating_mul(diff); let second_term = v_squared_2.saturating_mul(diff_squared); @@ -99,7 +92,7 @@ impl> Convert for TargetedFeeAdjustment< // multiplier. While at -1, it means that the network is so un-congested that all // transactions have no weight fee. We stop here and only increase if the network // became more busy. - .max(Fixed128::from_natural(-1)) + .max(Fixed128::saturating_from_integer(-1)) } } } @@ -111,7 +104,6 @@ mod tests { use crate::{MaximumBlockWeight, AvailableBlockRatio, Runtime}; use crate::{constants::currency::*, TransactionPayment, TargetBlockFullness}; use frame_support::weights::{Weight, WeightToFeePolynomial}; - use core::num::NonZeroI128; fn max() -> Weight { MaximumBlockWeight::get() @@ -135,7 +127,7 @@ mod tests { let s = block_weight; let fm = v * (s/m - ss/m) + v.powi(2) * (s/m - ss/m).powi(2) / 2.0; - let addition_fm = Fixed128::from_parts((fm * Fixed128::accuracy() as f64).round() as i128); + let addition_fm = Fixed128::from_inner((fm * Fixed128::accuracy() as f64).round() as i128); previous.saturating_add(addition_fm) } @@ -150,7 +142,7 @@ mod tests { #[test] fn fee_multiplier_update_poc_works() { - let fm = Fixed128::from_rational(0, NonZeroI128::new(1).unwrap()); + let fm = Fixed128::saturating_from_rational(0, 1); let test_set = vec![ (0, fm.clone()), (100, fm.clone()), @@ -164,7 +156,7 @@ mod tests { fee_multiplier_update(w, fm), TargetedFeeAdjustment::::convert(fm), // Error is only 1 in 10^18 - Fixed128::from_parts(1), + Fixed128::from_inner(1), ); }) }) @@ -180,7 +172,7 @@ mod tests { loop { let next = TargetedFeeAdjustment::::convert(fm); fm = next; - if fm == Fixed128::from_natural(-1) { break; } + if fm == Fixed128::saturating_from_integer(-1) { break; } iterations += 1; } println!("iteration {}, new fm = {:?}. Weight fee is now zero", iterations, fm); @@ -220,7 +212,7 @@ mod tests { iterations += 1; let fee = ::WeightToFee::calc(&tx_weight); - let adjusted_fee = fm.saturated_multiply_accumulate(fee); + let adjusted_fee = fm.saturating_mul_acc_int(fee); println!( "iteration {}, new fm = {:?}. Fee at this point is: {} units / {} millicents, \ {} cents, {} dollars", @@ -323,8 +315,8 @@ mod tests { // ... stops going down at -1 assert_eq!( - TargetedFeeAdjustment::::convert(Fixed128::from_natural(-1)), - Fixed128::from_natural(-1) + TargetedFeeAdjustment::::convert(Fixed128::saturating_from_integer(-1)), + Fixed128::saturating_from_integer(-1) ); }) } @@ -333,7 +325,7 @@ mod tests { fn weight_to_fee_should_not_overflow_on_large_weights() { let kb = 1024 as Weight; let mb = kb * kb; - let max_fm = Fixed128::from_natural(i128::max_value()); + let max_fm = Fixed128::saturating_from_integer(i128::max_value()); // check that for all values it can compute, correctly. vec![ @@ -356,7 +348,7 @@ mod tests { run_with_system_weight(i, || { let next = TargetedFeeAdjustment::::convert(Fixed128::default()); let truth = fee_multiplier_update(i, Fixed128::default()); - assert_eq_error_rate!(truth, next, Fixed128::from_parts(50_000_000)); + assert_eq_error_rate!(truth, next, Fixed128::from_inner(50_000_000)); }); }); diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index e484e84d432c..69c8e9831630 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -94,7 +94,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. spec_version: 250, - impl_version: 0, + impl_version: 1, apis: RUNTIME_API_VERSIONS, transaction_version: 1, }; diff --git a/frame/balances/src/tests.rs b/frame/balances/src/tests.rs index 9cfdc147b4a7..149b9f07d223 100644 --- a/frame/balances/src/tests.rs +++ b/frame/balances/src/tests.rs @@ -37,7 +37,7 @@ macro_rules! decl_tests { ($test:ty, $ext_builder:ty, $existential_deposit:expr) => { use crate::*; - use sp_runtime::{Fixed128, traits::{SignedExtension, BadOrigin}}; + use sp_runtime::{FixedPointNumber, Fixed128, traits::{SignedExtension, BadOrigin}}; use frame_support::{ assert_noop, assert_ok, assert_err, traits::{ @@ -154,7 +154,7 @@ macro_rules! decl_tests { .monied(true) .build() .execute_with(|| { - pallet_transaction_payment::NextFeeMultiplier::put(Fixed128::from_natural(1)); + pallet_transaction_payment::NextFeeMultiplier::put(Fixed128::saturating_from_integer(1)); Balances::set_lock(ID_1, &1, 10, WithdrawReason::Reserve.into()); assert_noop!( >::transfer(&1, &2, 1, AllowDeath), diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index 71ef8a56c2ff..95c799d7f2ab 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -44,7 +44,7 @@ use frame_support::{ dispatch::DispatchResult, }; use sp_runtime::{ - Fixed128, + Fixed128, FixedPointNumber, transaction_validity::{ TransactionPriority, ValidTransaction, InvalidTransaction, TransactionValidityError, TransactionValidity, @@ -83,7 +83,7 @@ pub trait Trait: frame_system::Trait { decl_storage! { trait Store for Module as TransactionPayment { - pub NextFeeMultiplier get(fn next_fee_multiplier): Multiplier = Multiplier::from_parts(0); + pub NextFeeMultiplier get(fn next_fee_multiplier): Multiplier = Multiplier::from_inner(0); } } @@ -164,7 +164,7 @@ impl Module { // the adjustable part of the fee let adjustable_fee = len_fee.saturating_add(unadjusted_weight_fee); let targeted_fee_adjustment = NextFeeMultiplier::get(); - let adjusted_fee = targeted_fee_adjustment.saturated_multiply_accumulate(adjustable_fee.saturated_into()); + let adjusted_fee = targeted_fee_adjustment.saturating_mul_acc_int(adjustable_fee.saturated_into()); let base_fee = Self::weight_to_fee(T::ExtrinsicBaseWeight::get()); base_fee.saturating_add(adjusted_fee.saturated_into()).saturating_add(tip) @@ -182,7 +182,7 @@ impl Module { { let fee = UniqueSaturatedInto::::unique_saturated_into(Self::weight_to_fee(weight)); UniqueSaturatedFrom::unique_saturated_from( - NextFeeMultiplier::get().saturated_multiply_accumulate(fee) + NextFeeMultiplier::get().saturating_mul_acc_int(fee) ) } @@ -321,7 +321,6 @@ impl SignedExtension for ChargeTransactionPayment whe #[cfg(test)] mod tests { use super::*; - use core::num::NonZeroI128; use codec::Encode; use frame_support::{ impl_outer_dispatch, impl_outer_origin, parameter_types, @@ -567,7 +566,7 @@ mod tests { .execute_with(|| { let len = 10; - NextFeeMultiplier::put(Fixed128::from_rational(1, NonZeroI128::new(2).unwrap())); + NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2)); let pre = ChargeTransactionPayment::::from(5 /* tipped */) .pre_dispatch(&2, CALL, &info_from_weight(100), len) @@ -655,7 +654,7 @@ mod tests { .execute_with(|| { // all fees should be x1.5 - NextFeeMultiplier::put(Fixed128::from_rational(1, NonZeroI128::new(2).unwrap())); + NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2)); let len = 10; assert!( @@ -683,7 +682,7 @@ mod tests { .execute_with(|| { // all fees should be x1.5 - NextFeeMultiplier::put(Fixed128::from_rational(1, NonZeroI128::new(2).unwrap())); + NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2)); assert_eq!( TransactionPayment::query_info(xt, len), @@ -712,7 +711,7 @@ mod tests { .execute_with(|| { // Next fee multiplier is zero - assert_eq!(NextFeeMultiplier::get(), Fixed128::from_natural(0)); + assert_eq!(NextFeeMultiplier::get(), Fixed128::saturating_from_integer(0)); // Tip only, no fees works let dispatch_info = DispatchInfo { @@ -752,7 +751,7 @@ mod tests { .execute_with(|| { // Add a next fee multiplier - NextFeeMultiplier::put(Fixed128::from_rational(1, NonZeroI128::new(2).unwrap())); // = 1/2 = .5 + NextFeeMultiplier::put(Fixed128::saturating_from_rational(1, 2)); // = 1/2 = .5 // Base fee is unaffected by multiplier let dispatch_info = DispatchInfo { weight: 0, diff --git a/primitives/arithmetic/Cargo.toml b/primitives/arithmetic/Cargo.toml index c22706e32ee3..9d080d6010e3 100644 --- a/primitives/arithmetic/Cargo.toml +++ b/primitives/arithmetic/Cargo.toml @@ -20,12 +20,12 @@ num-traits = { version = "0.2.8", default-features = false } sp-std = { version = "2.0.0-dev", default-features = false, path = "../std" } serde = { version = "1.0.101", optional = true, features = ["derive"] } sp-debug-derive = { version = "2.0.0-dev", default-features = false, path = "../../primitives/debug-derive" } -primitive-types = { version = "0.7.0", default-features = false } [dev-dependencies] rand = "0.7.2" criterion = "0.3" serde_json = "1.0" +primitive-types = "0.7.0" [features] default = ["std"] @@ -35,7 +35,6 @@ std = [ "sp-std/std", "serde", "sp-debug-derive/std", - "primitive-types/std", ] [[bench]] diff --git a/primitives/arithmetic/fuzzer/Cargo.lock b/primitives/arithmetic/fuzzer/Cargo.lock deleted file mode 100644 index 3a4187437ae7..000000000000 --- a/primitives/arithmetic/fuzzer/Cargo.lock +++ /dev/null @@ -1,401 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "arbitrary" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cf76cb6e2222ed0ea86b2b0ee2f71c96ec6edd5af42e84d59160e91b836ec4" - -[[package]] -name = "arrayvec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" - -[[package]] -name = "autocfg" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" - -[[package]] -name = "bitvec" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993f74b4c99c1908d156b8d2e0fb6277736b0ecbd833982fd1241d39b2766a6" - -[[package]] -name = "byte-slice-cast" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" - -[[package]] -name = "byteorder" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" - -[[package]] -name = "c2-chacha" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "214238caa1bf3a496ec3392968969cab8549f96ff30652c9e56885329315f6bb" -dependencies = [ - "ppv-lite86", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "fixed-hash" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3367952ceb191f4ab95dd5685dc163ac539e36202f9fcfd0cb22f9f9c542fefc" -dependencies = [ - "byteorder", - "rand", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "getrandom" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "honggfuzz" -version = "0.5.45" -dependencies = [ - "arbitrary", - "lazy_static", - "memmap", -] - -[[package]] -name = "impl-codec" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1be51a921b067b0eaca2fad532d9400041561aa922221cc65f95a85641c6bf53" -dependencies = [ - "parity-scale-codec", -] - -[[package]] -name = "integer-sqrt" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f65877bf7d44897a473350b1046277941cee20b263397e90869c50b6e766088b" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018" - -[[package]] -name = "memmap" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.42" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" -dependencies = [ - "autocfg", -] - -[[package]] -name = "parity-scale-codec" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f509c5e67ca0605ee17dcd3f91ef41cadd685c75a298fb6261b781a5acb3f910" -dependencies = [ - "arrayvec", - "bitvec", - "byte-slice-cast", - "parity-scale-codec-derive", - "serde", -] - -[[package]] -name = "parity-scale-codec-derive" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a0ec292e92e8ec7c58e576adacc1e3f399c597c8f263c42f18420abe58e7245" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b" - -[[package]] -name = "primitive-types" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4336f4f5d5524fa60bcbd6fe626f9223d8142a50e7053e979acdf0da41ab975" -dependencies = [ - "fixed-hash", - "impl-codec", - "uint", -] - -[[package]] -name = "proc-macro-crate" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10d4b51f154c8a7fb96fd6dad097cb74b863943ec010ac94b9fd1be8861fe1e" -dependencies = [ - "toml", -] - -[[package]] -name = "proc-macro2" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom", - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" -dependencies = [ - "c2-chacha", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "serde" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sp-arithmetic" -version = "2.0.0-alpha.3" -dependencies = [ - "integer-sqrt", - "num-traits", - "parity-scale-codec", - "serde", - "sp-debug-derive", - "sp-std", -] - -[[package]] -name = "sp-arithmetic-fuzzer" -version = "2.0.0" -dependencies = [ - "honggfuzz", - "num-bigint", - "num-traits", - "primitive-types", - "sp-arithmetic", -] - -[[package]] -name = "sp-debug-derive" -version = "2.0.0-alpha.3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sp-std" -version = "2.0.0-alpha.3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "syn" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "toml" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" -dependencies = [ - "serde", -] - -[[package]] -name = "uint" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e75a4cdd7b87b28840dba13c483b9a88ee6bbf16ba5c951ee1ecfcf723078e0d" -dependencies = [ - "byteorder", - "crunchy", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "unicode-xid" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "winapi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/primitives/arithmetic/fuzzer/Cargo.toml b/primitives/arithmetic/fuzzer/Cargo.toml index fdcf691762a1..c4842adc8462 100644 --- a/primitives/arithmetic/fuzzer/Cargo.toml +++ b/primitives/arithmetic/fuzzer/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-arithmetic = { version = "2.0.0-dev", path = ".." } -honggfuzz = "0.5" +honggfuzz = "0.5.49" primitive-types = "0.7.0" num-bigint = "0.2" num-traits = "0.2" @@ -31,3 +31,7 @@ path = "src/per_thing_rational.rs" [[bin]] name = "rational128" path = "src/rational128.rs" + +[[bin]] +name = "fixed" +path = "src/fixed.rs" \ No newline at end of file diff --git a/primitives/arithmetic/fuzzer/src/fixed.rs b/primitives/arithmetic/fuzzer/src/fixed.rs new file mode 100644 index 000000000000..115d7dbbdba0 --- /dev/null +++ b/primitives/arithmetic/fuzzer/src/fixed.rs @@ -0,0 +1,82 @@ +// This file is part of Substrate. + +// Copyright (C) 2020 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! # Running +//! Running this fuzzer can be done with `cargo hfuzz run fixed`. `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 fixed hfuzz_workspace/fixed/*.fuzz`. +//! +//! # More information +//! More information about `honggfuzz` can be found +//! [here](https://docs.rs/honggfuzz/). + +use honggfuzz::fuzz; +use sp_arithmetic::{FixedPointNumber, Fixed64, traits::Saturating}; + +fn main() { + loop { + fuzz!(|data: (i32, i32)| { + let x: i128 = data.0.into(); + let y: i128 = data.1.into(); + + // Check `from_rational` and division are consistent. + if y != 0 { + let f1 = Fixed64::saturating_from_integer(x) / Fixed64::saturating_from_integer(y); + let f2 = Fixed64::saturating_from_rational(x, y); + assert_eq!(f1.into_inner(), f2.into_inner()); + } + + // Check `saturating_mul`. + let a = Fixed64::saturating_from_rational(2, 5); + let b = a.saturating_mul(Fixed64::saturating_from_integer(x)); + let n = b.into_inner() as i128; + let m = 2i128 * x * Fixed64::accuracy() as i128 / 5i128; + assert_eq!(n, m); + + // Check `saturating_mul` and division are inverse. + if x != 0 { + assert_eq!(a, b / Fixed64::saturating_from_integer(x)); + } + + // Check `reciprocal`. + let r = a.reciprocal().unwrap().reciprocal().unwrap(); + assert_eq!(a, r); + + // Check addition. + let a = Fixed64::saturating_from_integer(x); + let b = Fixed64::saturating_from_integer(y); + let c = Fixed64::saturating_from_integer(x.saturating_add(y)); + assert_eq!(a.saturating_add(b), c); + + // Check substraction. + let a = Fixed64::saturating_from_integer(x); + let b = Fixed64::saturating_from_integer(y); + let c = Fixed64::saturating_from_integer(x.saturating_sub(y)); + assert_eq!(a.saturating_sub(b), c); + + // Check `saturating_mul_acc_int`. + let a = Fixed64::saturating_from_rational(2, 5); + let b = a.saturating_mul_acc_int(x); + let xx = Fixed64::saturating_from_integer(x); + let d = a.saturating_mul(xx).saturating_add(xx).into_inner() as i128 / Fixed64::accuracy() as i128; + assert_eq!(b, d); + }); + } +} diff --git a/primitives/arithmetic/src/fixed.rs b/primitives/arithmetic/src/fixed.rs new file mode 100644 index 000000000000..19f498593da6 --- /dev/null +++ b/primitives/arithmetic/src/fixed.rs @@ -0,0 +1,1534 @@ +// Copyright 2020 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 . + +//! Decimal Fixed Point implementations for Substrate runtime. + +use sp_std::{ops::{self, Add, Sub, Mul, Div}, fmt::Debug, prelude::*, convert::{TryInto, TryFrom}}; +use codec::{Encode, Decode}; +use crate::{ + helpers_128bit::multiply_by_rational, PerThing, + traits::{ + SaturatedConversion, CheckedSub, CheckedAdd, CheckedMul, CheckedDiv, CheckedNeg, + Bounded, Saturating, UniqueSaturatedInto, Zero, One, Signed + }, +}; + +#[cfg(feature = "std")] +use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; + +/// Integer types that can be used to interact with `FixedPointNumber` implementations. +pub trait FixedPointOperand: Copy + Clone + Bounded + Zero + Saturating + + PartialOrd + UniqueSaturatedInto + TryFrom + CheckedNeg {} + +impl FixedPointOperand for i128 {} +impl FixedPointOperand for u128 {} +impl FixedPointOperand for i64 {} +impl FixedPointOperand for u64 {} +impl FixedPointOperand for i32 {} +impl FixedPointOperand for u32 {} +impl FixedPointOperand for i16 {} +impl FixedPointOperand for u16 {} +impl FixedPointOperand for i8 {} +impl FixedPointOperand for u8 {} + +/// Something that implements a decimal fixed point number. +/// +/// The precision is given by `Self::DIV`, i.e. `1 / DIV` can be represented. +/// +/// Each type can store numbers from `Self::Inner::min_value() / Self::DIV` +/// to `Self::Inner::max_value() / Self::DIV`. +/// This is also referred to as the _accuracy_ of the type in the documentation. +pub trait FixedPointNumber: + Sized + Copy + Default + Debug + + Saturating + Bounded + + Eq + PartialEq + Ord + PartialOrd + + CheckedSub + CheckedAdd + CheckedMul + CheckedDiv + + Add + Sub + Div + Mul +{ + /// The underlying data type used for this fixed point number. + type Inner: Debug + One + CheckedMul + CheckedDiv + CheckedNeg + Signed + FixedPointOperand; + + /// Precision of this fixed point implementation. It should be a power of `10`. + const DIV: Self::Inner; + + /// Precision of this fixed point implementation. + fn accuracy() -> Self::Inner { + Self::DIV + } + + /// Builds this type from an integer number. + fn from_inner(int: Self::Inner) -> Self; + + /// Consumes `self` and returns the inner raw value. + fn into_inner(self) -> Self::Inner; + + /// Creates self from an integer number `int`. + /// + /// Returns `Self::max` or `Self::min` if `int` exceeds accuracy. + fn saturating_from_integer>(int: N) -> Self { + Self::from_inner(int.unique_saturated_into().saturating_mul(Self::DIV)) + } + + /// Creates `self` from an integer number `int`. + /// + /// Returns `None` if `int` exceeds accuracy. + fn checked_from_integer(int: Self::Inner) -> Option { + int.checked_mul(&Self::DIV).map(|inner| Self::from_inner(inner)) + } + + /// Creates `self` from a rational number. Equal to `n / d`. + /// + /// Panics if `d = 0`. Returns `Self::max` or `Self::min` if `n / d` exceeds accuracy. + fn saturating_from_rational(n: N, d: D) -> Self { + if d == D::zero() { + panic!("attempt to divide by zero") + } + Self::checked_from_rational(n, d).unwrap_or(to_bound(n, d)) + } + + /// Creates `self` from a rational number. Equal to `n / d`. + /// + /// Returns `None` if `d == 0` or `n / d` exceeds accuracy. + fn checked_from_rational(n: N, d: D) -> Option { + if d == D::zero() { + return None + } + + let n: I129 = n.into(); + let d: I129 = d.into(); + let negative = n.negative != d.negative; + + multiply_by_rational(n.value, Self::DIV.unique_saturated_into(), d.value).ok() + .and_then(|value| from_i129(I129 { value, negative })) + .map(|inner| Self::from_inner(inner)) + } + + /// Checked multiplication for integer type `N`. Equal to `self * n`. + /// + /// Returns `None` if the result does not fit in `N`. + fn checked_mul_int(self, n: N) -> Option { + let lhs: I129 = self.into_inner().into(); + let rhs: I129 = n.into(); + let negative = lhs.negative != rhs.negative; + + multiply_by_rational(lhs.value, rhs.value, Self::DIV.unique_saturated_into()).ok() + .and_then(|value| from_i129(I129 { value, negative })) + } + + /// Saturating multiplication for integer type `N`. Equal to `self * n`. + /// + /// Returns `N::min` or `N::max` if the result does not fit in `N`. + fn saturating_mul_int(self, n: N) -> N { + self.checked_mul_int(n).unwrap_or(to_bound(self.into_inner(), n)) + } + + /// Checked division for integer type `N`. Equal to `self / d`. + /// + /// Returns `None` if the result does not fit in `N` or `d == 0`. + fn checked_div_int(self, d: N) -> Option { + let lhs: I129 = self.into_inner().into(); + let rhs: I129 = d.into(); + let negative = lhs.negative != rhs.negative; + + lhs.value.checked_div(rhs.value) + .and_then(|n| n.checked_div(Self::DIV.unique_saturated_into())) + .and_then(|value| from_i129(I129 { value, negative })) + } + + /// Saturating division for integer type `N`. Equal to `self / d`. + /// + /// Panics if `d == 0`. Returns `N::min` or `N::max` if the result does not fit in `N`. + fn saturating_div_int(self, d: N) -> N { + if d == N::zero() { + panic!("attempt to divide by zero") + } + self.checked_div_int(d).unwrap_or(to_bound(self.into_inner(), d)) + } + + /// Saturating multiplication for integer type `N`, adding the result back. + /// Equal to `self * n + n`. + /// + /// Returns `N::min` or `N::max` if the multiplication or final result does not fit in `N`. + fn saturating_mul_acc_int(self, n: N) -> N { + self.saturating_mul_int(n).saturating_add(n) + } + + /// Saturating absolute value. + /// + /// Returns `Self::max` if `self == Self::min`. + fn saturating_abs(self) -> Self { + let inner = self.into_inner(); + if inner.is_positive() { + self + } else { + Self::from_inner(inner.checked_neg().unwrap_or(Self::Inner::max_value())) + } + } + + /// Takes the reciprocal (inverse). Equal to `1 / self`. + /// + /// Returns `None` if `self = 0`. + fn reciprocal(self) -> Option { + Self::one().checked_div(&self) + } + + /// Returns zero. + fn zero() -> Self { + Self::from_inner(Self::Inner::zero()) + } + + /// Checks if the number is zero. + fn is_zero(&self) -> bool { + self.into_inner() == Self::Inner::zero() + } + + /// Returns one. + fn one() -> Self { + Self::from_inner(Self::DIV) + } + + /// Checks if the number is one. + fn is_one(&self) -> bool { + self.into_inner() == Self::Inner::one() + } + + /// Checks if the number is positive. + fn is_positive(self) -> bool { + self.into_inner() >= Self::Inner::zero() + } + + /// Checks if the number is negative. + fn is_negative(self) -> bool { + self.into_inner() < Self::Inner::zero() + } + + /// Returns the integer part. + fn trunc(self) -> Self { + self.into_inner().checked_div(&Self::DIV) + .expect("panics only if DIV is zero, DIV is not zero; qed") + .checked_mul(&Self::DIV) + .map(|inner| Self::from_inner(inner)) + .expect("can not overflow since fixed number is >= integer part") + } + + /// Returns the fractional part. + /// + /// Note: the returned fraction will be non-negative for negative numbers, + /// except in the case where the integer part is zero. + fn frac(self) -> Self { + let integer = self.trunc(); + let fractional = self.saturating_sub(integer); + if integer == Self::zero() { + fractional + } else { + fractional.saturating_abs() + } + } + + /// Returns the smallest integer greater than or equal to a number. + /// + /// Saturates to `Self::max` (truncated) if the result does not fit. + fn ceil(self) -> Self { + if self.is_negative() { + self.trunc() + } else { + self.saturating_add(Self::one()).trunc() + } + } + + /// Returns the largest integer less than or equal to a number. + /// + /// Saturates to `Self::min` (truncated) if the result does not fit. + fn floor(self) -> Self { + if self.is_negative() { + self.saturating_sub(Self::one()).trunc() + } else { + self.trunc() + } + } + + /// Returns the number rounded to the nearest integer. Rounds half-way cases away from 0.0. + /// + /// Saturates to `Self::min` or `Self::max` (truncated) if the result does not fit. + fn round(self) -> Self { + let n = self.frac().saturating_mul(Self::saturating_from_integer(10)); + if n < Self::saturating_from_integer(5) { + self.trunc() + } else { + let extra = Self::saturating_from_integer(self.into_inner().signum()); + (self.saturating_add(extra)).trunc() + } + } +} + +/// Data type used as intermediate storage in some computations to avoid overflow. +struct I129 { + value: u128, + negative: bool, +} + +impl From for I129 { + fn from(n: N) -> I129 { + if n < N::zero() { + let value: u128 = n.checked_neg() + .map(|n| n.unique_saturated_into()) + .unwrap_or(N::max_value().unique_saturated_into().saturating_add(1)); + I129 { value, negative: true } + } else { + I129 { value: n.unique_saturated_into(), negative: false } + } + } +} + +/// Transforms an `I129` to `N` if it is possible. +fn from_i129(n: I129) -> Option { + let max_plus_one: u128 = N::max_value().unique_saturated_into().saturating_add(1); + if n.negative && N::min_value() < N::zero() && n.value == max_plus_one { + Some(N::min_value()) + } else { + let unsigned_inner: N = n.value.try_into().ok()?; + let inner = if n.negative { unsigned_inner.checked_neg()? } else { unsigned_inner }; + Some(inner) + } +} + +/// Returns `R::max` if the sign of `n * m` is positive, `R::min` otherwise. +fn to_bound(n: N, m: D) -> R { + if (n < N::zero()) != (m < D::zero()) { + R::min_value() + } else { + R::max_value() + } +} + +macro_rules! implement_fixed { + ( + $name:ident, + $test_mod:ident, + $inner_type:ty, + $div:tt, + $title:expr $(,)? + ) => { + /// A fixed point number representation in the range. + /// + #[doc = $title] + #[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] + pub struct $name($inner_type); + + impl From<$inner_type> for $name { + fn from(int: $inner_type) -> Self { + $name::saturating_from_integer(int) + } + } + + impl From<(N, D)> for $name { + fn from(r: (N, D)) -> Self { + $name::saturating_from_rational(r.0, r.1) + } + } + + impl FixedPointNumber for $name { + type Inner = $inner_type; + + const DIV: Self::Inner = $div; + + fn from_inner(inner: Self::Inner) -> Self { + Self(inner) + } + + fn into_inner(self) -> Self::Inner { + self.0 + } + } + + impl Saturating for $name { + fn saturating_add(self, rhs: Self) -> Self { + Self(self.0.saturating_add(rhs.0)) + } + + fn saturating_sub(self, rhs: Self) -> Self { + Self(self.0.saturating_sub(rhs.0)) + } + + fn saturating_mul(self, rhs: Self) -> Self { + self.checked_mul(&rhs).unwrap_or(to_bound(self.0, rhs.0)) + } + + fn saturating_pow(self, exp: usize) -> Self { + if exp == 0 { + return Self::saturating_from_integer(1); + } + + let exp = exp as u32; + let msb_pos = 32 - exp.leading_zeros(); + + let mut result = Self::saturating_from_integer(1); + let mut pow_val = self; + for i in 0..msb_pos { + if ((1 << i) & exp) > 0 { + result = result.saturating_mul(pow_val); + } + pow_val = pow_val.saturating_mul(pow_val); + } + result + } + } + + impl ops::Neg for $name { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } + } + + impl ops::Add for $name { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self(self.0 + rhs.0) + } + } + + impl ops::Sub for $name { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self(self.0 - rhs.0) + } + } + + impl ops::Mul for $name { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + self.checked_mul(&rhs) + .unwrap_or_else(|| panic!("attempt to multiply with overflow")) + } + } + + impl ops::Div for $name { + type Output = Self; + + fn div(self, rhs: Self) -> Self::Output { + if rhs.0 == 0 { + panic!("attempt to divide by zero") + } + self.checked_div(&rhs) + .unwrap_or_else(|| panic!("attempt to divide with overflow")) + } + } + + impl CheckedSub for $name { + fn checked_sub(&self, rhs: &Self) -> Option { + self.0.checked_sub(rhs.0).map(Self) + } + } + + impl CheckedAdd for $name { + fn checked_add(&self, rhs: &Self) -> Option { + self.0.checked_add(rhs.0).map(Self) + } + } + + impl CheckedDiv for $name { + fn checked_div(&self, other: &Self) -> Option { + if other.0 == 0 { + return None + } + + let lhs: I129 = self.0.into(); + let rhs: I129 = other.0.into(); + let negative = lhs.negative != rhs.negative; + + multiply_by_rational(lhs.value, Self::DIV as u128, rhs.value).ok() + .and_then(|value| from_i129(I129 { value, negative })) + .map(Self) + } + } + + impl CheckedMul for $name { + fn checked_mul(&self, other: &Self) -> Option { + let lhs: I129 = self.0.into(); + let rhs: I129 = other.0.into(); + let negative = lhs.negative != rhs.negative; + + multiply_by_rational(lhs.value, rhs.value, Self::DIV as u128).ok() + .and_then(|value| from_i129(I129 { value, negative })) + .map(Self) + } + } + + impl Bounded for $name { + fn min_value() -> Self { + Self(::Inner::min_value()) + } + + fn max_value() -> Self { + Self(::Inner::max_value()) + } + } + + impl sp_std::fmt::Debug for $name { + #[cfg(feature = "std")] + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + let integral = { + let int = self.0 / Self::accuracy(); + let signum_for_zero = if int == 0 && self.is_negative() { "-" } else { "" }; + format!("{}{}", signum_for_zero, int) + }; + let precision = (Self::accuracy() as f64).log10() as usize; + let fractional = format!("{:0>weight$}", (self.0 % Self::accuracy()).abs(), weight=precision); + write!(f, "{}({}.{})", stringify!($name), integral, fractional) + } + + #[cfg(not(feature = "std"))] + fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + Ok(()) + } + } + + impl From

for $name { + fn from(p: P) -> Self { + let accuracy = P::ACCURACY.saturated_into(); + let value = p.deconstruct().saturated_into(); + $name::saturating_from_rational(value, accuracy) + } + } + + #[cfg(feature = "std")] + impl sp_std::fmt::Display for $name { + fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { + write!(f, "{}", self.0) + } + } + + #[cfg(feature = "std")] + impl sp_std::str::FromStr for $name { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + let inner: ::Inner = s.parse() + .map_err(|_| "invalid string input for fixed point number")?; + Ok(Self::from_inner(inner)) + } + } + + // Manual impl `Serialize` as serde_json does not support i128. + // TODO: remove impl if issue https://github.com/serde-rs/json/issues/548 fixed. + #[cfg(feature = "std")] + impl Serialize for $name { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&self.to_string()) + } + } + + // Manual impl `Deserialize` as serde_json does not support i128. + // TODO: remove impl if issue https://github.com/serde-rs/json/issues/548 fixed. + #[cfg(feature = "std")] + impl<'de> Deserialize<'de> for $name { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + use sp_std::str::FromStr; + let s = String::deserialize(deserializer)?; + $name::from_str(&s).map_err(|err_str| de::Error::custom(err_str)) + } + } + + #[cfg(test)] + mod $test_mod { + use super::*; + use crate::{Perbill, Percent, Permill, Perquintill}; + + fn max() -> $name { + $name::max_value() + } + + fn min() -> $name { + $name::min_value() + } + + fn precision() -> usize { + ($name::accuracy() as f64).log10() as usize + } + + #[test] + fn macro_preconditions() { + assert!($name::DIV > 0); + } + + #[test] + fn from_i129_works() { + let a = I129 { + value: 1, + negative: true, + }; + + // Can't convert negative number to unsigned. + assert_eq!(from_i129::(a), None); + + let a = I129 { + value: u128::max_value() - 1, + negative: false, + }; + + // Max - 1 value fits. + assert_eq!(from_i129::(a), Some(u128::max_value() - 1)); + + let a = I129 { + value: u128::max_value(), + negative: false, + }; + + // Max value fits. + assert_eq!(from_i129::(a), Some(u128::max_value())); + + let a = I129 { + value: i128::max_value() as u128 + 1, + negative: true, + }; + + // Min value fits. + assert_eq!(from_i129::(a), Some(i128::min_value())); + + let a = I129 { + value: i128::max_value() as u128 + 1, + negative: false, + }; + + // Max + 1 does not fit. + assert_eq!(from_i129::(a), None); + + let a = I129 { + value: i128::max_value() as u128, + negative: false, + }; + + // Max value fits. + assert_eq!(from_i129::(a), Some(i128::max_value())); + } + + #[test] + fn to_bound_works() { + let a = 1i32; + let b = 1i32; + + // Pos + Pos => Max. + assert_eq!(to_bound::<_, _, i32>(a, b), i32::max_value()); + + let a = -1i32; + let b = -1i32; + + // Neg + Neg => Max. + assert_eq!(to_bound::<_, _, i32>(a, b), i32::max_value()); + + let a = 1i32; + let b = -1i32; + + // Pos + Neg => Min. + assert_eq!(to_bound::<_, _, i32>(a, b), i32::min_value()); + + let a = -1i32; + let b = 1i32; + + // Neg + Pos => Min. + assert_eq!(to_bound::<_, _, i32>(a, b), i32::min_value()); + + let a = 1i32; + let b = -1i32; + + // Pos + Neg => Min (unsigned). + assert_eq!(to_bound::<_, _, u32>(a, b), 0); + } + + #[test] + #[should_panic(expected = "attempt to negate with overflow")] + fn op_neg_panics() { + let a = $name::min_value(); + let _ = -a; + } + + #[test] + fn op_neg_works() { + let a = $name::saturating_from_integer(5); + let b = -a; + + // Positive. + assert_eq!($name::saturating_from_integer(-5), b); + + let a = $name::saturating_from_integer(-5); + let b = -a; + + // Negative + assert_eq!($name::saturating_from_integer(5), b); + + let a = $name::max_value(); + let b = -a; + + // Max. + assert_eq!($name::min_value() + $name::from_inner(1), b); + + let a = $name::min_value() + $name::from_inner(1); + let b = -a; + + // Min. + assert_eq!($name::max_value(), b); + + let a = $name::zero(); + let b = -a; + + // Zero. + assert_eq!(a, b); + } + + #[test] + #[should_panic(expected = "attempt to add with overflow")] + fn op_add_panics() { + let a = $name::max_value(); + let b = 1.into(); + let _ = a + b; + } + + #[test] + fn op_add_works() { + let a = $name::saturating_from_rational(5, 2); + let b = $name::saturating_from_rational(1, 2); + + // Positive case: 6/2 = 3. + assert_eq!($name::saturating_from_integer(3), a + b); + + let b = $name::saturating_from_rational(1, -2); + + // Negative case: 4/2 = 2. + assert_eq!($name::saturating_from_integer(2), a + b); + } + + #[test] + #[should_panic(expected = "attempt to subtract with overflow")] + fn op_sub_panics() { + let a = $name::min_value(); + let b = 1.into(); + let _c = a - b; + } + + #[test] + fn op_sub_works() { + let a = $name::saturating_from_rational(5, 2); + let b = $name::saturating_from_rational(1, 2); + + // Negative case: 4/2 = 2. + assert_eq!($name::saturating_from_integer(2), a - b); + + let b = $name::saturating_from_rational(1, -2); + + // Positive case: 6/2 = 3. + assert_eq!($name::saturating_from_integer(3), a - b); + } + + #[test] + #[should_panic(expected = "attempt to multiply with overflow")] + fn op_mul_panics() { + let a = $name::max_value(); + let b = 2.into(); + let _c = a * b; + } + + #[test] + fn op_mul_works() { + let a = $name::saturating_from_integer(42); + let b = $name::saturating_from_integer(2); + assert_eq!($name::saturating_from_integer(84), a * b); + + let a = $name::saturating_from_integer(42); + let b = $name::saturating_from_integer(-2); + assert_eq!($name::saturating_from_integer(-84), a * b); + } + + #[test] + #[should_panic(expected = "attempt to divide by zero")] + fn op_div_panics_on_zero_divisor() { + let a = $name::saturating_from_integer(1); + let b = 0.into(); + let _c = a / b; + } + + #[test] + #[should_panic(expected = "attempt to divide with overflow")] + fn op_div_panics_on_overflow() { + let a = $name::min_value(); + let b = (-1).into(); + let _c = a / b; + } + + #[test] + fn op_div_works() { + let a = $name::saturating_from_integer(42); + let b = $name::saturating_from_integer(2); + assert_eq!($name::saturating_from_integer(21), a / b); + + let a = $name::saturating_from_integer(42); + let b = $name::saturating_from_integer(-2); + assert_eq!($name::saturating_from_integer(-21), a / b); + } + + #[test] + fn from_integer_works() { + let inner_max = <$name as FixedPointNumber>::Inner::max_value(); + let inner_min = <$name as FixedPointNumber>::Inner::min_value(); + let accuracy = $name::accuracy(); + + // Cases where integer fits. + let a = $name::saturating_from_integer(42); + assert_eq!(a.into_inner(), 42 * accuracy); + + let a = $name::saturating_from_integer(-42); + assert_eq!(a.into_inner(), -42 * accuracy); + + // Max/min integers that fit. + let a = $name::saturating_from_integer(inner_max / accuracy); + assert_eq!(a.into_inner(), (inner_max / accuracy) * accuracy); + + let a = $name::saturating_from_integer(inner_min / accuracy); + assert_eq!(a.into_inner(), (inner_min / accuracy) * accuracy); + + // Cases where integer doesn't fit, so it saturates. + let a = $name::saturating_from_integer(inner_max / accuracy + 1); + assert_eq!(a.into_inner(), inner_max); + + let a = $name::saturating_from_integer(inner_min / accuracy - 1); + assert_eq!(a.into_inner(), inner_min); + } + + #[test] + fn checked_from_integer_works() { + let inner_max = <$name as FixedPointNumber>::Inner::max_value(); + let inner_min = <$name as FixedPointNumber>::Inner::min_value(); + let accuracy = $name::accuracy(); + + // Cases where integer fits. + let a = $name::checked_from_integer(42) + .expect("42 * accuracy <= inner_max; qed"); + assert_eq!(a.into_inner(), 42 * accuracy); + + let a = $name::checked_from_integer(-42) + .expect("-42 * accuracy >= inner_min; qed"); + assert_eq!(a.into_inner(), -42 * accuracy); + + // Max/min integers that fit. + let a = $name::checked_from_integer(inner_max / accuracy) + .expect("(inner_max / accuracy) * accuracy <= inner_max; qed"); + assert_eq!(a.into_inner(), (inner_max / accuracy) * accuracy); + + let a = $name::checked_from_integer(inner_min / accuracy) + .expect("(inner_min / accuracy) * accuracy <= inner_min; qed"); + assert_eq!(a.into_inner(), (inner_min / accuracy) * accuracy); + + // Cases where integer doesn't fit, so it returns `None`. + let a = $name::checked_from_integer(inner_max / accuracy + 1); + assert_eq!(a, None); + + let a = $name::checked_from_integer(inner_min / accuracy - 1); + assert_eq!(a, None); + } + + #[test] + fn from_inner_works() { + let inner_max = <$name as FixedPointNumber>::Inner::max_value(); + let inner_min = <$name as FixedPointNumber>::Inner::min_value(); + + assert_eq!(max(), $name::from_inner(inner_max)); + assert_eq!(min(), $name::from_inner(inner_min)); + } + + #[test] + #[should_panic(expected = "attempt to divide by zero")] + fn saturating_from_rational_panics_on_zero_divisor() { + let _ = $name::saturating_from_rational(1, 0); + } + + #[test] + fn saturating_from_rational_works() { + let inner_max = <$name as FixedPointNumber>::Inner::max_value(); + let inner_min = <$name as FixedPointNumber>::Inner::min_value(); + let accuracy = $name::accuracy(); + + let a = $name::saturating_from_rational(5, 2); + + // Positive case: 2.5 + assert_eq!(a.into_inner(), 25 * accuracy / 10); + + let a = $name::saturating_from_rational(-5, 2); + + // Negative case: -2.5 + assert_eq!(a.into_inner(), -25 * accuracy / 10); + + let a = $name::saturating_from_rational(5, -2); + + // Other negative case: -2.5 + assert_eq!(a.into_inner(), -25 * accuracy / 10); + + let a = $name::saturating_from_rational(-5, -2); + + // Other positive case: 2.5 + assert_eq!(a.into_inner(), 25 * accuracy / 10); + + // Max - 1. + let a = $name::saturating_from_rational(inner_max - 1, accuracy); + assert_eq!(a.into_inner(), inner_max - 1); + + // Min + 1. + let a = $name::saturating_from_rational(inner_min + 1, accuracy); + assert_eq!(a.into_inner(), inner_min + 1); + + // Max. + let a = $name::saturating_from_rational(inner_max, accuracy); + assert_eq!(a.into_inner(), inner_max); + + // Min. + let a = $name::saturating_from_rational(inner_min, accuracy); + assert_eq!(a.into_inner(), inner_min); + + // Max + 1, saturates. + let a = $name::saturating_from_rational(inner_max as u128 + 1, accuracy); + assert_eq!(a.into_inner(), inner_max); + + // Min - 1, saturates. + let a = $name::saturating_from_rational(inner_max as u128 + 2, -accuracy); + assert_eq!(a.into_inner(), inner_min); + + // Zero. + let a = $name::saturating_from_rational(0, 1); + assert_eq!(a.into_inner(), 0); + + let a = $name::saturating_from_rational(inner_max, -accuracy); + assert_eq!(a.into_inner(), -inner_max); + + let a = $name::saturating_from_rational(inner_min, -accuracy); + assert_eq!(a.into_inner(), inner_max); + + let a = $name::saturating_from_rational(inner_min + 1, -accuracy); + assert_eq!(a.into_inner(), inner_max); + + let a = $name::saturating_from_rational(inner_max - 1, accuracy); + assert_eq!(a.into_inner(), inner_max - 1); + + let a = $name::saturating_from_rational(inner_min + 1, accuracy); + assert_eq!(a.into_inner(), inner_min + 1); + + let a = $name::saturating_from_rational(inner_max, 1); + assert_eq!(a.into_inner(), inner_max); + + let a = $name::saturating_from_rational(inner_min, 1); + assert_eq!(a.into_inner(), inner_min); + + let a = $name::saturating_from_rational(inner_min, -1); + assert_eq!(a.into_inner(), inner_max); + + let a = $name::saturating_from_rational(inner_max, -1); + assert_eq!(a.into_inner(), inner_min); + + let a = $name::saturating_from_rational(inner_max, inner_max); + assert_eq!(a.into_inner(), accuracy); + + let a = $name::saturating_from_rational(inner_min, inner_min); + assert_eq!(a.into_inner(), accuracy); + + let a = $name::saturating_from_rational(inner_max, -inner_max); + assert_eq!(a.into_inner(), -accuracy); + + let a = $name::saturating_from_rational(-inner_max, inner_max); + assert_eq!(a.into_inner(), -accuracy); + + let a = $name::saturating_from_rational(inner_max, 3 * accuracy); + assert_eq!(a.into_inner(), inner_max / 3); + + let a = $name::saturating_from_rational(inner_max, -3 * accuracy); + assert_eq!(a.into_inner(), -inner_max / 3); + + let a = $name::saturating_from_rational(inner_min, 2 * accuracy); + assert_eq!(a.into_inner(), inner_min / 2); + + let a = $name::saturating_from_rational(inner_min, accuracy / -3); + assert_eq!(a.into_inner(), inner_max); + + let a = $name::saturating_from_rational(inner_min, accuracy / 3); + assert_eq!(a.into_inner(), inner_min); + + let a = $name::saturating_from_rational(1, accuracy); + assert_eq!(a.into_inner(), 1); + + let a = $name::saturating_from_rational(1, -accuracy); + assert_eq!(a.into_inner(), -1); + + // Out of accuracy. + let a = $name::saturating_from_rational(1, accuracy + 1); + assert_eq!(a.into_inner(), 0); + + let a = $name::saturating_from_rational(1, -accuracy - 1); + assert_eq!(a.into_inner(), 0); + } + + #[test] + fn checked_from_rational_works() { + let inner_max = <$name as FixedPointNumber>::Inner::max_value(); + let inner_min = <$name as FixedPointNumber>::Inner::min_value(); + let accuracy = $name::accuracy(); + + // Divide by zero => None. + let a = $name::checked_from_rational(1, 0); + assert_eq!(a, None); + + // Max - 1. + let a = $name::checked_from_rational(inner_max - 1, accuracy).unwrap(); + assert_eq!(a.into_inner(), inner_max - 1); + + // Min + 1. + let a = $name::checked_from_rational(inner_min + 1, accuracy).unwrap(); + assert_eq!(a.into_inner(), inner_min + 1); + + // Max. + let a = $name::checked_from_rational(inner_max, accuracy).unwrap(); + assert_eq!(a.into_inner(), inner_max); + + // Min. + let a = $name::checked_from_rational(inner_min, accuracy).unwrap(); + assert_eq!(a.into_inner(), inner_min); + + // Max + 1 => Overflow => None. + let a = $name::checked_from_rational(inner_min, -accuracy); + assert_eq!(a, None); + + // Min - 1 => Underflow => None. + let a = $name::checked_from_rational(inner_max as u128 + 2, -accuracy); + assert_eq!(a, None); + + let a = $name::checked_from_rational(inner_max, 3 * accuracy).unwrap(); + assert_eq!(a.into_inner(), inner_max / 3); + + let a = $name::checked_from_rational(inner_max, -3 * accuracy).unwrap(); + assert_eq!(a.into_inner(), -inner_max / 3); + + let a = $name::checked_from_rational(inner_min, 2 * accuracy).unwrap(); + assert_eq!(a.into_inner(), inner_min / 2); + + let a = $name::checked_from_rational(inner_min, accuracy / -3); + assert_eq!(a, None); + + let a = $name::checked_from_rational(inner_min, accuracy / 3); + assert_eq!(a, None); + + let a = $name::checked_from_rational(1, accuracy).unwrap(); + assert_eq!(a.into_inner(), 1); + + let a = $name::checked_from_rational(1, -accuracy).unwrap(); + assert_eq!(a.into_inner(), -1); + + let a = $name::checked_from_rational(1, accuracy + 1).unwrap(); + assert_eq!(a.into_inner(), 0); + + let a = $name::checked_from_rational(1, -accuracy - 1).unwrap(); + assert_eq!(a.into_inner(), 0); + } + + #[test] + fn checked_mul_int_works() { + let a = $name::saturating_from_integer(2); + // Max - 1. + assert_eq!(a.checked_mul_int((i128::max_value() - 1) / 2), Some(i128::max_value() - 1)); + // Max. + assert_eq!(a.checked_mul_int(i128::max_value() / 2), Some(i128::max_value() - 1)); + // Max + 1 => None. + assert_eq!(a.checked_mul_int(i128::max_value() / 2 + 1), None); + + // Min - 1. + assert_eq!(a.checked_mul_int((i128::min_value() + 1) / 2), Some(i128::min_value() + 2)); + // Min. + assert_eq!(a.checked_mul_int(i128::min_value() / 2), Some(i128::min_value())); + // Min + 1 => None. + assert_eq!(a.checked_mul_int(i128::min_value() / 2 - 1), None); + + let a = $name::saturating_from_rational(1, 2); + assert_eq!(a.checked_mul_int(42i128), Some(21)); + assert_eq!(a.checked_mul_int(i128::max_value()), Some(i128::max_value() / 2)); + assert_eq!(a.checked_mul_int(i128::min_value()), Some(i128::min_value() / 2)); + + let b = $name::saturating_from_rational(1, -2); + assert_eq!(b.checked_mul_int(42i128), Some(-21)); + assert_eq!(b.checked_mul_int(u128::max_value()), None); + assert_eq!(b.checked_mul_int(i128::max_value()), Some(i128::max_value() / -2)); + assert_eq!(b.checked_mul_int(i128::min_value()), Some(i128::min_value() / -2)); + + let c = $name::saturating_from_integer(255); + assert_eq!(c.checked_mul_int(2i8), None); + assert_eq!(c.checked_mul_int(2i128), Some(510)); + assert_eq!(c.checked_mul_int(i128::max_value()), None); + assert_eq!(c.checked_mul_int(i128::min_value()), None); + } + + #[test] + fn saturating_mul_int_works() { + let a = $name::saturating_from_integer(2); + // Max - 1. + assert_eq!(a.saturating_mul_int((i128::max_value() - 1) / 2), i128::max_value() - 1); + // Max. + assert_eq!(a.saturating_mul_int(i128::max_value() / 2), i128::max_value() - 1); + // Max + 1 => saturates to max. + assert_eq!(a.saturating_mul_int(i128::max_value() / 2 + 1), i128::max_value()); + + // Min - 1. + assert_eq!(a.saturating_mul_int((i128::min_value() + 1) / 2), i128::min_value() + 2); + // Min. + assert_eq!(a.saturating_mul_int(i128::min_value() / 2), i128::min_value()); + // Min + 1 => saturates to min. + assert_eq!(a.saturating_mul_int(i128::min_value() / 2 - 1), i128::min_value()); + + let a = $name::saturating_from_rational(1, 2); + assert_eq!(a.saturating_mul_int(42i32), 21); + assert_eq!(a.saturating_mul_int(i128::max_value()), i128::max_value() / 2); + assert_eq!(a.saturating_mul_int(i128::min_value()), i128::min_value() / 2); + + let b = $name::saturating_from_rational(1, -2); + assert_eq!(b.saturating_mul_int(42i32), -21); + assert_eq!(b.saturating_mul_int(i128::max_value()), i128::max_value() / -2); + assert_eq!(b.saturating_mul_int(i128::min_value()), i128::min_value() / -2); + assert_eq!(b.saturating_mul_int(u128::max_value()), u128::min_value()); + + let c = $name::saturating_from_integer(255); + assert_eq!(c.saturating_mul_int(2i8), i8::max_value()); + assert_eq!(c.saturating_mul_int(-2i8), i8::min_value()); + assert_eq!(c.saturating_mul_int(i128::max_value()), i128::max_value()); + assert_eq!(c.saturating_mul_int(i128::min_value()), i128::min_value()); + } + + #[test] + fn checked_mul_works() { + let inner_max = <$name as FixedPointNumber>::Inner::max_value(); + let inner_min = <$name as FixedPointNumber>::Inner::min_value(); + + let a = $name::saturating_from_integer(2); + + // Max - 1. + let b = $name::from_inner(inner_max - 1); + assert_eq!(a.checked_mul(&(b/2.into())), Some(b)); + + // Max. + let c = $name::from_inner(inner_max); + assert_eq!(a.checked_mul(&(c/2.into())), Some(b)); + + // Max + 1 => None. + let e = $name::from_inner(1); + assert_eq!(a.checked_mul(&(c/2.into()+e)), None); + + // Min + 1. + let b = $name::from_inner(inner_min + 1) / 2.into(); + let c = $name::from_inner(inner_min + 2); + assert_eq!(a.checked_mul(&b), Some(c)); + + // Min. + let b = $name::from_inner(inner_min) / 2.into(); + let c = $name::from_inner(inner_min); + assert_eq!(a.checked_mul(&b), Some(c)); + + // Min - 1 => None. + let b = $name::from_inner(inner_min) / 2.into() - $name::from_inner(1); + assert_eq!(a.checked_mul(&b), None); + + let a = $name::saturating_from_rational(1, 2); + let b = $name::saturating_from_rational(1, -2); + let c = $name::saturating_from_integer(255); + + assert_eq!(a.checked_mul(&42.into()), Some(21.into())); + assert_eq!(b.checked_mul(&42.into()), Some((-21).into())); + assert_eq!(c.checked_mul(&2.into()), Some(510.into())); + + assert_eq!(b.checked_mul(&$name::max_value()), $name::max_value().checked_div(&(-2).into())); + assert_eq!(b.checked_mul(&$name::min_value()), $name::min_value().checked_div(&(-2).into())); + + assert_eq!(c.checked_mul(&$name::max_value()), None); + assert_eq!(c.checked_mul(&$name::min_value()), None); + + assert_eq!(a.checked_mul(&$name::max_value()), $name::max_value().checked_div(&2.into())); + assert_eq!(a.checked_mul(&$name::min_value()), $name::min_value().checked_div(&2.into())); + } + + #[test] + fn checked_div_int_works() { + let inner_max = <$name as FixedPointNumber>::Inner::max_value(); + let inner_min = <$name as FixedPointNumber>::Inner::min_value(); + let accuracy = $name::accuracy(); + + let a = $name::from_inner(inner_max); + let b = $name::from_inner(inner_min); + let c = $name::zero(); + let d = $name::one(); + let e = $name::saturating_from_integer(6); + let f = $name::saturating_from_integer(5); + + assert_eq!(e.checked_div_int(2.into()), Some(3)); + assert_eq!(f.checked_div_int(2.into()), Some(2)); + + assert_eq!(a.checked_div_int(i128::max_value()), Some(0)); + assert_eq!(a.checked_div_int(2), Some(inner_max / (2 * accuracy))); + assert_eq!(a.checked_div_int(inner_max / accuracy), Some(1)); + assert_eq!(a.checked_div_int(1i8), None); + + assert_eq!(a.checked_div_int(-2), Some(-inner_max / (2 * accuracy))); + assert_eq!(a.checked_div_int(inner_max / -accuracy), Some(-1)); + + assert_eq!(b.checked_div_int(i128::min_value()), Some(0)); + assert_eq!(b.checked_div_int(2), Some(inner_min / (2 * accuracy))); + assert_eq!(b.checked_div_int(inner_min / accuracy), Some(1)); + assert_eq!(b.checked_div_int(1i8), None); + + assert_eq!(b.checked_div_int(-2), Some(-(inner_min / (2 * accuracy)))); + assert_eq!(b.checked_div_int(-(inner_min / accuracy)), Some(-1)); + + assert_eq!(c.checked_div_int(1), Some(0)); + assert_eq!(c.checked_div_int(i128::max_value()), Some(0)); + assert_eq!(c.checked_div_int(i128::min_value()), Some(0)); + assert_eq!(c.checked_div_int(1i8), Some(0)); + + assert_eq!(d.checked_div_int(1), Some(1)); + assert_eq!(d.checked_div_int(i32::max_value()), Some(0)); + assert_eq!(d.checked_div_int(i32::min_value()), Some(0)); + assert_eq!(d.checked_div_int(1i8), Some(1)); + + assert_eq!(a.checked_div_int(0), None); + assert_eq!(b.checked_div_int(0), None); + assert_eq!(c.checked_div_int(0), None); + assert_eq!(d.checked_div_int(0), None); + } + + #[test] + #[should_panic(expected = "attempt to divide by zero")] + fn saturating_div_int_panics_when_divisor_is_zero() { + let _ = $name::one().saturating_div_int(0); + } + + #[test] + fn saturating_div_int_works() { + let inner_max = <$name as FixedPointNumber>::Inner::max_value(); + let inner_min = <$name as FixedPointNumber>::Inner::min_value(); + let accuracy = $name::accuracy(); + + let a = $name::saturating_from_integer(5); + assert_eq!(a.saturating_div_int(2), 2); + + let a = $name::saturating_from_integer(5); + assert_eq!(a.saturating_div_int(-2), -2); + + let a = $name::min_value(); + assert_eq!(a.saturating_div_int(-1i128), (inner_max / accuracy) as i128); + + let a = $name::min_value(); + assert_eq!(a.saturating_div_int(1i128), (inner_min / accuracy) as i128); + } + + #[test] + fn saturating_abs_works() { + let inner_max = <$name as FixedPointNumber>::Inner::max_value(); + let inner_min = <$name as FixedPointNumber>::Inner::min_value(); + + assert_eq!($name::from_inner(inner_min).saturating_abs(), $name::max_value()); + assert_eq!($name::from_inner(inner_max).saturating_abs(), $name::max_value()); + assert_eq!($name::zero().saturating_abs(), 0.into()); + assert_eq!($name::saturating_from_rational(-1, 2).saturating_abs(), (1, 2).into()); + } + + #[test] + fn saturating_mul_acc_int_works() { + assert_eq!($name::zero().saturating_mul_acc_int(42i8), 42i8); + assert_eq!($name::one().saturating_mul_acc_int(42i8), 2 * 42i8); + + assert_eq!($name::one().saturating_mul_acc_int(i128::max_value()), i128::max_value()); + assert_eq!($name::one().saturating_mul_acc_int(i128::min_value()), i128::min_value()); + + assert_eq!($name::one().saturating_mul_acc_int(u128::max_value() / 2), u128::max_value() - 1); + assert_eq!($name::one().saturating_mul_acc_int(u128::min_value()), u128::min_value()); + + let a = $name::saturating_from_rational(-1, 2); + assert_eq!(a.saturating_mul_acc_int(42i8), 21i8); + assert_eq!(a.saturating_mul_acc_int(u128::max_value() - 1), u128::max_value() - 1); + } + + #[test] + fn saturating_pow_should_work() { + assert_eq!($name::saturating_from_integer(2).saturating_pow(0), $name::saturating_from_integer(1)); + assert_eq!($name::saturating_from_integer(2).saturating_pow(1), $name::saturating_from_integer(2)); + assert_eq!($name::saturating_from_integer(2).saturating_pow(2), $name::saturating_from_integer(4)); + assert_eq!($name::saturating_from_integer(2).saturating_pow(3), $name::saturating_from_integer(8)); + assert_eq!($name::saturating_from_integer(2).saturating_pow(50), + $name::saturating_from_integer(1125899906842624i64)); + + // Saturating. + assert_eq!($name::saturating_from_integer(2).saturating_pow(68), $name::max_value()); + + assert_eq!($name::saturating_from_integer(1).saturating_pow(1000), (1).into()); + assert_eq!($name::saturating_from_integer(-1).saturating_pow(1000), (1).into()); + assert_eq!($name::saturating_from_integer(-1).saturating_pow(1001), (-1).into()); + assert_eq!($name::saturating_from_integer(1).saturating_pow(usize::max_value()), (1).into()); + assert_eq!($name::saturating_from_integer(-1).saturating_pow(usize::max_value()), (-1).into()); + assert_eq!($name::saturating_from_integer(-1).saturating_pow(usize::max_value() - 1), (1).into()); + + assert_eq!($name::saturating_from_integer(114209).saturating_pow(5), $name::max_value()); + + assert_eq!($name::saturating_from_integer(1).saturating_pow(usize::max_value()), (1).into()); + assert_eq!($name::saturating_from_integer(0).saturating_pow(usize::max_value()), (0).into()); + assert_eq!($name::saturating_from_integer(2).saturating_pow(usize::max_value()), $name::max_value()); + } + + #[test] + fn checked_div_works() { + let inner_max = <$name as FixedPointNumber>::Inner::max_value(); + let inner_min = <$name as FixedPointNumber>::Inner::min_value(); + + let a = $name::from_inner(inner_max); + let b = $name::from_inner(inner_min); + let c = $name::zero(); + let d = $name::one(); + let e = $name::saturating_from_integer(6); + let f = $name::saturating_from_integer(5); + + assert_eq!(e.checked_div(&2.into()), Some(3.into())); + assert_eq!(f.checked_div(&2.into()), Some((5, 2).into())); + + assert_eq!(a.checked_div(&inner_max.into()), Some(1.into())); + assert_eq!(a.checked_div(&2.into()), Some($name::from_inner(inner_max / 2))); + assert_eq!(a.checked_div(&$name::max_value()), Some(1.into())); + assert_eq!(a.checked_div(&d), Some(a)); + + assert_eq!(a.checked_div(&(-2).into()), Some($name::from_inner(-inner_max / 2))); + assert_eq!(a.checked_div(&-$name::max_value()), Some((-1).into())); + + assert_eq!(b.checked_div(&b), Some($name::one())); + assert_eq!(b.checked_div(&2.into()), Some($name::from_inner(inner_min / 2))); + + assert_eq!(b.checked_div(&(-2).into()), Some($name::from_inner(inner_min / -2))); + assert_eq!(b.checked_div(&a), Some((-1).into())); + + assert_eq!(c.checked_div(&1.into()), Some(0.into())); + assert_eq!(c.checked_div(&$name::max_value()), Some(0.into())); + assert_eq!(c.checked_div(&$name::min_value()), Some(0.into())); + + assert_eq!(d.checked_div(&1.into()), Some(1.into())); + + assert_eq!(a.checked_div(&$name::one()), Some(a)); + assert_eq!(b.checked_div(&$name::one()), Some(b)); + assert_eq!(c.checked_div(&$name::one()), Some(c)); + assert_eq!(d.checked_div(&$name::one()), Some(d)); + + assert_eq!(a.checked_div(&$name::zero()), None); + assert_eq!(b.checked_div(&$name::zero()), None); + assert_eq!(c.checked_div(&$name::zero()), None); + assert_eq!(d.checked_div(&$name::zero()), None); + } + + #[test] + fn trunc_works() { + let n = $name::saturating_from_rational(5, 2).trunc(); + assert_eq!(n, $name::saturating_from_integer(2)); + + let n = $name::saturating_from_rational(-5, 2).trunc(); + assert_eq!(n, $name::saturating_from_integer(-2)); + } + + #[test] + fn frac_works() { + let n = $name::saturating_from_rational(5, 2); + let i = n.trunc(); + let f = n.frac(); + + assert_eq!(n, i + f); + + let n = $name::saturating_from_rational(-5, 2); + let i = n.trunc(); + let f = n.frac(); + + assert_eq!(n, i - f); + + let n = $name::saturating_from_rational(5, 2) + .frac() + .saturating_mul(10.into()); + assert_eq!(n, 5.into()); + + let n = $name::saturating_from_rational(1, 2) + .frac() + .saturating_mul(10.into()); + assert_eq!(n, 5.into()); + + // The sign is attached to the integer part unless it is zero. + let n = $name::saturating_from_rational(-5, 2) + .frac() + .saturating_mul(10.into()); + assert_eq!(n, 5.into()); + + let n = $name::saturating_from_rational(-1, 2) + .frac() + .saturating_mul(10.into()); + assert_eq!(n, (-5).into()); + } + + #[test] + fn ceil_works() { + let n = $name::saturating_from_rational(5, 2); + assert_eq!(n.ceil(), 3.into()); + + let n = $name::saturating_from_rational(-5, 2); + assert_eq!(n.ceil(), (-2).into()); + + // On the limits: + let n = $name::max_value(); + assert_eq!(n.ceil(), n.trunc()); + + let n = $name::min_value(); + assert_eq!(n.ceil(), n.trunc()); + } + + #[test] + fn floor_works() { + let n = $name::saturating_from_rational(5, 2); + assert_eq!(n.floor(), 2.into()); + + let n = $name::saturating_from_rational(-5, 2); + assert_eq!(n.floor(), (-3).into()); + + // On the limits: + let n = $name::max_value(); + assert_eq!(n.floor(), n.trunc()); + + let n = $name::min_value(); + assert_eq!(n.floor(), n.trunc()); + } + + #[test] + fn round_works() { + let n = $name::zero(); + assert_eq!(n.round(), n); + + let n = $name::one(); + assert_eq!(n.round(), n); + + let n = $name::saturating_from_rational(5, 2); + assert_eq!(n.round(), 3.into()); + + let n = $name::saturating_from_rational(-5, 2); + assert_eq!(n.round(), (-3).into()); + + // Saturating: + let n = $name::max_value(); + assert_eq!(n.round(), n.trunc()); + + let n = $name::min_value(); + assert_eq!(n.round(), n.trunc()); + + // On the limit: + + // floor(max - 1) + 0.33.. + let n = $name::max_value() + .saturating_sub(1.into()) + .trunc() + .saturating_add((1, 3).into()); + + assert_eq!(n.round(), ($name::max_value() - 1.into()).trunc()); + + // floor(min + 1) - 0.33.. + let n = $name::min_value() + .saturating_add(1.into()) + .trunc() + .saturating_sub((1, 3).into()); + + assert_eq!(n.round(), ($name::min_value() + 1.into()).trunc()); + + // floor(max - 1) + 0.5 + let n = $name::max_value() + .saturating_sub(1.into()) + .trunc() + .saturating_add((1, 2).into()); + + assert_eq!(n.round(), $name::max_value().trunc()); + + // floor(min + 1) - 0.5 + let n = $name::min_value() + .saturating_add(1.into()) + .trunc() + .saturating_sub((1, 2).into()); + + assert_eq!(n.round(), $name::min_value().trunc()); + } + + #[test] + fn perthing_into_works() { + let ten_percent_percent: $name = Percent::from_percent(10).into(); + assert_eq!(ten_percent_percent.into_inner(), $name::accuracy() / 10); + + let ten_percent_permill: $name = Permill::from_percent(10).into(); + assert_eq!(ten_percent_permill.into_inner(), $name::accuracy() / 10); + + let ten_percent_perbill: $name = Perbill::from_percent(10).into(); + assert_eq!(ten_percent_perbill.into_inner(), $name::accuracy() / 10); + + let ten_percent_perquintill: $name = Perquintill::from_percent(10).into(); + assert_eq!(ten_percent_perquintill.into_inner(), $name::accuracy() / 10); + } + + #[test] + fn fmt_should_work() { + let zero = $name::zero(); + assert_eq!(format!("{:?}", zero), format!("{}(0.{:0>weight$})", stringify!($name), 0, weight=precision())); + + let one = $name::one(); + assert_eq!(format!("{:?}", one), format!("{}(1.{:0>weight$})", stringify!($name), 0, weight=precision())); + + let neg = -$name::one(); + assert_eq!(format!("{:?}", neg), format!("{}(-1.{:0>weight$})", stringify!($name), 0, weight=precision())); + + let frac = $name::saturating_from_rational(1, 2); + assert_eq!(format!("{:?}", frac), format!("{}(0.{:0 Self { - Self(int.saturating_mul(DIV)) - } - - /// Accuracy of `Fixed128`. - pub const fn accuracy() -> i128 { - DIV - } - - /// Raw constructor. Equal to `parts / DIV`. - pub const fn from_parts(parts: i128) -> Self { - Self(parts) - } - - /// Creates self from a rational number. Equal to `n/d`. - /// - /// Note that this might be lossy. Only use this if you are sure that `n * DIV` can fit into an - /// i128. - pub fn from_rational>(n: N, d: NonZeroI128) -> Self { - let n = n.unique_saturated_into(); - Self(n.saturating_mul(DIV.into()) / d.get()) - } - - /// Consume self and return the inner raw `i128` value. - /// - /// Note this is a low level function, as the returned value is represented with accuracy. - pub fn deconstruct(self) -> i128 { - self.0 - } - - /// Takes the reciprocal(inverse) of Fixed128, 1/x - pub fn recip(&self) -> Option { - Self::from_natural(1i128).checked_div(self) - } - - /// Checked add. Same semantic to `num_traits::CheckedAdd`. - pub fn checked_add(&self, rhs: &Self) -> Option { - self.0.checked_add(rhs.0).map(Self) - } - - /// Checked sub. Same semantic to `num_traits::CheckedSub`. - pub fn checked_sub(&self, rhs: &Self) -> Option { - self.0.checked_sub(rhs.0).map(Self) - } - - /// Checked mul. Same semantic to `num_traits::CheckedMul`. - pub fn checked_mul(&self, rhs: &Self) -> Option { - let signum = self.0.signum() * rhs.0.signum(); - let mut lhs = self.0; - if lhs.is_negative() { - lhs = lhs.saturating_mul(-1); - } - let mut rhs: i128 = rhs.0.saturated_into(); - if rhs.is_negative() { - rhs = rhs.saturating_mul(-1); - } - - U256::from(lhs) - .checked_mul(U256::from(rhs)) - .and_then(|n| n.checked_div(U256::from(DIV))) - .and_then(|n| TryInto::::try_into(n).ok()) - .map(|n| Self(n * signum)) - } - - /// Checked div. Same semantic to `num_traits::CheckedDiv`. - pub fn checked_div(&self, rhs: &Self) -> Option { - if rhs.0.signum() == 0 { - return None; - } - if self.0 == 0 { - return Some(*self); - } - - let signum = self.0.signum() / rhs.0.signum(); - let mut lhs: i128 = self.0; - if lhs.is_negative() { - lhs = lhs.saturating_mul(-1); - } - let mut rhs: i128 = rhs.0.saturated_into(); - if rhs.is_negative() { - rhs = rhs.saturating_mul(-1); - } - - U256::from(lhs) - .checked_mul(U256::from(DIV)) - .and_then(|n| n.checked_div(U256::from(rhs))) - .and_then(|n| TryInto::::try_into(n).ok()) - .map(|n| Self(n * signum)) - } - - /// Checked mul for int type `N`. - pub fn checked_mul_int(&self, other: &N) -> Option - where - N: Copy + TryFrom + TryInto, - { - N::try_into(*other).ok().and_then(|rhs| { - let mut lhs = self.0; - if lhs.is_negative() { - lhs = lhs.saturating_mul(-1); - } - let mut rhs: i128 = rhs.saturated_into(); - let signum = self.0.signum() * rhs.signum(); - if rhs.is_negative() { - rhs = rhs.saturating_mul(-1); - } - - U256::from(lhs) - .checked_mul(U256::from(rhs)) - .and_then(|n| n.checked_div(U256::from(DIV))) - .and_then(|n| TryInto::::try_into(n).ok()) - .and_then(|n| TryInto::::try_into(n * signum).ok()) - }) - } - - /// Checked mul for int type `N`. - pub fn saturating_mul_int(&self, other: &N) -> N - where - N: Copy + TryFrom + TryInto + Bounded, - { - self.checked_mul_int(other).unwrap_or_else(|| { - N::try_into(*other) - .map(|n| n.signum()) - .map(|n| n * self.0.signum()) - .map(|signum| { - if signum.is_negative() { - Bounded::min_value() - } else { - Bounded::max_value() - } - }) - .unwrap_or(Bounded::max_value()) - }) - } - - /// Checked div for int type `N`. - pub fn checked_div_int(&self, other: &N) -> Option - where - N: Copy + TryFrom + TryInto, - { - N::try_into(*other) - .ok() - .and_then(|n| self.0.checked_div(n)) - .and_then(|n| n.checked_div(DIV)) - .and_then(|n| TryInto::::try_into(n).ok()) - } - - pub fn zero() -> Self { - Self(0) - } - - pub fn is_zero(&self) -> bool { - self.0 == 0 - } - - /// Saturating absolute value. Returning MAX if `parts` == i128::MIN instead of overflowing. - pub fn saturating_abs(&self) -> Self { - if self.0 == i128::min_value() { - return Fixed128::max_value(); - } - - if self.0.is_negative() { - Fixed128::from_parts(self.0 * -1) - } else { - *self - } - } - - pub fn is_positive(&self) -> bool { - self.0.is_positive() - } - - pub fn is_negative(&self) -> bool { - self.0.is_negative() - } - - /// 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 u128; - let positive = self.0 > 0; - // safe to convert as absolute value. - let parts = self.0.checked_abs().map(|v| v as u128).unwrap_or(i128::max_value() as u128 + 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 u64. - let perquintill_parts = (parts % div) as u64; - - let n = int.clone().saturating_mul(natural_parts); - let p = Perquintill::from_parts(perquintill_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) - } - } -} - -/// Note that this is a standard, _potentially-panicking_, implementation. Use `Saturating` trait -/// for safe addition. -impl ops::Add for Fixed128 { - 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 Fixed128 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - Self(self.0 - rhs.0) - } -} - -impl Saturating for Fixed128 { - fn saturating_add(self, rhs: Self) -> Self { - Self(self.0.saturating_add(rhs.0)) - } - - fn saturating_sub(self, rhs: Self) -> Self { - Self(self.0.saturating_sub(rhs.0)) - } - - fn saturating_mul(self, rhs: Self) -> Self { - self.checked_mul(&rhs).unwrap_or_else(|| { - if (self.0.signum() * rhs.0.signum()).is_negative() { - Bounded::min_value() - } else { - Bounded::max_value() - } - }) - } - - fn saturating_pow(self, exp: usize) -> Self { - if exp == 0 { - return Self::from_natural(1); - } - - let exp = exp as u64; - let msb_pos = 64 - exp.leading_zeros(); - - let mut result = Self::from_natural(1); - let mut pow_val = self; - for i in 0..msb_pos { - if ((1 << i) & exp) > 0 { - result = result.saturating_mul(pow_val); - } - pow_val = pow_val.saturating_mul(pow_val); - } - result - } -} - -impl Bounded for Fixed128 { - fn min_value() -> Self { - Self(Bounded::min_value()) - } - - fn max_value() -> Self { - Self(Bounded::max_value()) - } -} - -impl fmt::Debug for Fixed128 { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let integral = { - let int = self.0 / DIV; - let signum_for_zero = if int == 0 && self.is_negative() { "-" } else { "" }; - format!("{}{}", signum_for_zero, int) - }; - let fractional = format!("{:0>18}", (self.0 % DIV).abs()); - write!(f, "Fixed128({}.{})", integral, fractional) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { - Ok(()) - } -} - -impl From

for Fixed128 { - fn from(val: P) -> Self { - let accuracy = P::ACCURACY.saturated_into().max(1) as i128; - let value = val.deconstruct().saturated_into() as i128; - Fixed128::from_rational(value, NonZeroI128::new(accuracy).unwrap()) - } -} - -#[cfg(feature = "std")] -impl Fixed128 { - fn i128_str(&self) -> String { - format!("{}", &self.0) - } - - fn try_from_i128_str(s: &str) -> Result { - let parts: i128 = s.parse().map_err(|_| "invalid string input")?; - Ok(Self::from_parts(parts)) - } -} - -// Manual impl `Serialize` as serde_json does not support i128. -// TODO: remove impl if issue https://github.com/serde-rs/json/issues/548 fixed. -#[cfg(feature = "std")] -impl Serialize for Fixed128 { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&self.i128_str()) - } -} - -// Manual impl `Serialize` as serde_json does not support i128. -// TODO: remove impl if issue https://github.com/serde-rs/json/issues/548 fixed. -#[cfg(feature = "std")] -impl<'de> Deserialize<'de> for Fixed128 { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let s = String::deserialize(deserializer)?; - Fixed128::try_from_i128_str(&s).map_err(|err_str| de::Error::custom(err_str)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::{Perbill, Percent, Permill, Perquintill}; - - fn max() -> Fixed128 { - Fixed128::max_value() - } - - fn min() -> Fixed128 { - Fixed128::min_value() - } - - #[test] - fn fixed128_semantics() { - let a = Fixed128::from_rational(5, NonZeroI128::new(2).unwrap()); - let b = Fixed128::from_rational(10, NonZeroI128::new(4).unwrap()); - assert_eq!(a.0, 5 * DIV / 2); - assert_eq!(a, b); - - let a = Fixed128::from_rational(-5, NonZeroI128::new(1).unwrap()); - assert_eq!(a, Fixed128::from_natural(-5)); - - let a = Fixed128::from_rational(5, NonZeroI128::new(-1).unwrap()); - assert_eq!(a, Fixed128::from_natural(-5)); - - // biggest value that can be created. - assert_ne!(max(), Fixed128::from_natural(170_141_183_460_469_231_731)); - assert_eq!(max(), Fixed128::from_natural(170_141_183_460_469_231_732)); - - // the smallest value that can be created. - assert_ne!(min(), Fixed128::from_natural(-170_141_183_460_469_231_731)); - assert_eq!(min(), Fixed128::from_natural(-170_141_183_460_469_231_732)); - } - - #[test] - fn fixed128_operation() { - let a = Fixed128::from_natural(2); - let b = Fixed128::from_natural(1); - assert_eq!(a.checked_add(&b), Some(Fixed128::from_natural(1 + 2))); - assert_eq!(a.checked_sub(&b), Some(Fixed128::from_natural(2 - 1))); - assert_eq!(a.checked_mul(&b), Some(Fixed128::from_natural(1 * 2))); - assert_eq!( - a.checked_div(&b), - Some(Fixed128::from_rational(2, NonZeroI128::new(1).unwrap())) - ); - - let a = Fixed128::from_rational(5, NonZeroI128::new(2).unwrap()); - let b = Fixed128::from_rational(3, NonZeroI128::new(2).unwrap()); - assert_eq!( - a.checked_add(&b), - Some(Fixed128::from_rational(8, NonZeroI128::new(2).unwrap())) - ); - assert_eq!( - a.checked_sub(&b), - Some(Fixed128::from_rational(2, NonZeroI128::new(2).unwrap())) - ); - assert_eq!( - a.checked_mul(&b), - Some(Fixed128::from_rational(15, NonZeroI128::new(4).unwrap())) - ); - assert_eq!( - a.checked_div(&b), - Some(Fixed128::from_rational(10, NonZeroI128::new(6).unwrap())) - ); - - let a = Fixed128::from_natural(120); - assert_eq!(a.checked_div_int(&2i32), Some(60)); - - let a = Fixed128::from_rational(20, NonZeroI128::new(1).unwrap()); - assert_eq!(a.checked_div_int(&2i32), Some(10)); - - let a = Fixed128::from_natural(120); - assert_eq!(a.checked_mul_int(&2i32), Some(240)); - - let a = Fixed128::from_rational(1, NonZeroI128::new(2).unwrap()); - assert_eq!(a.checked_mul_int(&20i32), Some(10)); - - let a = Fixed128::from_rational(-1, NonZeroI128::new(2).unwrap()); - assert_eq!(a.checked_mul_int(&20i32), Some(-10)); - } - - #[test] - fn saturating_mul_should_work() { - let a = Fixed128::from_natural(-1); - assert_eq!(min().saturating_mul(a), max()); - - assert_eq!(Fixed128::from_natural(125).saturating_mul(a).deconstruct(), -125 * DIV); - - let a = Fixed128::from_rational(1, NonZeroI128::new(5).unwrap()); - assert_eq!(Fixed128::from_natural(125).saturating_mul(a).deconstruct(), 25 * DIV); - } - - #[test] - fn saturating_mul_int_works() { - let a = Fixed128::from_rational(10, NonZeroI128::new(1).unwrap()); - assert_eq!(a.saturating_mul_int(&i32::max_value()), i32::max_value()); - - let a = Fixed128::from_rational(-10, NonZeroI128::new(1).unwrap()); - assert_eq!(a.saturating_mul_int(&i32::max_value()), i32::min_value()); - - let a = Fixed128::from_rational(3, NonZeroI128::new(1).unwrap()); - assert_eq!(a.saturating_mul_int(&100i8), i8::max_value()); - - let a = Fixed128::from_rational(10, NonZeroI128::new(1).unwrap()); - assert_eq!(a.saturating_mul_int(&123i128), 1230); - - let a = Fixed128::from_rational(-10, NonZeroI128::new(1).unwrap()); - assert_eq!(a.saturating_mul_int(&123i128), -1230); - - assert_eq!(max().saturating_mul_int(&2i128), 340_282_366_920_938_463_463); - - assert_eq!(max().saturating_mul_int(&i128::min_value()), i128::min_value()); - - assert_eq!(min().saturating_mul_int(&i128::max_value()), i128::min_value()); - - assert_eq!(min().saturating_mul_int(&i128::min_value()), i128::max_value()); - } - - #[test] - fn zero_works() { - assert_eq!(Fixed128::zero(), Fixed128::from_natural(0)); - } - - #[test] - fn is_zero_works() { - assert!(Fixed128::zero().is_zero()); - assert!(!Fixed128::from_natural(1).is_zero()); - } - - #[test] - fn checked_div_with_zero_should_be_none() { - let a = Fixed128::from_natural(1); - let b = Fixed128::from_natural(0); - assert_eq!(a.checked_div(&b), None); - assert_eq!(b.checked_div(&a), Some(b)); - } - - #[test] - fn checked_div_int_with_zero_should_be_none() { - let a = Fixed128::from_natural(1); - assert_eq!(a.checked_div_int(&0i32), None); - let a = Fixed128::from_natural(0); - assert_eq!(a.checked_div_int(&1i32), Some(0)); - } - - #[test] - fn checked_div_with_zero_dividend_should_be_zero() { - let a = Fixed128::zero(); - let b = Fixed128::from_parts(1); - - assert_eq!(a.checked_div(&b), Some(Fixed128::zero())); - } - - #[test] - fn under_flow_should_be_none() { - let b = Fixed128::from_natural(1); - assert_eq!(min().checked_sub(&b), None); - } - - #[test] - fn over_flow_should_be_none() { - let a = Fixed128::from_parts(i128::max_value() - 1); - let b = Fixed128::from_parts(2); - assert_eq!(a.checked_add(&b), None); - - let a = Fixed128::max_value(); - let b = Fixed128::from_rational(2, NonZeroI128::new(1).unwrap()); - assert_eq!(a.checked_mul(&b), None); - - let a = Fixed128::from_natural(255); - let b = 2u8; - assert_eq!(a.checked_mul_int(&b), None); - - let a = Fixed128::from_natural(256); - let b = 1u8; - assert_eq!(a.checked_div_int(&b), None); - - let a = Fixed128::from_natural(256); - let b = -1i8; - assert_eq!(a.checked_div_int(&b), None); - } - - #[test] - fn checked_div_int_should_work() { - // 256 / 10 = 25 (25.6 as int = 25) - let a = Fixed128::from_natural(256); - let result = a.checked_div_int(&10i128).unwrap(); - assert_eq!(result, 25); - - // 256 / 100 = 2 (2.56 as int = 2) - let a = Fixed128::from_natural(256); - let result = a.checked_div_int(&100i128).unwrap(); - assert_eq!(result, 2); - - // 256 / 1000 = 0 (0.256 as int = 0) - let a = Fixed128::from_natural(256); - let result = a.checked_div_int(&1000i128).unwrap(); - assert_eq!(result, 0); - - // 256 / -1 = -256 - let a = Fixed128::from_natural(256); - let result = a.checked_div_int(&-1i128).unwrap(); - assert_eq!(result, -256); - - // -256 / -1 = 256 - let a = Fixed128::from_natural(-256); - let result = a.checked_div_int(&-1i128).unwrap(); - assert_eq!(result, 256); - - // 10 / -5 = -2 - let a = Fixed128::from_rational(20, NonZeroI128::new(2).unwrap()); - let result = a.checked_div_int(&-5i128).unwrap(); - assert_eq!(result, -2); - - // -170_141_183_460_469_231_731 / -2 = 85_070_591_730_234_615_865 - let result = min().checked_div_int(&-2i128).unwrap(); - assert_eq!(result, 85_070_591_730_234_615_865); - - // 85_070_591_730_234_615_865 * -2 = -170_141_183_460_469_231_730 - let result = Fixed128::from_natural(result).checked_mul_int(&-2i128).unwrap(); - assert_eq!(result, -170_141_183_460_469_231_730); - } - - #[test] - fn perthing_into_fixed_i128() { - let ten_percent_percent: Fixed128 = Percent::from_percent(10).into(); - assert_eq!(ten_percent_percent.deconstruct(), DIV / 10); - - let ten_percent_permill: Fixed128 = Permill::from_percent(10).into(); - assert_eq!(ten_percent_permill.deconstruct(), DIV / 10); - - let ten_percent_perbill: Fixed128 = Perbill::from_percent(10).into(); - assert_eq!(ten_percent_perbill.deconstruct(), DIV / 10); - - let ten_percent_perquintill: Fixed128 = Perquintill::from_percent(10).into(); - assert_eq!(ten_percent_perquintill.deconstruct(), DIV / 10); - } - - #[test] - fn recip_should_work() { - let a = Fixed128::from_natural(2); - assert_eq!( - a.recip(), - Some(Fixed128::from_rational(1, NonZeroI128::new(2).unwrap())) - ); - - let a = Fixed128::from_natural(2); - assert_eq!(a.recip().unwrap().checked_mul_int(&4i32), Some(2i32)); - - let a = Fixed128::from_rational(100, NonZeroI128::new(121).unwrap()); - assert_eq!( - a.recip(), - Some(Fixed128::from_rational(121, NonZeroI128::new(100).unwrap())) - ); - - let a = Fixed128::from_rational(1, NonZeroI128::new(2).unwrap()); - assert_eq!(a.recip().unwrap().checked_mul(&a), Some(Fixed128::from_natural(1))); - - let a = Fixed128::from_natural(0); - assert_eq!(a.recip(), None); - - let a = Fixed128::from_rational(-1, NonZeroI128::new(2).unwrap()); - assert_eq!(a.recip(), Some(Fixed128::from_natural(-2))); - } - - #[test] - fn serialize_deserialize_should_work() { - let two_point_five = Fixed128::from_rational(5, NonZeroI128::new(2).unwrap()); - let serialized = serde_json::to_string(&two_point_five).unwrap(); - assert_eq!(serialized, "\"2500000000000000000\""); - let deserialized: Fixed128 = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, two_point_five); - - let minus_two_point_five = Fixed128::from_rational(-5, NonZeroI128::new(2).unwrap()); - let serialized = serde_json::to_string(&minus_two_point_five).unwrap(); - assert_eq!(serialized, "\"-2500000000000000000\""); - let deserialized: Fixed128 = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, minus_two_point_five); - } - - #[test] - fn saturating_abs_should_work() { - // normal - assert_eq!(Fixed128::from_parts(1).saturating_abs(), Fixed128::from_parts(1)); - assert_eq!(Fixed128::from_parts(-1).saturating_abs(), Fixed128::from_parts(1)); - - // saturating - assert_eq!(Fixed128::min_value().saturating_abs(), Fixed128::max_value()); - } - - #[test] - fn is_positive_negative_should_work() { - let positive = Fixed128::from_parts(1); - assert!(positive.is_positive()); - assert!(!positive.is_negative()); - - let negative = Fixed128::from_parts(-1); - assert!(!negative.is_positive()); - assert!(negative.is_negative()); - - let zero = Fixed128::zero(); - assert!(!zero.is_positive()); - assert!(!zero.is_negative()); - } - - #[test] - fn fmt_should_work() { - let positive = Fixed128::from_parts(1000000000000000001); - assert_eq!(format!("{:?}", positive), "Fixed128(1.000000000000000001)"); - let negative = Fixed128::from_parts(-1000000000000000001); - assert_eq!(format!("{:?}", negative), "Fixed128(-1.000000000000000001)"); - - let positive_fractional = Fixed128::from_parts(1); - assert_eq!(format!("{:?}", positive_fractional), "Fixed128(0.000000000000000001)"); - let negative_fractional = Fixed128::from_parts(-1); - assert_eq!(format!("{:?}", negative_fractional), "Fixed128(-0.000000000000000001)"); - - let zero = Fixed128::zero(); - assert_eq!(format!("{:?}", zero), "Fixed128(0.000000000000000000)"); - } - - #[test] - fn saturating_pow_should_work() { - assert_eq!(Fixed128::from_natural(2).saturating_pow(0), Fixed128::from_natural(1)); - assert_eq!(Fixed128::from_natural(2).saturating_pow(1), Fixed128::from_natural(2)); - assert_eq!(Fixed128::from_natural(2).saturating_pow(2), Fixed128::from_natural(4)); - assert_eq!(Fixed128::from_natural(2).saturating_pow(3), Fixed128::from_natural(8)); - assert_eq!(Fixed128::from_natural(2).saturating_pow(50), Fixed128::from_natural(1125899906842624)); - - assert_eq!(Fixed128::from_natural(1).saturating_pow(1000), Fixed128::from_natural(1)); - assert_eq!(Fixed128::from_natural(-1).saturating_pow(1000), Fixed128::from_natural(1)); - assert_eq!(Fixed128::from_natural(-1).saturating_pow(1001), Fixed128::from_natural(-1)); - assert_eq!(Fixed128::from_natural(1).saturating_pow(usize::max_value()), Fixed128::from_natural(1)); - assert_eq!(Fixed128::from_natural(-1).saturating_pow(usize::max_value()), Fixed128::from_natural(-1)); - assert_eq!(Fixed128::from_natural(-1).saturating_pow(usize::max_value() - 1), Fixed128::from_natural(1)); - - assert_eq!(Fixed128::from_natural(114209).saturating_pow(4), Fixed128::from_natural(170137997018538053761)); - assert_eq!(Fixed128::from_natural(114209).saturating_pow(5), Fixed128::max_value()); - - assert_eq!(Fixed128::from_natural(1).saturating_pow(usize::max_value()), Fixed128::from_natural(1)); - assert_eq!(Fixed128::from_natural(0).saturating_pow(usize::max_value()), Fixed128::from_natural(0)); - assert_eq!(Fixed128::from_natural(2).saturating_pow(usize::max_value()), Fixed128::max_value()); - } -} diff --git a/primitives/arithmetic/src/fixed64.rs b/primitives/arithmetic/src/fixed64.rs deleted file mode 100644 index 14baad543cf7..000000000000 --- a/primitives/arithmetic/src/fixed64.rs +++ /dev/null @@ -1,382 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2019-2020 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use sp_std::{ - ops, prelude::*, - convert::{TryFrom, TryInto}, -}; -use codec::{Encode, Decode}; -use crate::{ - Perbill, - traits::{ - SaturatedConversion, CheckedSub, CheckedAdd, CheckedDiv, 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 - } - - /// Consume self and return the inner value. - pub fn into_inner(self) -> i64 { self.0 } - - /// 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) - } - } - - pub fn is_negative(&self) -> bool { - self.0.is_negative() - } -} - -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 { - let a = self.0 as i128; - let b = rhs.0 as i128; - let res = a * b / DIV as i128; - Self(res.saturated_into()) - } - - fn saturating_sub(self, rhs: Self) -> Self { - Self(self.0.saturating_sub(rhs.0)) - } - - fn saturating_pow(self, exp: usize) -> Self { - if exp == 0 { - return Self::from_natural(1); - } - - let exp = exp as u64; - let msb_pos = 64 - exp.leading_zeros(); - - let mut result = Self::from_natural(1); - let mut pow_val = self; - for i in 0..msb_pos { - if ((1 << i) & exp) > 0 { - result = result.saturating_mul(pow_val); - } - pow_val = pow_val.saturating_mul(pow_val); - } - result - } -} - -/// 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) - } -} - -/// 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) - } -} - -/// Use `CheckedDiv` trait for safe division. -impl ops::Div for Fixed64 { - type Output = Self; - - fn div(self, rhs: Self) -> Self::Output { - if rhs.0 == 0 { - panic!("attempt to divide by zero"); - } - let (n, d) = if rhs.0 < 0 { - (-self.0, rhs.0.abs() as u64) - } else { - (self.0, rhs.0 as u64) - }; - Fixed64::from_rational(n, d) - } -} - -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) - } -} - -impl CheckedDiv for Fixed64 { - fn checked_div(&self, rhs: &Self) -> Option { - if rhs.0 == 0 { - None - } else { - Some(*self / *rhs) - } - } -} - -impl sp_std::fmt::Debug for Fixed64 { - #[cfg(feature = "std")] - fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - let integral = { - let int = self.0 / DIV; - let signum_for_zero = if int == 0 && self.is_negative() { "-" } else { "" }; - format!("{}{}", signum_for_zero, int) - }; - let fractional = format!("{:0>9}", (self.0 % DIV).abs()); - write!(f, "Fixed64({}.{})", integral, fractional) - } - - #[cfg(not(feature = "std"))] - fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result { - Ok(()) - } -} - -#[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); - } - - #[test] - fn div_works() { - let a = Fixed64::from_rational(12, 10); - let b = Fixed64::from_rational(10, 1); - assert_eq!(a / b, Fixed64::from_rational(12, 100)); - - let a = Fixed64::from_rational(12, 10); - let b = Fixed64::from_rational(1, 100); - assert_eq!(a / b, Fixed64::from_rational(120, 1)); - - let a = Fixed64::from_rational(12, 100); - let b = Fixed64::from_rational(10, 1); - assert_eq!(a / b, Fixed64::from_rational(12, 1000)); - - let a = Fixed64::from_rational(12, 100); - let b = Fixed64::from_rational(1, 100); - assert_eq!(a / b, Fixed64::from_rational(12, 1)); - - let a = Fixed64::from_rational(-12, 10); - let b = Fixed64::from_rational(10, 1); - assert_eq!(a / b, Fixed64::from_rational(-12, 100)); - - let a = Fixed64::from_rational(12, 10); - let b = Fixed64::from_rational(-10, 1); - assert_eq!(a / b, Fixed64::from_rational(-12, 100)); - - let a = Fixed64::from_rational(-12, 10); - let b = Fixed64::from_rational(-10, 1); - assert_eq!(a / b, Fixed64::from_rational(12, 100)); - } - - #[test] - #[should_panic(expected = "attempt to divide by zero")] - fn div_zero() { - let a = Fixed64::from_rational(12, 10); - let b = Fixed64::from_natural(0); - let _ = a / b; - } - - #[test] - fn checked_div_zero() { - let a = Fixed64::from_rational(12, 10); - let b = Fixed64::from_natural(0); - assert_eq!(a.checked_div(&b), None); - } - - #[test] - fn checked_div_non_zero() { - let a = Fixed64::from_rational(12, 10); - let b = Fixed64::from_rational(1, 100); - assert_eq!(a.checked_div(&b), Some(Fixed64::from_rational(120, 1))); - } - - #[test] - fn saturating_mul_should_work() { - assert_eq!(Fixed64::from_natural(100).saturating_mul(Fixed64::from_natural(100)), Fixed64::from_natural(10000)); - } - - #[test] - fn saturating_pow_should_work() { - assert_eq!(Fixed64::from_natural(2).saturating_pow(0), Fixed64::from_natural(1)); - assert_eq!(Fixed64::from_natural(2).saturating_pow(1), Fixed64::from_natural(2)); - assert_eq!(Fixed64::from_natural(2).saturating_pow(2), Fixed64::from_natural(4)); - assert_eq!(Fixed64::from_natural(2).saturating_pow(3), Fixed64::from_natural(8)); - assert_eq!(Fixed64::from_natural(2).saturating_pow(20), Fixed64::from_natural(1048576)); - - assert_eq!(Fixed64::from_natural(1).saturating_pow(1000), Fixed64::from_natural(1)); - assert_eq!(Fixed64::from_natural(-1).saturating_pow(1000), Fixed64::from_natural(1)); - assert_eq!(Fixed64::from_natural(-1).saturating_pow(1001), Fixed64::from_natural(-1)); - assert_eq!(Fixed64::from_natural(1).saturating_pow(usize::max_value()), Fixed64::from_natural(1)); - assert_eq!(Fixed64::from_natural(-1).saturating_pow(usize::max_value()), Fixed64::from_natural(-1)); - assert_eq!(Fixed64::from_natural(-1).saturating_pow(usize::max_value() - 1), Fixed64::from_natural(1)); - - assert_eq!(Fixed64::from_natural(309).saturating_pow(4), Fixed64::from_natural(9_116_621_361)); - assert_eq!(Fixed64::from_natural(309).saturating_pow(5), Fixed64::from_parts(i64::max_value())); - - assert_eq!(Fixed64::from_natural(1).saturating_pow(usize::max_value()), Fixed64::from_natural(1)); - assert_eq!(Fixed64::from_natural(0).saturating_pow(usize::max_value()), Fixed64::from_natural(0)); - assert_eq!(Fixed64::from_natural(2).saturating_pow(usize::max_value()), Fixed64::from_parts(i64::max_value())); - } -} diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index e5e7139d7be4..0ac58b12fe0d 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -37,12 +37,10 @@ pub mod biguint; pub mod helpers_128bit; pub mod traits; mod per_things; -mod fixed64; -mod fixed128; +mod fixed; mod rational128; -pub use fixed64::Fixed64; -pub use fixed128::Fixed128; +pub use fixed::{FixedPointNumber, Fixed64, Fixed128}; pub use per_things::{PerThing, Percent, PerU16, Permill, Perbill, Perquintill}; pub use rational128::Rational128; diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index d8d789577ebf..7985f09a20f0 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -21,8 +21,8 @@ use sp_std::{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, checked_pow + Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv, CheckedNeg, + CheckedShl, CheckedShr, checked_pow, Signed }; use sp_std::ops::{ Add, Sub, Mul, Div, Rem, AddAssign, SubAssign, MulAssign, DivAssign, diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index e2a489515f50..79b91424598c 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -72,7 +72,7 @@ pub use sp_core::RuntimeDebug; /// Re-export top-level arithmetic stuff. pub use sp_arithmetic::{ Perquintill, Perbill, Permill, Percent, PerU16, Rational128, Fixed64, Fixed128, - PerThing, traits::SaturatedConversion, + PerThing, traits::SaturatedConversion, FixedPointNumber, }; /// Re-export 128 bit helpers. pub use sp_arithmetic::helpers_128bit;