mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 09:57:56 +00:00
Several tweaks needed for Governance 2.0 (#11124)
* Add stepped curve for referenda * Treasury SpendOrigin * Add tests * Better Origin Or-gating * Reciprocal curve * Tests for reciprical and rounding in PerThings * Tweaks and new quad curve * Const derivation of reciprocal curve parameters * Remove some unneeded code * Actually useful linear curve * Fixes * Provisional curves * Rejig 'turnout' as 'support' * Use TypedGet * Fixes * Enable curve's ceil to be configured * Formatting * Fixes * Fixes * Fixes * Remove EnsureOneOf * Fixes * Fixes * Fixes * Formatting * Fixes * Update frame/support/src/traits/dispatch.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Grumbles * Formatting * Fixes * APIs of VoteTally should include class * Fixes * Fix overlay prefix removal result * Second part of the overlay prefix removal fix. * Formatting * Fixes * Add some tests and make clear rounding algo * Fixes * Formatting * Revert questionable fix * Introduce test for kill_prefix * Fixes * Formatting * Fixes * Fix possible overflow * Docs * Add benchmark test * Formatting * Update frame/referenda/src/types.rs Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * Docs * Fixes * Use latest API in tests * Formatting * Whitespace * Use latest API in tests Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: Keith Yeung <kungfukeith11@gmail.com>
This commit is contained in:
@@ -18,12 +18,12 @@
|
||||
//! Decimal Fixed Point implementations for Substrate runtime.
|
||||
|
||||
use crate::{
|
||||
helpers_128bit::multiply_by_rational,
|
||||
helpers_128bit::{multiply_by_rational, multiply_by_rational_with_rounding, sqrt},
|
||||
traits::{
|
||||
Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedSub, One,
|
||||
SaturatedConversion, Saturating, UniqueSaturatedInto, Zero,
|
||||
},
|
||||
PerThing,
|
||||
PerThing, Perbill, Rounding, SignedRounding,
|
||||
};
|
||||
use codec::{CompactAs, Decode, Encode};
|
||||
use sp_std::{
|
||||
@@ -406,20 +406,326 @@ macro_rules! implement_fixed {
|
||||
}
|
||||
|
||||
impl $name {
|
||||
/// const version of `FixedPointNumber::from_inner`.
|
||||
/// Create a new instance from the given `inner` value.
|
||||
///
|
||||
/// `const` version of `FixedPointNumber::from_inner`.
|
||||
pub const fn from_inner(inner: $inner_type) -> Self {
|
||||
Self(inner)
|
||||
}
|
||||
|
||||
/// Return the instance's inner value.
|
||||
///
|
||||
/// `const` version of `FixedPointNumber::into_inner`.
|
||||
pub const fn into_inner(self) -> $inner_type {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Creates self from a `u32`.
|
||||
///
|
||||
/// WARNING: This is a `const` function designed for convenient use at build time and
|
||||
/// will panic on overflow. Ensure that any inputs are sensible.
|
||||
pub const fn from_u32(n: u32) -> Self {
|
||||
Self::from_inner((n as $inner_type) * $div)
|
||||
}
|
||||
|
||||
/// Convert from a `float` value.
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub fn from_float(x: f64) -> Self {
|
||||
Self((x * (<Self as FixedPointNumber>::DIV as f64)) as $inner_type)
|
||||
}
|
||||
|
||||
/// Convert from a `Perbill` value.
|
||||
pub const fn from_perbill(n: Perbill) -> Self {
|
||||
Self::from_rational(n.deconstruct() as u128, 1_000_000_000)
|
||||
}
|
||||
|
||||
/// Convert into a `Perbill` value. Will saturate if above one or below zero.
|
||||
pub const fn into_perbill(self) -> Perbill {
|
||||
if self.0 <= 0 {
|
||||
Perbill::zero()
|
||||
} else if self.0 >= $div {
|
||||
Perbill::one()
|
||||
} else {
|
||||
match multiply_by_rational_with_rounding(
|
||||
self.0 as u128,
|
||||
1_000_000_000,
|
||||
Self::DIV as u128,
|
||||
Rounding::NearestPrefDown,
|
||||
) {
|
||||
Some(value) => {
|
||||
if value > (u32::max_value() as u128) {
|
||||
panic!(
|
||||
"prior logic ensures 0<self.0<DIV; \
|
||||
multiply ensures 0<self.0<1000000000; \
|
||||
qed"
|
||||
);
|
||||
}
|
||||
Perbill::from_parts(value as u32)
|
||||
},
|
||||
None => Perbill::zero(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert into a `float` value.
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub fn to_float(self) -> f64 {
|
||||
self.0 as f64 / <Self as FixedPointNumber>::DIV as f64
|
||||
}
|
||||
|
||||
/// Attempt to convert into a `PerThing`. This will succeed iff `self` is at least zero
|
||||
/// and at most one. If it is out of bounds, it will result in an error returning the
|
||||
/// clamped value.
|
||||
pub fn try_into_perthing<P: PerThing>(self) -> Result<P, P> {
|
||||
if self < Self::zero() {
|
||||
Err(P::zero())
|
||||
} else if self > Self::one() {
|
||||
Err(P::one())
|
||||
} else {
|
||||
Ok(P::from_rational(self.0 as u128, $div))
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert into a `PerThing`. This will always succeed resulting in a
|
||||
/// clamped value if `self` is less than zero or greater than one.
|
||||
pub fn into_clamped_perthing<P: PerThing>(self) -> P {
|
||||
if self < Self::zero() {
|
||||
P::zero()
|
||||
} else if self > Self::one() {
|
||||
P::one()
|
||||
} else {
|
||||
P::from_rational(self.0 as u128, $div)
|
||||
}
|
||||
}
|
||||
|
||||
/// Negate the value.
|
||||
///
|
||||
/// WARNING: This is a `const` function designed for convenient use at build time and
|
||||
/// will panic on overflow. Ensure that any inputs are sensible.
|
||||
pub const fn neg(self) -> Self {
|
||||
Self(0 - self.0)
|
||||
}
|
||||
|
||||
/// Take the square root of a positive value.
|
||||
///
|
||||
/// WARNING: This is a `const` function designed for convenient use at build time and
|
||||
/// will panic on overflow. Ensure that any inputs are sensible.
|
||||
pub const fn sqrt(self) -> Self {
|
||||
match self.try_sqrt() {
|
||||
Some(v) => v,
|
||||
None => panic!("sqrt overflow or negative input"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the square root, rounding as desired. If it overflows or is negative, then
|
||||
/// `None` is returned.
|
||||
pub const fn try_sqrt(self) -> Option<Self> {
|
||||
if self.0 == 0 {
|
||||
return Some(Self(0))
|
||||
}
|
||||
if self.0 < 1 {
|
||||
return None
|
||||
}
|
||||
let v = self.0 as u128;
|
||||
|
||||
// Want x' = sqrt(x) where x = n/D and x' = n'/D (D is fixed)
|
||||
// Our prefered way is:
|
||||
// sqrt(n/D) = sqrt(nD / D^2) = sqrt(nD)/sqrt(D^2) = sqrt(nD)/D
|
||||
// ergo n' = sqrt(nD)
|
||||
// but this requires nD to fit into our type.
|
||||
// if nD doesn't fit then we can fall back on:
|
||||
// sqrt(nD) = sqrt(n)*sqrt(D)
|
||||
// computing them individually and taking the product at the end. we will lose some
|
||||
// precision though.
|
||||
let maybe_vd = u128::checked_mul(v, $div);
|
||||
let r = if let Some(vd) = maybe_vd { sqrt(vd) } else { sqrt(v) * sqrt($div) };
|
||||
Some(Self(r as $inner_type))
|
||||
}
|
||||
|
||||
/// Add a value and return the result.
|
||||
///
|
||||
/// WARNING: This is a `const` function designed for convenient use at build time and
|
||||
/// will panic on overflow. Ensure that any inputs are sensible.
|
||||
pub const fn add(self, rhs: Self) -> Self {
|
||||
Self(self.0 + rhs.0)
|
||||
}
|
||||
|
||||
/// Subtract a value and return the result.
|
||||
///
|
||||
/// WARNING: This is a `const` function designed for convenient use at build time and
|
||||
/// will panic on overflow. Ensure that any inputs are sensible.
|
||||
pub const fn sub(self, rhs: Self) -> Self {
|
||||
Self(self.0 - rhs.0)
|
||||
}
|
||||
|
||||
/// Multiply by a value and return the result.
|
||||
///
|
||||
/// Result will be rounded to the nearest representable value, rounding down if it is
|
||||
/// equidistant between two neighbours.
|
||||
///
|
||||
/// WARNING: This is a `const` function designed for convenient use at build time and
|
||||
/// will panic on overflow. Ensure that any inputs are sensible.
|
||||
pub const fn mul(self, rhs: Self) -> Self {
|
||||
match $name::const_checked_mul(self, rhs) {
|
||||
Some(v) => v,
|
||||
None => panic!("attempt to multiply with overflow"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Divide by a value and return the result.
|
||||
///
|
||||
/// Result will be rounded to the nearest representable value, rounding down if it is
|
||||
/// equidistant between two neighbours.
|
||||
///
|
||||
/// WARNING: This is a `const` function designed for convenient use at build time and
|
||||
/// will panic on overflow. Ensure that any inputs are sensible.
|
||||
pub const fn div(self, rhs: Self) -> Self {
|
||||
match $name::const_checked_div(self, rhs) {
|
||||
Some(v) => v,
|
||||
None => panic!("attempt to divide with overflow or NaN"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert into an `I129` format value.
|
||||
///
|
||||
/// WARNING: This is a `const` function designed for convenient use at build time and
|
||||
/// will panic on overflow. Ensure that any inputs are sensible.
|
||||
const fn into_i129(self) -> I129 {
|
||||
#[allow(unused_comparisons)]
|
||||
if self.0 < 0 {
|
||||
let value = match self.0.checked_neg() {
|
||||
Some(n) => n as u128,
|
||||
None => u128::saturating_add(<$inner_type>::max_value() as u128, 1),
|
||||
};
|
||||
I129 { value, negative: true }
|
||||
} else {
|
||||
I129 { value: self.0 as u128, negative: false }
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from an `I129` format value.
|
||||
///
|
||||
/// WARNING: This is a `const` function designed for convenient use at build time and
|
||||
/// will panic on overflow. Ensure that any inputs are sensible.
|
||||
const fn from_i129(n: I129) -> Option<Self> {
|
||||
let max_plus_one = u128::saturating_add(<$inner_type>::max_value() as u128, 1);
|
||||
#[allow(unused_comparisons)]
|
||||
let inner = if n.negative && <$inner_type>::min_value() < 0 && n.value == max_plus_one {
|
||||
<$inner_type>::min_value()
|
||||
} else {
|
||||
let unsigned_inner = n.value as $inner_type;
|
||||
if unsigned_inner as u128 != n.value || (unsigned_inner > 0) != (n.value > 0) {
|
||||
return None
|
||||
};
|
||||
if n.negative {
|
||||
match unsigned_inner.checked_neg() {
|
||||
Some(v) => v,
|
||||
None => return None,
|
||||
}
|
||||
} else {
|
||||
unsigned_inner
|
||||
}
|
||||
};
|
||||
Some(Self(inner))
|
||||
}
|
||||
|
||||
/// Calculate an approximation of a rational.
|
||||
///
|
||||
/// Result will be rounded to the nearest representable value, rounding down if it is
|
||||
/// equidistant between two neighbours.
|
||||
///
|
||||
/// WARNING: This is a `const` function designed for convenient use at build time and
|
||||
/// will panic on overflow. Ensure that any inputs are sensible.
|
||||
pub const fn from_rational(a: u128, b: u128) -> Self {
|
||||
Self::from_rational_with_rounding(a, b, Rounding::NearestPrefDown)
|
||||
}
|
||||
|
||||
/// Calculate an approximation of a rational with custom rounding.
|
||||
///
|
||||
/// WARNING: This is a `const` function designed for convenient use at build time and
|
||||
/// will panic on overflow. Ensure that any inputs are sensible.
|
||||
pub const fn from_rational_with_rounding(a: u128, b: u128, rounding: Rounding) -> Self {
|
||||
if b == 0 {
|
||||
panic!("attempt to divide by zero in from_rational")
|
||||
}
|
||||
match multiply_by_rational_with_rounding(Self::DIV as u128, a, b, rounding) {
|
||||
Some(value) => match Self::from_i129(I129 { value, negative: false }) {
|
||||
Some(x) => x,
|
||||
None => panic!("overflow in from_rational"),
|
||||
},
|
||||
None => panic!("overflow in from_rational"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiply by another value, returning `None` in the case of an error.
|
||||
///
|
||||
/// Result will be rounded to the nearest representable value, rounding down if it is
|
||||
/// equidistant between two neighbours.
|
||||
pub const fn const_checked_mul(self, other: Self) -> Option<Self> {
|
||||
self.const_checked_mul_with_rounding(other, SignedRounding::NearestPrefLow)
|
||||
}
|
||||
|
||||
/// Multiply by another value with custom rounding, returning `None` in the case of an
|
||||
/// error.
|
||||
///
|
||||
/// Result will be rounded to the nearest representable value, rounding down if it is
|
||||
/// equidistant between two neighbours.
|
||||
pub const fn const_checked_mul_with_rounding(
|
||||
self,
|
||||
other: Self,
|
||||
rounding: SignedRounding,
|
||||
) -> Option<Self> {
|
||||
let lhs = self.into_i129();
|
||||
let rhs = other.into_i129();
|
||||
let negative = lhs.negative != rhs.negative;
|
||||
|
||||
match multiply_by_rational_with_rounding(
|
||||
lhs.value,
|
||||
rhs.value,
|
||||
Self::DIV as u128,
|
||||
Rounding::from_signed(rounding, negative),
|
||||
) {
|
||||
Some(value) => Self::from_i129(I129 { value, negative }),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Divide by another value, returning `None` in the case of an error.
|
||||
///
|
||||
/// Result will be rounded to the nearest representable value, rounding down if it is
|
||||
/// equidistant between two neighbours.
|
||||
pub const fn const_checked_div(self, other: Self) -> Option<Self> {
|
||||
self.checked_rounding_div(other, SignedRounding::NearestPrefLow)
|
||||
}
|
||||
|
||||
/// Divide by another value with custom rounding, returning `None` in the case of an
|
||||
/// error.
|
||||
///
|
||||
/// Result will be rounded to the nearest representable value, rounding down if it is
|
||||
/// equidistant between two neighbours.
|
||||
pub const fn checked_rounding_div(
|
||||
self,
|
||||
other: Self,
|
||||
rounding: SignedRounding,
|
||||
) -> Option<Self> {
|
||||
if other.0 == 0 {
|
||||
return None
|
||||
}
|
||||
|
||||
let lhs = self.into_i129();
|
||||
let rhs = other.into_i129();
|
||||
let negative = lhs.negative != rhs.negative;
|
||||
|
||||
match multiply_by_rational_with_rounding(
|
||||
lhs.value,
|
||||
Self::DIV as u128,
|
||||
rhs.value,
|
||||
Rounding::from_signed(rounding, negative),
|
||||
) {
|
||||
Some(value) => Self::from_i129(I129 { value, negative }),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Saturating for $name {
|
||||
@@ -522,6 +828,10 @@ macro_rules! implement_fixed {
|
||||
let rhs: I129 = other.0.into();
|
||||
let negative = lhs.negative != rhs.negative;
|
||||
|
||||
// Note that this uses the old (well-tested) code with sign-ignorant rounding. This
|
||||
// is equivalent to the `SignedRounding::NearestPrefMinor`. This means it is
|
||||
// expected to give exactly the same result as `const_checked_div` when the result
|
||||
// is positive and a result up to one epsilon greater when it is negative.
|
||||
multiply_by_rational(lhs.value, Self::DIV as u128, rhs.value)
|
||||
.ok()
|
||||
.and_then(|value| from_i129(I129 { value, negative }))
|
||||
@@ -851,6 +1161,16 @@ macro_rules! implement_fixed {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn op_sqrt_works() {
|
||||
for i in 1..1_000i64 {
|
||||
let x = $name::saturating_from_rational(i, 1_000i64);
|
||||
assert_eq!((x * x).try_sqrt(), Some(x));
|
||||
let x = $name::saturating_from_rational(i, 1i64);
|
||||
assert_eq!((x * x).try_sqrt(), Some(x));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn op_div_works() {
|
||||
let a = $name::saturating_from_integer(42);
|
||||
@@ -1133,6 +1453,41 @@ macro_rules! implement_fixed {
|
||||
assert_eq!(a.into_inner(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_rational_works() {
|
||||
let inner_max: u128 = <$name as FixedPointNumber>::Inner::max_value() as u128;
|
||||
let inner_min: u128 = 0;
|
||||
let accuracy: u128 = $name::accuracy() as u128;
|
||||
|
||||
// Max - 1.
|
||||
let a = $name::from_rational(inner_max - 1, accuracy);
|
||||
assert_eq!(a.into_inner() as u128, inner_max - 1);
|
||||
|
||||
// Min + 1.
|
||||
let a = $name::from_rational(inner_min + 1, accuracy);
|
||||
assert_eq!(a.into_inner() as u128, inner_min + 1);
|
||||
|
||||
// Max.
|
||||
let a = $name::from_rational(inner_max, accuracy);
|
||||
assert_eq!(a.into_inner() as u128, inner_max);
|
||||
|
||||
// Min.
|
||||
let a = $name::from_rational(inner_min, accuracy);
|
||||
assert_eq!(a.into_inner() as u128, inner_min);
|
||||
|
||||
let a = $name::from_rational(inner_max, 3 * accuracy);
|
||||
assert_eq!(a.into_inner() as u128, inner_max / 3);
|
||||
|
||||
let a = $name::from_rational(1, accuracy);
|
||||
assert_eq!(a.into_inner() as u128, 1);
|
||||
|
||||
let a = $name::from_rational(1, accuracy + 1);
|
||||
assert_eq!(a.into_inner() as u128, 1);
|
||||
|
||||
let a = $name::from_rational_with_rounding(1, accuracy + 1, Rounding::Down);
|
||||
assert_eq!(a.into_inner() as u128, 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_mul_int_works() {
|
||||
let a = $name::saturating_from_integer(2);
|
||||
@@ -1272,6 +1627,76 @@ macro_rules! implement_fixed {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn const_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(2u32);
|
||||
|
||||
// Max - 1.
|
||||
let b = $name::from_inner(inner_max - 1);
|
||||
assert_eq!(a.const_checked_mul((b / 2.into())), Some(b));
|
||||
|
||||
// Max.
|
||||
let c = $name::from_inner(inner_max);
|
||||
assert_eq!(a.const_checked_mul((c / 2.into())), Some(b));
|
||||
|
||||
// Max + 1 => None.
|
||||
let e = $name::from_inner(1);
|
||||
assert_eq!(a.const_checked_mul((c / 2.into() + e)), None);
|
||||
|
||||
if $name::SIGNED {
|
||||
// Min + 1.
|
||||
let b = $name::from_inner(inner_min + 1) / 2.into();
|
||||
let c = $name::from_inner(inner_min + 2);
|
||||
assert_eq!(a.const_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.const_checked_mul(b), Some(c));
|
||||
|
||||
// Min - 1 => None.
|
||||
let b = $name::from_inner(inner_min) / 2.into() - $name::from_inner(1);
|
||||
assert_eq!(a.const_checked_mul(b), None);
|
||||
|
||||
let b = $name::saturating_from_rational(1i32, -2i32);
|
||||
let c = $name::saturating_from_integer(-21i32);
|
||||
let d = $name::saturating_from_integer(42);
|
||||
|
||||
assert_eq!(b.const_checked_mul(d), Some(c));
|
||||
|
||||
let minus_two = $name::saturating_from_integer(-2i32);
|
||||
assert_eq!(
|
||||
b.const_checked_mul($name::max_value()),
|
||||
$name::max_value().const_checked_div(minus_two)
|
||||
);
|
||||
assert_eq!(
|
||||
b.const_checked_mul($name::min_value()),
|
||||
$name::min_value().const_checked_div(minus_two)
|
||||
);
|
||||
|
||||
let c = $name::saturating_from_integer(255u32);
|
||||
assert_eq!(c.const_checked_mul($name::min_value()), None);
|
||||
}
|
||||
|
||||
let a = $name::saturating_from_rational(1i32, 2i32);
|
||||
let c = $name::saturating_from_integer(255i32);
|
||||
|
||||
assert_eq!(a.const_checked_mul(42.into()), Some(21.into()));
|
||||
assert_eq!(c.const_checked_mul(2.into()), Some(510.into()));
|
||||
assert_eq!(c.const_checked_mul($name::max_value()), None);
|
||||
assert_eq!(
|
||||
a.const_checked_mul($name::max_value()),
|
||||
$name::max_value().checked_div(&2.into())
|
||||
);
|
||||
assert_eq!(
|
||||
a.const_checked_mul($name::min_value()),
|
||||
$name::min_value().const_checked_div($name::saturating_from_integer(2))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn checked_div_int_works() {
|
||||
let inner_max = <$name as FixedPointNumber>::Inner::max_value();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// Some code is based upon Derek Dreery's IntegerSquareRoot impl, used under license.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@@ -20,10 +21,11 @@
|
||||
//! assumptions of a bigger type (u128) being available, or simply create a per-thing and use the
|
||||
//! multiplication implementation provided there.
|
||||
|
||||
use crate::biguint;
|
||||
use crate::{biguint, Rounding};
|
||||
use num_traits::Zero;
|
||||
use sp_std::{
|
||||
cmp::{max, min},
|
||||
convert::TryInto,
|
||||
mem,
|
||||
};
|
||||
|
||||
@@ -117,3 +119,254 @@ pub fn multiply_by_rational(mut a: u128, mut b: u128, mut c: u128) -> Result<u12
|
||||
q.try_into().map_err(|_| "result cannot fit in u128")
|
||||
}
|
||||
}
|
||||
|
||||
mod double128 {
|
||||
// Inspired by: https://medium.com/wicketh/mathemagic-512-bit-division-in-solidity-afa55870a65
|
||||
|
||||
/// Returns the least significant 64 bits of a
|
||||
const fn low_64(a: u128) -> u128 {
|
||||
a & ((1 << 64) - 1)
|
||||
}
|
||||
|
||||
/// Returns the most significant 64 bits of a
|
||||
const fn high_64(a: u128) -> u128 {
|
||||
a >> 64
|
||||
}
|
||||
|
||||
/// Returns 2^128 - a (two's complement)
|
||||
const fn neg128(a: u128) -> u128 {
|
||||
(!a).wrapping_add(1)
|
||||
}
|
||||
|
||||
/// Returns 2^128 / a
|
||||
const fn div128(a: u128) -> u128 {
|
||||
(neg128(a) / a).wrapping_add(1)
|
||||
}
|
||||
|
||||
/// Returns 2^128 % a
|
||||
const fn mod128(a: u128) -> u128 {
|
||||
neg128(a) % a
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct Double128 {
|
||||
high: u128,
|
||||
low: u128,
|
||||
}
|
||||
|
||||
impl Double128 {
|
||||
pub const fn try_into_u128(self) -> Result<u128, ()> {
|
||||
match self.high {
|
||||
0 => Ok(self.low),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn zero() -> Self {
|
||||
Self { high: 0, low: 0 }
|
||||
}
|
||||
|
||||
/// Return a `Double128` value representing the `scaled_value << 64`.
|
||||
///
|
||||
/// This means the lower half of the `high` component will be equal to the upper 64-bits of
|
||||
/// `scaled_value` (in the lower positions) and the upper half of the `low` component will
|
||||
/// be equal to the lower 64-bits of `scaled_value`.
|
||||
pub const fn left_shift_64(scaled_value: u128) -> Self {
|
||||
Self { high: scaled_value >> 64, low: scaled_value << 64 }
|
||||
}
|
||||
|
||||
/// Construct a value from the upper 128 bits only, with the lower being zeroed.
|
||||
pub const fn from_low(low: u128) -> Self {
|
||||
Self { high: 0, low }
|
||||
}
|
||||
|
||||
/// Returns the same value ignoring anything in the high 128-bits.
|
||||
pub const fn low_part(self) -> Self {
|
||||
Self { high: 0, ..self }
|
||||
}
|
||||
|
||||
/// Returns a*b (in 256 bits)
|
||||
pub const fn product_of(a: u128, b: u128) -> Self {
|
||||
// Split a and b into hi and lo 64-bit parts
|
||||
let (a_low, a_high) = (low_64(a), high_64(a));
|
||||
let (b_low, b_high) = (low_64(b), high_64(b));
|
||||
// a = (a_low + a_high << 64); b = (b_low + b_high << 64);
|
||||
// ergo a*b = (a_low + a_high << 64)(b_low + b_high << 64)
|
||||
// = a_low * b_low
|
||||
// + a_low * b_high << 64
|
||||
// + a_high << 64 * b_low
|
||||
// + a_high << 64 * b_high << 64
|
||||
// assuming:
|
||||
// f = a_low * b_low
|
||||
// o = a_low * b_high
|
||||
// i = a_high * b_low
|
||||
// l = a_high * b_high
|
||||
// then:
|
||||
// a*b = (o+i) << 64 + f + l << 128
|
||||
let (f, o, i, l) = (a_low * b_low, a_low * b_high, a_high * b_low, a_high * b_high);
|
||||
let fl = Self { high: l, low: f };
|
||||
let i = Self::left_shift_64(i);
|
||||
let o = Self::left_shift_64(o);
|
||||
fl.add(i).add(o)
|
||||
}
|
||||
|
||||
pub const fn add(self, b: Self) -> Self {
|
||||
let (low, overflow) = self.low.overflowing_add(b.low);
|
||||
let carry = overflow as u128; // 1 if true, 0 if false.
|
||||
let high = self.high.wrapping_add(b.high).wrapping_add(carry as u128);
|
||||
Double128 { high, low }
|
||||
}
|
||||
|
||||
pub const fn div(mut self, rhs: u128) -> (Self, u128) {
|
||||
if rhs == 1 {
|
||||
return (self, 0)
|
||||
}
|
||||
|
||||
// (self === a; rhs === b)
|
||||
// Calculate a / b
|
||||
// = (a_high << 128 + a_low) / b
|
||||
// let (q, r) = (div128(b), mod128(b));
|
||||
// = (a_low * (q * b + r)) + a_high) / b
|
||||
// = (a_low * q * b + a_low * r + a_high)/b
|
||||
// = (a_low * r + a_high) / b + a_low * q
|
||||
let (q, r) = (div128(rhs), mod128(rhs));
|
||||
|
||||
// x = current result
|
||||
// a = next number
|
||||
let mut x = Self::zero();
|
||||
while self.high != 0 {
|
||||
// x += a.low * q
|
||||
x = x.add(Self::product_of(self.high, q));
|
||||
// a = a.low * r + a.high
|
||||
self = Self::product_of(self.high, r).add(self.low_part());
|
||||
}
|
||||
|
||||
(x.add(Self::from_low(self.low / rhs)), self.low % rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `a * b / c` and `(a * b) % c` (wrapping to 128 bits) or `None` in the case of
|
||||
/// overflow.
|
||||
pub const fn multiply_by_rational_with_rounding(
|
||||
a: u128,
|
||||
b: u128,
|
||||
c: u128,
|
||||
r: Rounding,
|
||||
) -> Option<u128> {
|
||||
use double128::Double128;
|
||||
if c == 0 {
|
||||
panic!("attempt to divide by zero")
|
||||
}
|
||||
let (result, remainder) = Double128::product_of(a, b).div(c);
|
||||
let mut result: u128 = match result.try_into_u128() {
|
||||
Ok(v) => v,
|
||||
Err(_) => return None,
|
||||
};
|
||||
if match r {
|
||||
Rounding::Up => remainder > 0,
|
||||
// cannot be `(c + 1) / 2` since `c` might be `max_value` and overflow.
|
||||
Rounding::NearestPrefUp => remainder >= c / 2 + c % 2,
|
||||
Rounding::NearestPrefDown => remainder > c / 2,
|
||||
Rounding::Down => false,
|
||||
} {
|
||||
result = match result.checked_add(1) {
|
||||
Some(v) => v,
|
||||
None => return None,
|
||||
};
|
||||
}
|
||||
Some(result)
|
||||
}
|
||||
|
||||
pub const fn sqrt(mut n: u128) -> u128 {
|
||||
// Modified from https://github.com/derekdreery/integer-sqrt-rs (Apache/MIT).
|
||||
if n == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Compute bit, the largest power of 4 <= n
|
||||
let max_shift: u32 = 0u128.leading_zeros() - 1;
|
||||
let shift: u32 = (max_shift - n.leading_zeros()) & !1;
|
||||
let mut bit = 1u128 << shift;
|
||||
|
||||
// Algorithm based on the implementation in:
|
||||
// https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Binary_numeral_system_(base_2)
|
||||
// Note that result/bit are logically unsigned (even if T is signed).
|
||||
let mut result = 0u128;
|
||||
while bit != 0 {
|
||||
if n >= result + bit {
|
||||
n -= result + bit;
|
||||
result = (result >> 1) + bit;
|
||||
} else {
|
||||
result = result >> 1;
|
||||
}
|
||||
bit = bit >> 2;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codec::{Decode, Encode};
|
||||
use multiply_by_rational_with_rounding as mulrat;
|
||||
use Rounding::*;
|
||||
|
||||
const MAX: u128 = u128::max_value();
|
||||
|
||||
#[test]
|
||||
fn rational_multiply_basic_rounding_works() {
|
||||
assert_eq!(mulrat(1, 1, 1, Up), Some(1));
|
||||
assert_eq!(mulrat(3, 1, 3, Up), Some(1));
|
||||
assert_eq!(mulrat(1, 1, 3, Up), Some(1));
|
||||
assert_eq!(mulrat(1, 2, 3, Down), Some(0));
|
||||
assert_eq!(mulrat(1, 1, 3, NearestPrefDown), Some(0));
|
||||
assert_eq!(mulrat(1, 1, 2, NearestPrefDown), Some(0));
|
||||
assert_eq!(mulrat(1, 2, 3, NearestPrefDown), Some(1));
|
||||
assert_eq!(mulrat(1, 1, 3, NearestPrefUp), Some(0));
|
||||
assert_eq!(mulrat(1, 1, 2, NearestPrefUp), Some(1));
|
||||
assert_eq!(mulrat(1, 2, 3, NearestPrefUp), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rational_multiply_big_number_works() {
|
||||
assert_eq!(mulrat(MAX, MAX - 1, MAX, Down), Some(MAX - 1));
|
||||
assert_eq!(mulrat(MAX, 1, MAX, Down), Some(1));
|
||||
assert_eq!(mulrat(MAX, MAX - 1, MAX, Up), Some(MAX - 1));
|
||||
assert_eq!(mulrat(MAX, 1, MAX, Up), Some(1));
|
||||
assert_eq!(mulrat(1, MAX - 1, MAX, Down), Some(0));
|
||||
assert_eq!(mulrat(1, 1, MAX, Up), Some(1));
|
||||
assert_eq!(mulrat(1, MAX / 2, MAX, NearestPrefDown), Some(0));
|
||||
assert_eq!(mulrat(1, MAX / 2 + 1, MAX, NearestPrefDown), Some(1));
|
||||
assert_eq!(mulrat(1, MAX / 2, MAX, NearestPrefUp), Some(0));
|
||||
assert_eq!(mulrat(1, MAX / 2 + 1, MAX, NearestPrefUp), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sqrt_works() {
|
||||
for i in 0..100_000u32 {
|
||||
let a = sqrt(random_u128(i));
|
||||
assert_eq!(sqrt(a * a), a);
|
||||
}
|
||||
}
|
||||
|
||||
fn random_u128(seed: u32) -> u128 {
|
||||
u128::decode(&mut &seed.using_encoded(sp_core::hashing::twox_128)[..]).unwrap_or(0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn op_checked_rounded_div_works() {
|
||||
for i in 0..100_000u32 {
|
||||
let a = random_u128(i);
|
||||
let b = random_u128(i + 1 << 30);
|
||||
let c = random_u128(i + 1 << 31);
|
||||
let x = mulrat(a, b, c, NearestPrefDown);
|
||||
let y = multiply_by_rational(a, b, c).ok();
|
||||
assert_eq!(x.is_some(), y.is_some());
|
||||
let x = x.unwrap_or(0);
|
||||
let y = y.unwrap_or(0);
|
||||
let d = x.max(y) - x.min(y);
|
||||
assert_eq!(d, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,10 @@ pub mod rational;
|
||||
pub mod traits;
|
||||
|
||||
pub use fixed_point::{FixedI128, FixedI64, FixedPointNumber, FixedPointOperand, FixedU128};
|
||||
pub use per_things::{InnerOf, PerThing, PerU16, Perbill, Percent, Permill, Perquintill, UpperOf};
|
||||
pub use per_things::{
|
||||
InnerOf, PerThing, PerU16, Perbill, Percent, Permill, Perquintill, Rounding, SignedRounding,
|
||||
UpperOf,
|
||||
};
|
||||
pub use rational::{Rational128, RationalInfinite};
|
||||
|
||||
use sp_std::{cmp::Ordering, fmt::Debug, prelude::*};
|
||||
|
||||
@@ -26,7 +26,7 @@ use codec::{CompactAs, Encode};
|
||||
use num_traits::{Pow, SaturatingAdd, SaturatingSub};
|
||||
use sp_std::{
|
||||
fmt, ops,
|
||||
ops::{Add, Sub},
|
||||
ops::{Add, AddAssign, Div, Rem, Sub},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
@@ -89,6 +89,40 @@ pub trait PerThing:
|
||||
self.deconstruct() == Self::ACCURACY
|
||||
}
|
||||
|
||||
/// Return the next lower value to `self` or `self` if it is already zero.
|
||||
fn less_epsilon(self) -> Self {
|
||||
if self.is_zero() {
|
||||
return self
|
||||
}
|
||||
Self::from_parts(self.deconstruct() - One::one())
|
||||
}
|
||||
|
||||
/// Return the next lower value to `self` or an error with the same value if `self` is already
|
||||
/// zero.
|
||||
fn try_less_epsilon(self) -> Result<Self, Self> {
|
||||
if self.is_zero() {
|
||||
return Err(self)
|
||||
}
|
||||
Ok(Self::from_parts(self.deconstruct() - One::one()))
|
||||
}
|
||||
|
||||
/// Return the next higher value to `self` or `self` if it is already one.
|
||||
fn plus_epsilon(self) -> Self {
|
||||
if self.is_one() {
|
||||
return self
|
||||
}
|
||||
Self::from_parts(self.deconstruct() + One::one())
|
||||
}
|
||||
|
||||
/// Return the next higher value to `self` or an error with the same value if `self` is already
|
||||
/// one.
|
||||
fn try_plus_epsilon(self) -> Result<Self, Self> {
|
||||
if self.is_one() {
|
||||
return Err(self)
|
||||
}
|
||||
Ok(Self::from_parts(self.deconstruct() + One::one()))
|
||||
}
|
||||
|
||||
/// Build this type from a percent. Equivalent to `Self::from_parts(x * Self::ACCURACY / 100)`
|
||||
/// but more accurate and can cope with potential type overflows.
|
||||
fn from_percent(x: Self::Inner) -> Self {
|
||||
@@ -188,7 +222,7 @@ pub trait PerThing:
|
||||
+ Unsigned,
|
||||
Self::Inner: Into<N>,
|
||||
{
|
||||
saturating_reciprocal_mul::<N, Self>(b, self.deconstruct(), Rounding::Nearest)
|
||||
saturating_reciprocal_mul::<N, Self>(b, self.deconstruct(), Rounding::NearestPrefUp)
|
||||
}
|
||||
|
||||
/// Saturating multiplication by the reciprocal of `self`. The result is rounded down to the
|
||||
@@ -275,9 +309,9 @@ pub trait PerThing:
|
||||
/// # fn main () {
|
||||
/// // 989/1000 is technically closer to 99%.
|
||||
/// assert_eq!(
|
||||
/// Percent::from_rational(989u64, 1000),
|
||||
/// Percent::from_parts(98),
|
||||
/// );
|
||||
/// Percent::from_rational(989u64, 1000),
|
||||
/// Percent::from_parts(98),
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
fn from_rational<N>(p: N, q: N) -> Self
|
||||
@@ -289,7 +323,82 @@ pub trait PerThing:
|
||||
+ ops::Div<N, Output = N>
|
||||
+ ops::Rem<N, Output = N>
|
||||
+ ops::Add<N, Output = N>
|
||||
+ Unsigned,
|
||||
+ ops::AddAssign<N>
|
||||
+ Unsigned
|
||||
+ Zero
|
||||
+ One,
|
||||
Self::Inner: Into<N>,
|
||||
{
|
||||
Self::from_rational_with_rounding(p, q, Rounding::Down).unwrap_or_else(|_| Self::one())
|
||||
}
|
||||
|
||||
/// Approximate the fraction `p/q` into a per-thing fraction.
|
||||
///
|
||||
/// The computation of this approximation is performed in the generic type `N`. Given
|
||||
/// `M` as the data type that can hold the maximum value of this per-thing (e.g. `u32` for
|
||||
/// `Perbill`), this can only work if `N == M` or `N: From<M> + TryInto<M>`.
|
||||
///
|
||||
/// In the case of an overflow (or divide by zero), an `Err` is returned.
|
||||
///
|
||||
/// Rounding is determined by the parameter `rounding`, i.e.
|
||||
///
|
||||
/// ```rust
|
||||
/// # use sp_arithmetic::{Percent, PerThing, Rounding::*};
|
||||
/// # fn main () {
|
||||
/// // 989/100 is technically closer to 99%.
|
||||
/// assert_eq!(
|
||||
/// Percent::from_rational_with_rounding(989u64, 1000, Down).unwrap(),
|
||||
/// Percent::from_parts(98),
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// Percent::from_rational_with_rounding(984u64, 1000, NearestPrefUp).unwrap(),
|
||||
/// Percent::from_parts(98),
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// Percent::from_rational_with_rounding(985u64, 1000, NearestPrefDown).unwrap(),
|
||||
/// Percent::from_parts(98),
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// Percent::from_rational_with_rounding(985u64, 1000, NearestPrefUp).unwrap(),
|
||||
/// Percent::from_parts(99),
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// Percent::from_rational_with_rounding(986u64, 1000, NearestPrefDown).unwrap(),
|
||||
/// Percent::from_parts(99),
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// Percent::from_rational_with_rounding(981u64, 1000, Up).unwrap(),
|
||||
/// Percent::from_parts(99),
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// Percent::from_rational_with_rounding(1001u64, 1000, Up),
|
||||
/// Err(()),
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// ```rust
|
||||
/// # use sp_arithmetic::{Percent, PerThing, Rounding::*};
|
||||
/// # fn main () {
|
||||
/// assert_eq!(
|
||||
/// Percent::from_rational_with_rounding(981u64, 1000, Up).unwrap(),
|
||||
/// Percent::from_parts(99),
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
fn from_rational_with_rounding<N>(p: N, q: N, rounding: Rounding) -> Result<Self, ()>
|
||||
where
|
||||
N: Clone
|
||||
+ Ord
|
||||
+ TryInto<Self::Inner>
|
||||
+ TryInto<Self::Upper>
|
||||
+ ops::Div<N, Output = N>
|
||||
+ ops::Rem<N, Output = N>
|
||||
+ ops::Add<N, Output = N>
|
||||
+ ops::AddAssign<N>
|
||||
+ Unsigned
|
||||
+ Zero
|
||||
+ One,
|
||||
Self::Inner: Into<N>;
|
||||
|
||||
/// Same as `Self::from_rational`.
|
||||
@@ -303,6 +412,7 @@ pub trait PerThing:
|
||||
+ ops::Div<N, Output = N>
|
||||
+ ops::Rem<N, Output = N>
|
||||
+ ops::Add<N, Output = N>
|
||||
+ ops::AddAssign<N>
|
||||
+ Unsigned
|
||||
+ Zero
|
||||
+ One,
|
||||
@@ -312,14 +422,54 @@ pub trait PerThing:
|
||||
}
|
||||
}
|
||||
|
||||
/// The rounding method to use.
|
||||
///
|
||||
/// `PerThing`s are unsigned so `Up` means towards infinity and `Down` means towards zero.
|
||||
/// `Nearest` will round an exact half down.
|
||||
enum Rounding {
|
||||
/// The rounding method to use for unsigned quantities.
|
||||
#[derive(Copy, Clone, sp_std::fmt::Debug)]
|
||||
pub enum Rounding {
|
||||
// Towards infinity.
|
||||
Up,
|
||||
// Towards zero.
|
||||
Down,
|
||||
Nearest,
|
||||
// Nearest integer, rounding as `Up` when equidistant.
|
||||
NearestPrefUp,
|
||||
// Nearest integer, rounding as `Down` when equidistant.
|
||||
NearestPrefDown,
|
||||
}
|
||||
|
||||
/// The rounding method to use.
|
||||
#[derive(Copy, Clone, sp_std::fmt::Debug)]
|
||||
pub enum SignedRounding {
|
||||
// Towards positive infinity.
|
||||
High,
|
||||
// Towards negative infinity.
|
||||
Low,
|
||||
// Nearest integer, rounding as `High` when exactly equidistant.
|
||||
NearestPrefHigh,
|
||||
// Nearest integer, rounding as `Low` when exactly equidistant.
|
||||
NearestPrefLow,
|
||||
// Away from zero (up when positive, down when negative). When positive, equivalent to `High`.
|
||||
Major,
|
||||
// Towards zero (down when positive, up when negative). When positive, equivalent to `Low`.
|
||||
Minor,
|
||||
// Nearest integer, rounding as `Major` when exactly equidistant.
|
||||
NearestPrefMajor,
|
||||
// Nearest integer, rounding as `Minor` when exactly equidistant.
|
||||
NearestPrefMinor,
|
||||
}
|
||||
|
||||
impl Rounding {
|
||||
/// Returns the value for `Rounding` which would give the same result ignorant of the sign.
|
||||
pub const fn from_signed(rounding: SignedRounding, negative: bool) -> Self {
|
||||
use Rounding::*;
|
||||
use SignedRounding::*;
|
||||
match (rounding, negative) {
|
||||
(Low, true) | (Major, _) | (High, false) => Up,
|
||||
(High, true) | (Minor, _) | (Low, false) => Down,
|
||||
(NearestPrefMajor, _) | (NearestPrefHigh, false) | (NearestPrefLow, true) =>
|
||||
NearestPrefUp,
|
||||
(NearestPrefMinor, _) | (NearestPrefLow, false) | (NearestPrefHigh, true) =>
|
||||
NearestPrefDown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Saturating reciprocal multiplication. Compute `x / self`, saturating at the numeric
|
||||
@@ -397,18 +547,53 @@ where
|
||||
rem_mul_div_inner += 1.into();
|
||||
}
|
||||
},
|
||||
// Round up if the fractional part of the result is greater than a half. An exact half is
|
||||
// rounded down.
|
||||
Rounding::Nearest => {
|
||||
Rounding::NearestPrefDown =>
|
||||
if rem_mul_upper % denom_upper > denom_upper / 2.into() {
|
||||
// `rem * numer / denom` is less than `numer`, so this will not overflow.
|
||||
rem_mul_div_inner += 1.into();
|
||||
}
|
||||
},
|
||||
},
|
||||
Rounding::NearestPrefUp =>
|
||||
if rem_mul_upper % denom_upper >= denom_upper / 2.into() + denom_upper % 2.into() {
|
||||
// `rem * numer / denom` is less than `numer`, so this will not overflow.
|
||||
rem_mul_div_inner += 1.into();
|
||||
},
|
||||
}
|
||||
rem_mul_div_inner.into()
|
||||
}
|
||||
|
||||
/// Just a simple generic integer divide with custom rounding.
|
||||
fn div_rounded<N>(n: N, d: N, r: Rounding) -> N
|
||||
where
|
||||
N: Clone
|
||||
+ Eq
|
||||
+ Ord
|
||||
+ Zero
|
||||
+ One
|
||||
+ AddAssign
|
||||
+ Add<Output = N>
|
||||
+ Rem<Output = N>
|
||||
+ Div<Output = N>,
|
||||
{
|
||||
let mut o = n.clone() / d.clone();
|
||||
use Rounding::*;
|
||||
let two = || N::one() + N::one();
|
||||
if match r {
|
||||
Up => !((n % d).is_zero()),
|
||||
NearestPrefDown => {
|
||||
let rem = n % d.clone();
|
||||
rem > d / two()
|
||||
},
|
||||
NearestPrefUp => {
|
||||
let rem = n % d.clone();
|
||||
rem >= d.clone() / two() + d % two()
|
||||
},
|
||||
Down => false,
|
||||
} {
|
||||
o += N::one()
|
||||
}
|
||||
o
|
||||
}
|
||||
|
||||
macro_rules! implement_per_thing {
|
||||
(
|
||||
$name:ident,
|
||||
@@ -423,7 +608,7 @@ macro_rules! implement_per_thing {
|
||||
///
|
||||
#[doc = $title]
|
||||
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
|
||||
#[derive(Encode, Copy, Clone, PartialEq, Eq, codec::MaxEncodedLen, PartialOrd, Ord, sp_std::fmt::Debug, scale_info::TypeInfo)]
|
||||
#[derive(Encode, Copy, Clone, PartialEq, Eq, codec::MaxEncodedLen, PartialOrd, Ord, scale_info::TypeInfo)]
|
||||
pub struct $name($type);
|
||||
|
||||
/// Implementation makes any compact encoding of `PerThing::Inner` valid,
|
||||
@@ -445,6 +630,55 @@ macro_rules! implement_per_thing {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl sp_std::fmt::Debug for $name {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
if $max == <$type>::max_value() {
|
||||
// Not a power of ten: show as N/D and approx %
|
||||
let pc = (self.0 as f64) / (self.0 as f64) * 100f64;
|
||||
write!(fmt, "{:.2}% ({}/{})", pc, self.0, $max)
|
||||
} else {
|
||||
// A power of ten: calculate exact percent
|
||||
let units = self.0 / ($max / 100);
|
||||
let rest = self.0 % ($max / 100);
|
||||
write!(fmt, "{}", units)?;
|
||||
if rest > 0 {
|
||||
write!(fmt, ".")?;
|
||||
let mut m = $max / 100;
|
||||
while rest % m > 0 {
|
||||
m /= 10;
|
||||
write!(fmt, "{:01}", rest / m % 10)?;
|
||||
}
|
||||
}
|
||||
write!(fmt, "%")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl sp_std::fmt::Debug for $name {
|
||||
fn fmt(&self, fmt: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
||||
if $max == <$type>::max_value() {
|
||||
// Not a power of ten: show as N/D and approx %
|
||||
write!(fmt, "{}/{}", self.0, $max)
|
||||
} else {
|
||||
// A power of ten: calculate exact percent
|
||||
let units = self.0 / ($max / 100);
|
||||
let rest = self.0 % ($max / 100);
|
||||
write!(fmt, "{}", units)?;
|
||||
if rest > 0 {
|
||||
write!(fmt, ".")?;
|
||||
let mut m = $max / 100;
|
||||
while rest % m > 0 {
|
||||
m /= 10;
|
||||
write!(fmt, "{:01}", rest / m % 10)?;
|
||||
}
|
||||
}
|
||||
write!(fmt, "%")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PerThing for $name {
|
||||
type Inner = $type;
|
||||
type Upper = $upper_type;
|
||||
@@ -463,53 +697,50 @@ macro_rules! implement_per_thing {
|
||||
Self::from_parts((x.max(0.).min(1.) * $max as f64) as Self::Inner)
|
||||
}
|
||||
|
||||
fn from_rational<N>(p: N, q: N) -> Self
|
||||
fn from_rational_with_rounding<N>(p: N, q: N, r: Rounding) -> Result<Self, ()>
|
||||
where
|
||||
N: Clone + Ord + TryInto<Self::Inner> + TryInto<Self::Upper>
|
||||
+ ops::Div<N, Output=N> + ops::Rem<N, Output=N> + ops::Add<N, Output=N> + Unsigned
|
||||
+ Zero + One,
|
||||
Self::Inner: Into<N>,
|
||||
N: Clone
|
||||
+ Ord
|
||||
+ TryInto<Self::Inner>
|
||||
+ TryInto<Self::Upper>
|
||||
+ ops::Div<N, Output = N>
|
||||
+ ops::Rem<N, Output = N>
|
||||
+ ops::Add<N, Output = N>
|
||||
+ ops::AddAssign<N>
|
||||
+ Unsigned
|
||||
+ Zero
|
||||
+ One,
|
||||
Self::Inner: Into<N>
|
||||
{
|
||||
let div_ceil = |x: N, f: N| -> N {
|
||||
let mut o = x.clone() / f.clone();
|
||||
let r = x.rem(f.clone());
|
||||
if r > N::zero() {
|
||||
o = o + N::one();
|
||||
}
|
||||
o
|
||||
};
|
||||
|
||||
// q cannot be zero.
|
||||
let q: N = q.max((1 as Self::Inner).into());
|
||||
if q.is_zero() { return Err(()) }
|
||||
// p should not be bigger than q.
|
||||
let p: N = p.min(q.clone());
|
||||
if p > q { return Err(()) }
|
||||
|
||||
let factor: N = div_ceil(q.clone(), $max.into()).max((1 as Self::Inner).into());
|
||||
let factor = div_rounded::<N>(q.clone(), $max.into(), Rounding::Up).max(One::one());
|
||||
|
||||
// q cannot overflow: (q / (q/$max)) < $max. p < q hence p also cannot overflow.
|
||||
let q_reduce: $type = (q.clone() / factor.clone())
|
||||
let q_reduce: $type = div_rounded(q, factor.clone(), r)
|
||||
.try_into()
|
||||
.map_err(|_| "Failed to convert")
|
||||
.expect(
|
||||
"q / ceil(q/$max) < $max. Macro prevents any type being created that \
|
||||
"`q / ceil(q/$max) < $max`; macro prevents any type being created that \
|
||||
does not satisfy this; qed"
|
||||
);
|
||||
let p_reduce: $type = (p / factor)
|
||||
let p_reduce: $type = div_rounded(p, factor, r)
|
||||
.try_into()
|
||||
.map_err(|_| "Failed to convert")
|
||||
.expect(
|
||||
"q / ceil(q/$max) < $max. Macro prevents any type being created that \
|
||||
"`p / ceil(p/$max) < $max`; macro prevents any type being created that \
|
||||
does not satisfy this; qed"
|
||||
);
|
||||
|
||||
// `p_reduced` and `q_reduced` are withing Self::Inner. Mul by another $max will
|
||||
// always fit in $upper_type. This is guaranteed by the macro tests.
|
||||
let part =
|
||||
p_reduce as $upper_type
|
||||
* <$upper_type>::from($max)
|
||||
/ q_reduce as $upper_type;
|
||||
|
||||
$name(part as Self::Inner)
|
||||
// `p_reduced` and `q_reduced` are within `Self::Inner`. Multiplication by another
|
||||
// `$max` will always fit in `$upper_type`. This is guaranteed by the macro tests.
|
||||
let n = p_reduce as $upper_type * <$upper_type>::from($max);
|
||||
let d = q_reduce as $upper_type;
|
||||
let part = div_rounded(n, d, r);
|
||||
Ok($name(part as Self::Inner))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -570,24 +801,52 @@ macro_rules! implement_per_thing {
|
||||
/// See [`PerThing::from_rational`].
|
||||
#[deprecated = "Use `PerThing::from_rational` instead"]
|
||||
pub fn from_rational_approximation<N>(p: N, q: N) -> Self
|
||||
where N: Clone + Ord + TryInto<$type> +
|
||||
TryInto<$upper_type> + ops::Div<N, Output=N> + ops::Rem<N, Output=N> +
|
||||
ops::Add<N, Output=N> + Unsigned,
|
||||
$type: Into<N>,
|
||||
where
|
||||
N: Clone
|
||||
+ Ord
|
||||
+ TryInto<$type>
|
||||
+ TryInto<$upper_type>
|
||||
+ ops::Div<N, Output = N>
|
||||
+ ops::Rem<N, Output = N>
|
||||
+ ops::Add<N, Output = N>
|
||||
+ ops::AddAssign<N>
|
||||
+ Unsigned
|
||||
+ Zero
|
||||
+ One,
|
||||
$type: Into<N>
|
||||
{
|
||||
<Self as PerThing>::from_rational(p, q)
|
||||
}
|
||||
|
||||
/// See [`PerThing::from_rational`].
|
||||
pub fn from_rational<N>(p: N, q: N) -> Self
|
||||
where N: Clone + Ord + TryInto<$type> +
|
||||
TryInto<$upper_type> + ops::Div<N, Output=N> + ops::Rem<N, Output=N> +
|
||||
ops::Add<N, Output=N> + Unsigned,
|
||||
$type: Into<N>,
|
||||
where
|
||||
N: Clone
|
||||
+ Ord
|
||||
+ TryInto<$type>
|
||||
+ TryInto<$upper_type>
|
||||
+ ops::Div<N, Output = N>
|
||||
+ ops::Rem<N, Output = N>
|
||||
+ ops::Add<N, Output = N>
|
||||
+ ops::AddAssign<N>
|
||||
+ Unsigned
|
||||
+ Zero
|
||||
+ One,
|
||||
$type: Into<N>
|
||||
{
|
||||
<Self as PerThing>::from_rational(p, q)
|
||||
}
|
||||
|
||||
/// Integer multiplication with another value, saturating at 1.
|
||||
pub fn int_mul(self, b: $type) -> Self {
|
||||
PerThing::from_parts(self.0.saturating_mul(b))
|
||||
}
|
||||
|
||||
/// Integer division with another value, rounding down.
|
||||
pub fn int_div(self, b: Self) -> $type {
|
||||
self.0 / b.0
|
||||
}
|
||||
|
||||
/// See [`PerThing::mul_floor`].
|
||||
pub fn mul_floor<N>(self, b: N) -> N
|
||||
where
|
||||
@@ -643,6 +902,38 @@ macro_rules! implement_per_thing {
|
||||
{
|
||||
PerThing::saturating_reciprocal_mul_ceil(self, b)
|
||||
}
|
||||
|
||||
/// Saturating division. Compute `self / rhs`, saturating at one if `rhs < self`.
|
||||
///
|
||||
/// The `rounding` method must be specified. e.g.:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use sp_arithmetic::{Percent, PerThing, Rounding::*};
|
||||
/// # fn main () {
|
||||
/// let pc = |x| Percent::from_percent(x);
|
||||
/// assert_eq!(
|
||||
/// pc(2).saturating_div(pc(3), Down),
|
||||
/// pc(66),
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// pc(1).saturating_div(pc(3), NearestPrefUp),
|
||||
/// pc(33),
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// pc(2).saturating_div(pc(3), NearestPrefDown),
|
||||
/// pc(67),
|
||||
/// );
|
||||
/// assert_eq!(
|
||||
/// pc(1).saturating_div(pc(3), Up),
|
||||
/// pc(34),
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn saturating_div(self, rhs: Self, r: Rounding) -> Self {
|
||||
let p = self.0;
|
||||
let q = rhs.0;
|
||||
Self::from_rational_with_rounding(p, q, r).unwrap_or_else(|_| Self::one())
|
||||
}
|
||||
}
|
||||
|
||||
impl Saturating for $name {
|
||||
@@ -756,7 +1047,7 @@ macro_rules! implement_per_thing {
|
||||
{
|
||||
type Output = N;
|
||||
fn mul(self, b: N) -> Self::Output {
|
||||
overflow_prune_mul::<N, Self>(b, self.deconstruct(), Rounding::Nearest)
|
||||
overflow_prune_mul::<N, Self>(b, self.deconstruct(), Rounding::NearestPrefDown)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -903,6 +1194,11 @@ macro_rules! implement_per_thing {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_parts_cannot_overflow() {
|
||||
assert_eq!(<$name>::from_parts($max.saturating_add(1)), <$name>::one());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_max_encoded_len() {
|
||||
struct AsMaxEncodedLen<T: codec::MaxEncodedLen> {
|
||||
@@ -1040,10 +1336,10 @@ macro_rules! implement_per_thing {
|
||||
|
||||
#[test]
|
||||
fn per_thing_mul_rounds_to_nearest_number() {
|
||||
assert_eq!($name::from_float(0.33) * 10u64, 3);
|
||||
assert_eq!($name::from_float(0.34) * 10u64, 3);
|
||||
assert_eq!($name::from_float(0.35) * 10u64, 3);
|
||||
assert_eq!($name::from_float(0.36) * 10u64, 4);
|
||||
assert_eq!($name::from_percent(33) * 10u64, 3);
|
||||
assert_eq!($name::from_percent(34) * 10u64, 3);
|
||||
assert_eq!($name::from_percent(35) * 10u64, 3);
|
||||
assert_eq!($name::from_percent(36) * 10u64, 4);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1351,7 +1647,7 @@ macro_rules! implement_per_thing {
|
||||
<$type>::max_value(),
|
||||
<$type>::max_value(),
|
||||
<$type>::max_value(),
|
||||
super::Rounding::Nearest,
|
||||
super::Rounding::NearestPrefDown,
|
||||
),
|
||||
0,
|
||||
);
|
||||
@@ -1360,7 +1656,7 @@ macro_rules! implement_per_thing {
|
||||
<$type>::max_value() - 1,
|
||||
<$type>::max_value(),
|
||||
<$type>::max_value(),
|
||||
super::Rounding::Nearest,
|
||||
super::Rounding::NearestPrefDown,
|
||||
),
|
||||
<$type>::max_value() - 1,
|
||||
);
|
||||
@@ -1369,7 +1665,7 @@ macro_rules! implement_per_thing {
|
||||
((<$type>::max_value() - 1) as $upper_type).pow(2),
|
||||
<$type>::max_value(),
|
||||
<$type>::max_value(),
|
||||
super::Rounding::Nearest,
|
||||
super::Rounding::NearestPrefDown,
|
||||
),
|
||||
1,
|
||||
);
|
||||
@@ -1379,7 +1675,7 @@ macro_rules! implement_per_thing {
|
||||
(<$type>::max_value() as $upper_type).pow(2) - 1,
|
||||
<$type>::max_value(),
|
||||
<$type>::max_value(),
|
||||
super::Rounding::Nearest,
|
||||
super::Rounding::NearestPrefDown,
|
||||
),
|
||||
<$upper_type>::from((<$type>::max_value() - 1)),
|
||||
);
|
||||
@@ -1389,7 +1685,7 @@ macro_rules! implement_per_thing {
|
||||
(<$type>::max_value() as $upper_type).pow(2),
|
||||
<$type>::max_value(),
|
||||
2 as $type,
|
||||
super::Rounding::Nearest,
|
||||
super::Rounding::NearestPrefDown,
|
||||
),
|
||||
<$type>::max_value() as $upper_type / 2,
|
||||
);
|
||||
@@ -1399,7 +1695,7 @@ macro_rules! implement_per_thing {
|
||||
(<$type>::max_value() as $upper_type).pow(2) - 1,
|
||||
2 as $type,
|
||||
<$type>::max_value(),
|
||||
super::Rounding::Nearest,
|
||||
super::Rounding::NearestPrefDown,
|
||||
),
|
||||
2,
|
||||
);
|
||||
@@ -1586,6 +1882,33 @@ macro_rules! implement_per_thing_with_perthousand {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_rational_with_rounding_works_in_extreme_case() {
|
||||
use Rounding::*;
|
||||
for &r in [Down, NearestPrefDown, NearestPrefUp, Up].iter() {
|
||||
Percent::from_rational_with_rounding(1, u64::max_value(), r).unwrap();
|
||||
Percent::from_rational_with_rounding(1, u32::max_value(), r).unwrap();
|
||||
Percent::from_rational_with_rounding(1, u16::max_value(), r).unwrap();
|
||||
Percent::from_rational_with_rounding(u64::max_value() - 1, u64::max_value(), r).unwrap();
|
||||
Percent::from_rational_with_rounding(u32::max_value() - 1, u32::max_value(), r).unwrap();
|
||||
Percent::from_rational_with_rounding(u16::max_value() - 1, u16::max_value(), r).unwrap();
|
||||
PerU16::from_rational_with_rounding(1, u64::max_value(), r).unwrap();
|
||||
PerU16::from_rational_with_rounding(1, u32::max_value(), r).unwrap();
|
||||
PerU16::from_rational_with_rounding(1, u16::max_value(), r).unwrap();
|
||||
PerU16::from_rational_with_rounding(u64::max_value() - 1, u64::max_value(), r).unwrap();
|
||||
PerU16::from_rational_with_rounding(u32::max_value() - 1, u32::max_value(), r).unwrap();
|
||||
PerU16::from_rational_with_rounding(u16::max_value() - 1, u16::max_value(), r).unwrap();
|
||||
Permill::from_rational_with_rounding(1, u64::max_value(), r).unwrap();
|
||||
Permill::from_rational_with_rounding(1, u32::max_value(), r).unwrap();
|
||||
Permill::from_rational_with_rounding(u64::max_value() - 1, u64::max_value(), r).unwrap();
|
||||
Permill::from_rational_with_rounding(u32::max_value() - 1, u32::max_value(), r).unwrap();
|
||||
Perbill::from_rational_with_rounding(1, u64::max_value(), r).unwrap();
|
||||
Perbill::from_rational_with_rounding(1, u32::max_value(), r).unwrap();
|
||||
Perbill::from_rational_with_rounding(u64::max_value() - 1, u64::max_value(), r).unwrap();
|
||||
Perbill::from_rational_with_rounding(u32::max_value() - 1, u32::max_value(), r).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
implement_per_thing!(Percent, test_per_cent, [u32, u64, u128], 100u8, u8, u16, "_Percent_",);
|
||||
implement_per_thing_with_perthousand!(
|
||||
PerU16,
|
||||
|
||||
@@ -58,6 +58,7 @@ pub trait BaseArithmetic:
|
||||
+ Bounded
|
||||
+ HasCompact
|
||||
+ Sized
|
||||
+ Clone
|
||||
+ TryFrom<u8>
|
||||
+ TryInto<u8>
|
||||
+ TryFrom<u16>
|
||||
@@ -113,6 +114,7 @@ impl<
|
||||
+ Bounded
|
||||
+ HasCompact
|
||||
+ Sized
|
||||
+ Clone
|
||||
+ TryFrom<u8>
|
||||
+ TryInto<u8>
|
||||
+ TryFrom<u16>
|
||||
|
||||
Reference in New Issue
Block a user