#[cfg(feature = "std")]
use serde::{Serialize, Deserialize};
use sp_std::{ops, prelude::*, convert::TryInto};
use codec::{Encode, Decode, CompactAs};
use crate::traits::{
SaturatedConversion, UniqueSaturatedInto, Saturating, BaseArithmetic,
};
use sp_debug_derive::RuntimeDebug;
pub trait PerThing: Sized + Saturating + Copy {
type Inner: BaseArithmetic + Copy;
const ACCURACY: Self::Inner;
fn zero() -> Self;
fn is_zero(&self) -> bool;
fn one() -> Self;
fn deconstruct(self) -> Self::Inner;
fn from_parts(parts: Self::Inner) -> Self;
fn from_percent(x: Self::Inner) -> Self;
fn square(self) -> Self;
#[cfg(feature = "std")]
fn from_fraction(x: f64) -> Self;
fn from_rational_approximation<N>(p: N, q: N) -> Self
where N: Clone + Ord + From<Self::Inner> + TryInto<Self::Inner> + ops::Div<N, Output=N>;
}
macro_rules! implement_per_thing {
($name:ident, $test_mod:ident, [$($test_units:tt),+], $max:tt, $type:ty, $upper_type:ty, $title:expr $(,)?) => {
#[doc = $title]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, CompactAs)]
pub struct $name($type);
impl PerThing for $name {
type Inner = $type;
const ACCURACY: Self::Inner = $max;
fn zero() -> Self { Self(0) }
fn is_zero(&self) -> bool { self.0 == 0 }
fn one() -> Self { Self($max) }
fn deconstruct(self) -> Self::Inner { self.0 }
fn from_parts(parts: Self::Inner) -> Self {
Self([parts, $max][(parts > $max) as usize])
}
fn from_percent(x: Self::Inner) -> Self {
Self([x, 100][(x > 100) as usize] * ($max / 100))
}
fn square(self) -> Self {
let p: $upper_type = self.0 as $upper_type * self.0 as $upper_type;
let q: $upper_type = <$upper_type>::from($max) * <$upper_type>::from($max);
Self::from_rational_approximation(p, q)
}
#[cfg(feature = "std")]
fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as Self::Inner) }
fn from_rational_approximation<N>(p: N, q: N) -> Self
where N: Clone + Ord + From<Self::Inner> + TryInto<Self::Inner> + ops::Div<N, Output=N>
{
let q = q.max((1 as Self::Inner).into());
let p = p.min(q.clone());
let factor = (q.clone() / $max.into()).max((1 as Self::Inner).into());
let q_reduce: Self::Inner = (q / factor.clone())
.try_into()
.map_err(|_| "Failed to convert")
.expect(
"q / (q/$max) < (2 * $max). Macro prevents any type being created that \
does not satisfy this; qed"
);
let p_reduce: Self::Inner = (p / factor.clone())
.try_into()
.map_err(|_| "Failed to convert")
.expect(
"q / (q/$max) < (2 * $max). Macro prevents any type being created that \
does not satisfy this; qed"
);
let part =
p_reduce as $upper_type
* <$upper_type>::from($max)
/ q_reduce as $upper_type;
$name(part as Self::Inner)
}
}
impl $name {
pub const fn from_parts(parts: $type) -> Self {
Self([parts, $max][(parts > $max) as usize])
}
pub const fn from_percent(x: $type) -> Self {
Self([x, 100][(x > 100) as usize] * ($max / 100))
}
#[cfg(feature = "std")]
pub fn one() -> Self {
<Self as PerThing>::one()
}
}
impl Saturating for $name {
fn saturating_add(self, rhs: Self) -> Self {
Self::from_parts(self.0.saturating_add(rhs.0))
}
fn saturating_sub(self, rhs: Self) -> Self {
Self::from_parts(self.0.saturating_sub(rhs.0))
}
fn saturating_mul(self, rhs: Self) -> Self {
let a = self.0 as $upper_type;
let b = rhs.0 as $upper_type;
let m = <$upper_type>::from($max);
let parts = a * b / m;
Self::from_parts(parts as $type)
}
}
impl ops::Div for $name {
type Output = Self;
fn div(self, rhs: Self) -> Self::Output {
let p = self.0;
let q = rhs.0;
Self::from_rational_approximation(p, q)
}
}
impl<N> ops::Mul<N> for $name
where
N: Clone + From<$type> + UniqueSaturatedInto<$type> + ops::Rem<N, Output=N>
+ ops::Div<N, Output=N> + ops::Mul<N, Output=N> + ops::Add<N, Output=N>,
{
type Output = N;
fn mul(self, b: N) -> Self::Output {
let maximum: N = $max.into();
let upper_max: $upper_type = $max.into();
let part: N = self.0.into();
let rem_multiplied_divided = {
let rem = b.clone().rem(maximum.clone());
let rem_sized = rem.saturated_into::<$type>();
let rem_multiplied_upper = rem_sized as $upper_type * self.0 as $upper_type;
let mut rem_multiplied_divided_sized =
(rem_multiplied_upper / upper_max) as $type;
if rem_multiplied_upper % upper_max > upper_max / 2 {
rem_multiplied_divided_sized += 1;
}
rem_multiplied_divided_sized.into()
};
(b / maximum) * part + rem_multiplied_divided
}
}
#[cfg(test)]
mod $test_mod {
use codec::{Encode, Decode};
use super::{$name, Saturating, RuntimeDebug, PerThing};
use crate::traits::Zero;
#[test]
fn macro_expanded_correctly() {
assert!($max >= 100);
assert!($max % 100 == 0);
assert!(2 * $max < <$type>::max_value());
assert!(<$upper_type>::from($max) < <$upper_type>::max_value());
assert!((<$type>::max_value() as $upper_type) <= <$upper_type>::max_value());
assert!(<$upper_type>::from($max).checked_mul($max.into()).is_some());
}
#[derive(Encode, Decode, PartialEq, Eq, RuntimeDebug)]
struct WithCompact<T: codec::HasCompact> {
data: T,
}
#[test]
fn has_compact() {
let data = WithCompact { data: $name(1) };
let encoded = data.encode();
assert_eq!(data, WithCompact::<$name>::decode(&mut &encoded[..]).unwrap());
}
#[test]
fn compact_encoding() {
let tests = [
(0 as $type, 1usize),
(1 as $type, 1usize),
(63, 1),
(64, 2),
(65, 2),
(<$type>::max_value(), <$type>::max_value().encode().len() + 1)
];
for &(n, l) in &tests {
let compact: codec::Compact<$name> = $name(n).into();
let encoded = compact.encode();
assert_eq!(encoded.len(), l);
let decoded = <codec::Compact<$name>>::decode(&mut & encoded[..])
.unwrap();
let per_thingy: $name = decoded.into();
assert_eq!(per_thingy, $name(n));
}
}
#[test]
fn per_thing_api_works() {
assert_eq!($name::zero(), $name::from_parts(Zero::zero()));
assert_eq!($name::one(), $name::from_parts($max));
assert_eq!($name::ACCURACY, $max);
assert_eq!($name::from_percent(0), $name::from_parts(Zero::zero()));
assert_eq!($name::from_percent(10), $name::from_parts($max / 10));
assert_eq!($name::from_percent(100), $name::from_parts($max));
}
macro_rules! per_thing_mul_test {
($num_type:tt) => {
assert_eq!(
$name::from_percent(100) * $num_type::max_value(),
$num_type::max_value()
);
assert_eq_error_rate!(
$name::from_percent(99) * $num_type::max_value(),
((Into::<U256>::into($num_type::max_value()) * 99u32) / 100u32).as_u128() as $num_type,
1,
);
assert_eq!(
$name::from_percent(50) * $num_type::max_value(),
$num_type::max_value() / 2,
);
assert_eq_error_rate!(
$name::from_percent(1) * $num_type::max_value(),
$num_type::max_value() / 100,
1,
);
assert_eq!($name::from_percent(0) * $num_type::max_value(), 0);
assert_eq!($name::one() * $num_type::max_value(), $num_type::max_value());
assert_eq!($name::zero() * $num_type::max_value(), 0);
}
}
#[test]
fn per_thing_mul_works() {
use primitive_types::U256;
assert_eq!($name::from_rational_approximation(1 as $type, 3) * 30 as $type, 10);
$(per_thing_mul_test!($test_units);)*
}
#[test]
fn per_thing_mul_rounds_to_nearest_number() {
assert_eq!($name::from_percent(33) * 10u64, 3);
assert_eq!($name::from_percent(34) * 10u64, 3);
assert_eq!($name::from_percent(35) * 10u64, 3);
assert_eq!($name::from_percent(36) * 10u64, 4);
}
#[test]
fn per_thing_multiplication_with_large_number() {
use primitive_types::U256;
let max_minus_one = $max - 1;
assert_eq_error_rate!(
$name::from_parts(max_minus_one) * std::u128::MAX,
((Into::<U256>::into(std::u128::MAX) * max_minus_one) / $max).as_u128(),
1,
);
}
macro_rules! per_thing_from_rationale_approx_test {
($num_type:tt) => {
assert_eq!(
$name::from_rational_approximation(1 as $num_type, 0),
$name::one(),
);
assert_eq!(
$name::from_rational_approximation(1 as $num_type, 1),
$name::one(),
);
assert_eq_error_rate!(
$name::from_rational_approximation(1 as $num_type, 3).0,
$name::from_parts($max / 3).0,
2
);
assert_eq!(
$name::from_rational_approximation(1 as $num_type, 10),
$name::from_percent(10),
);
assert_eq!(
$name::from_rational_approximation(1 as $num_type, 4),
$name::from_percent(25),
);
assert_eq!(
$name::from_rational_approximation(1 as $num_type, 4),
$name::from_rational_approximation(2 as $num_type, 8),
);
assert_eq!(
$name::from_rational_approximation(
$num_type::max_value() - 1,
$num_type::max_value()
),
$name::one(),
);
assert_eq_error_rate!(
$name::from_rational_approximation(
$num_type::max_value() / 3,
$num_type::max_value()
).0,
$name::from_parts($max / 3).0,
2
);
assert_eq!(
$name::from_rational_approximation(1, $num_type::max_value()),
$name::zero(),
);
};
}
#[test]
fn per_thing_from_rationale_approx_works() {
let max_value = <$upper_type>::from($max);
assert_eq!(
$name::from_rational_approximation($max - 1, $max + 1),
$name::from_parts($max - 2),
);
assert_eq!(
$name::from_rational_approximation(1, $max-1),
$name::from_parts(1),
);
assert_eq!(
$name::from_rational_approximation(1, $max),
$name::from_parts(1),
);
assert_eq!(
$name::from_rational_approximation(2, 2 * $max - 1),
$name::from_parts(1),
);
assert_eq!(
$name::from_rational_approximation(1, $max+1),
$name::zero(),
);
assert_eq!(
$name::from_rational_approximation(3 * max_value / 2, 3 * max_value),
$name::from_percent(50),
);
$(per_thing_from_rationale_approx_test!($test_units);)*
}
#[test]
fn per_things_mul_operates_in_output_type() {
assert_eq!($name::from_percent(50) * 100u64, 50u64);
assert_eq!($name::from_percent(50) * 100u128, 50u128);
}
#[test]
fn per_thing_saturating_op_works() {
assert_eq!(
$name::from_percent(50).saturating_add($name::from_percent(40)),
$name::from_percent(90)
);
assert_eq!(
$name::from_percent(50).saturating_add($name::from_percent(50)),
$name::from_percent(100)
);
assert_eq!(
$name::from_percent(60).saturating_add($name::from_percent(50)),
$name::from_percent(100)
);
assert_eq!(
$name::from_percent(60).saturating_sub($name::from_percent(50)),
$name::from_percent(10)
);
assert_eq!(
$name::from_percent(60).saturating_sub($name::from_percent(60)),
$name::from_percent(0)
);
assert_eq!(
$name::from_percent(60).saturating_sub($name::from_percent(70)),
$name::from_percent(0)
);
assert_eq!(
$name::from_percent(50).saturating_mul($name::from_percent(50)),
$name::from_percent(25)
);
assert_eq!(
$name::from_percent(20).saturating_mul($name::from_percent(20)),
$name::from_percent(4)
);
assert_eq!(
$name::from_percent(10).saturating_mul($name::from_percent(10)),
$name::from_percent(1)
);
}
#[test]
fn per_thing_square_works() {
assert_eq!($name::from_percent(100).square(), $name::from_percent(100));
assert_eq!($name::from_percent(50).square(), $name::from_percent(25));
assert_eq!($name::from_percent(10).square(), $name::from_percent(1));
assert_eq!(
$name::from_percent(2).square(),
$name::from_parts((4 * <$upper_type>::from($max) / 100 / 100) as $type)
);
}
#[test]
fn per_things_div_works() {
assert_eq!($name::from_percent(10) / $name::from_percent(20),
$name::from_percent(50)
);
assert_eq!($name::from_percent(10) / $name::from_percent(10),
$name::from_percent(100)
);
assert_eq!($name::from_percent(10) / $name::from_percent(0),
$name::from_percent(100)
);
assert_eq!($name::from_percent(10) / $name::from_percent(5),
$name::from_percent(100)
);
assert_eq!($name::from_percent(100) / $name::from_percent(50),
$name::from_percent(100)
);
}
}
};
}
implement_per_thing!(
Percent,
test_per_cent,
[u32, u64, u128],
100u8,
u8,
u16,
"_Percent_",
);
implement_per_thing!(
Permill,
test_permill,
[u32, u64, u128],
1_000_000u32,
u32,
u64,
"_Parts per Million_",
);
implement_per_thing!(
Perbill,
test_perbill,
[u32, u64, u128],
1_000_000_000u32,
u32,
u64,
"_Parts per Billion_",
);
implement_per_thing!(
Perquintill,
test_perquintill,
[u64, u128],
1_000_000_000_000_000_000u64,
u64,
u128,
"_Parts per Quintillion_",
);