mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 19:51:05 +00:00
staking: Flexible generation of reward curve and associated tweaks (#8327)
* Initial abstraction * Alter rest of APIs * Fixes * Some extra getters in Gilt pallet. * Refactor Gilt to avoid u128 conversions * Simplify and improve pow in per_things * Add scalar division to per_things * Renaming from_fraction -> from_float, drop _approximation * Fixes * Fixes * Fixes * Fixes * Make stuff build * Fixes * Fixes * Fixes * Fixes * Update .gitignore Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/gilt/src/lib.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Update frame/gilt/src/mock.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Fixes * Fixes * Fixes Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
@@ -38,75 +38,75 @@ fn main() {
|
||||
|
||||
// peru16
|
||||
let (smaller, bigger) = (u16_pair.0.min(u16_pair.1), u16_pair.0.max(u16_pair.1));
|
||||
let ratio = PerU16::from_rational_approximation(smaller, bigger);
|
||||
let ratio = PerU16::from_rational(smaller, bigger);
|
||||
assert_per_thing_equal_error(
|
||||
ratio,
|
||||
PerU16::from_fraction(smaller as f64 / bigger.max(1) as f64),
|
||||
PerU16::from_float(smaller as f64 / bigger.max(1) as f64),
|
||||
1,
|
||||
);
|
||||
let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1));
|
||||
let ratio = PerU16::from_rational_approximation(smaller, bigger);
|
||||
let ratio = PerU16::from_rational(smaller, bigger);
|
||||
assert_per_thing_equal_error(
|
||||
ratio,
|
||||
PerU16::from_fraction(smaller as f64 / bigger.max(1) as f64),
|
||||
PerU16::from_float(smaller as f64 / bigger.max(1) as f64),
|
||||
1,
|
||||
);
|
||||
let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1));
|
||||
let ratio = PerU16::from_rational_approximation(smaller, bigger);
|
||||
let ratio = PerU16::from_rational(smaller, bigger);
|
||||
assert_per_thing_equal_error(
|
||||
ratio,
|
||||
PerU16::from_fraction(smaller as f64 / bigger.max(1) as f64),
|
||||
PerU16::from_float(smaller as f64 / bigger.max(1) as f64),
|
||||
1,
|
||||
);
|
||||
|
||||
// percent
|
||||
let (smaller, bigger) = (u16_pair.0.min(u16_pair.1), u16_pair.0.max(u16_pair.1));
|
||||
let ratio = Percent::from_rational_approximation(smaller, bigger);
|
||||
let ratio = Percent::from_rational(smaller, bigger);
|
||||
assert_per_thing_equal_error(
|
||||
ratio,
|
||||
Percent::from_fraction(smaller as f64 / bigger.max(1) as f64),
|
||||
Percent::from_float(smaller as f64 / bigger.max(1) as f64),
|
||||
1,
|
||||
);
|
||||
|
||||
let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1));
|
||||
let ratio = Percent::from_rational_approximation(smaller, bigger);
|
||||
let ratio = Percent::from_rational(smaller, bigger);
|
||||
assert_per_thing_equal_error(
|
||||
ratio,
|
||||
Percent::from_fraction(smaller as f64 / bigger.max(1) as f64),
|
||||
Percent::from_float(smaller as f64 / bigger.max(1) as f64),
|
||||
1,
|
||||
);
|
||||
|
||||
let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1));
|
||||
let ratio = Percent::from_rational_approximation(smaller, bigger);
|
||||
let ratio = Percent::from_rational(smaller, bigger);
|
||||
assert_per_thing_equal_error(
|
||||
ratio,
|
||||
Percent::from_fraction(smaller as f64 / bigger.max(1) as f64),
|
||||
Percent::from_float(smaller as f64 / bigger.max(1) as f64),
|
||||
1,
|
||||
);
|
||||
|
||||
// perbill
|
||||
let (smaller, bigger) = (u32_pair.0.min(u32_pair.1), u32_pair.0.max(u32_pair.1));
|
||||
let ratio = Perbill::from_rational_approximation(smaller, bigger);
|
||||
let ratio = Perbill::from_rational(smaller, bigger);
|
||||
assert_per_thing_equal_error(
|
||||
ratio,
|
||||
Perbill::from_fraction(smaller as f64 / bigger.max(1) as f64),
|
||||
Perbill::from_float(smaller as f64 / bigger.max(1) as f64),
|
||||
100,
|
||||
);
|
||||
|
||||
let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1));
|
||||
let ratio = Perbill::from_rational_approximation(smaller, bigger);
|
||||
let ratio = Perbill::from_rational(smaller, bigger);
|
||||
assert_per_thing_equal_error(
|
||||
ratio,
|
||||
Perbill::from_fraction(smaller as f64 / bigger.max(1) as f64),
|
||||
Perbill::from_float(smaller as f64 / bigger.max(1) as f64),
|
||||
100,
|
||||
);
|
||||
|
||||
// perquintillion
|
||||
let (smaller, bigger) = (u64_pair.0.min(u64_pair.1), u64_pair.0.max(u64_pair.1));
|
||||
let ratio = Perquintill::from_rational_approximation(smaller, bigger);
|
||||
let ratio = Perquintill::from_rational(smaller, bigger);
|
||||
assert_per_thing_equal_error(
|
||||
ratio,
|
||||
Perquintill::from_fraction(smaller as f64 / bigger.max(1) as f64),
|
||||
Perquintill::from_float(smaller as f64 / bigger.max(1) as f64),
|
||||
1000,
|
||||
);
|
||||
|
||||
|
||||
@@ -376,7 +376,7 @@ macro_rules! implement_fixed {
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", test))]
|
||||
pub fn from_fraction(x: f64) -> Self {
|
||||
pub fn from_float(x: f64) -> Self {
|
||||
Self((x * (<Self as FixedPointNumber>::DIV as f64)) as $inner_type)
|
||||
}
|
||||
|
||||
|
||||
@@ -494,7 +494,7 @@ mod threshold_compare_tests {
|
||||
fn peru16_rational_does_not_overflow() {
|
||||
// A historical example that will panic only for per_thing type that are created with
|
||||
// maximum capacity of their type, e.g. PerU16.
|
||||
let _ = PerU16::from_rational_approximation(17424870u32, 17424870);
|
||||
let _ = PerU16::from_rational(17424870u32, 17424870);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -18,8 +18,9 @@
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use sp_std::{ops, fmt, prelude::*, convert::TryInto};
|
||||
use sp_std::{ops, fmt, prelude::*, convert::{TryFrom, TryInto}};
|
||||
use codec::{Encode, CompactAs};
|
||||
use num_traits::Pow;
|
||||
use crate::traits::{
|
||||
SaturatedConversion, UniqueSaturatedInto, Saturating, BaseArithmetic, Bounded, Zero, Unsigned,
|
||||
One,
|
||||
@@ -36,6 +37,7 @@ pub type UpperOf<P> = <P as PerThing>::Upper;
|
||||
/// `X`_.
|
||||
pub trait PerThing:
|
||||
Sized + Saturating + Copy + Default + Eq + PartialEq + Ord + PartialOrd + Bounded + fmt::Debug
|
||||
+ ops::Div<Output=Self> + ops::Mul<Output=Self> + Pow<usize, Output=Self>
|
||||
{
|
||||
/// The data type used to build this per-thingy.
|
||||
type Inner: BaseArithmetic + Unsigned + Copy + Into<u128> + fmt::Debug;
|
||||
@@ -70,14 +72,14 @@ pub trait PerThing:
|
||||
fn from_percent(x: Self::Inner) -> Self {
|
||||
let a: Self::Inner = x.min(100.into());
|
||||
let b: Self::Inner = 100.into();
|
||||
Self::from_rational_approximation::<Self::Inner>(a, b)
|
||||
Self::from_rational::<Self::Inner>(a, b)
|
||||
}
|
||||
|
||||
/// Return the product of multiplication of this value by itself.
|
||||
fn square(self) -> Self {
|
||||
let p = Self::Upper::from(self.deconstruct());
|
||||
let q = Self::Upper::from(Self::ACCURACY);
|
||||
Self::from_rational_approximation::<Self::Upper>(p * p, q * q)
|
||||
Self::from_rational::<Self::Upper>(p * p, q * q)
|
||||
}
|
||||
|
||||
/// Return the part left when `self` is saturating-subtracted from `Self::one()`.
|
||||
@@ -204,7 +206,12 @@ pub trait PerThing:
|
||||
|
||||
/// Converts a fraction into `Self`.
|
||||
#[cfg(feature = "std")]
|
||||
fn from_fraction(x: f64) -> Self;
|
||||
fn from_float(x: f64) -> Self;
|
||||
|
||||
/// Same as `Self::from_float`.
|
||||
#[deprecated = "Use from_float instead"]
|
||||
#[cfg(feature = "std")]
|
||||
fn from_fraction(x: f64) -> Self { Self::from_float(x) }
|
||||
|
||||
/// Approximate the fraction `p/q` into a per-thing fraction. This will never overflow.
|
||||
///
|
||||
@@ -219,16 +226,28 @@ pub trait PerThing:
|
||||
/// # fn main () {
|
||||
/// // 989/100 is technically closer to 99%.
|
||||
/// assert_eq!(
|
||||
/// Percent::from_rational_approximation(989u64, 1000),
|
||||
/// Percent::from_rational(989u64, 1000),
|
||||
/// Percent::from_parts(98),
|
||||
/// );
|
||||
/// # }
|
||||
/// ```
|
||||
fn from_rational_approximation<N>(p: N, q: N) -> Self
|
||||
fn from_rational<N>(p: N, q: N) -> 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,
|
||||
Self::Inner: Into<N>;
|
||||
|
||||
/// Same as `Self::from_rational`.
|
||||
#[deprecated = "Use from_rational instead"]
|
||||
fn from_rational_approximation<N>(p: N, q: N) -> 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>,
|
||||
{
|
||||
Self::from_rational(p, q)
|
||||
}
|
||||
}
|
||||
|
||||
/// The rounding method to use.
|
||||
@@ -369,11 +388,11 @@ macro_rules! implement_per_thing {
|
||||
|
||||
/// NOTE: saturate to 0 or 1 if x is beyond `[0, 1]`
|
||||
#[cfg(feature = "std")]
|
||||
fn from_fraction(x: f64) -> Self {
|
||||
fn from_float(x: f64) -> Self {
|
||||
Self::from_parts((x.max(0.).min(1.) * $max as f64) as Self::Inner)
|
||||
}
|
||||
|
||||
fn from_rational_approximation<N>(p: N, q: N) -> Self
|
||||
fn from_rational<N>(p: N, q: N) -> 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
|
||||
@@ -471,20 +490,31 @@ macro_rules! implement_per_thing {
|
||||
PerThing::square(self)
|
||||
}
|
||||
|
||||
/// See [`PerThing::from_fraction`].
|
||||
/// See [`PerThing::from_float`].
|
||||
#[cfg(feature = "std")]
|
||||
pub fn from_fraction(x: f64) -> Self {
|
||||
<Self as PerThing>::from_fraction(x)
|
||||
pub fn from_float(x: f64) -> Self {
|
||||
<Self as PerThing>::from_float(x)
|
||||
}
|
||||
|
||||
/// See [`PerThing::from_rational_approximation`].
|
||||
/// 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>,
|
||||
{
|
||||
<Self as PerThing>::from_rational_approximation(p, q)
|
||||
<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>,
|
||||
{
|
||||
<Self as PerThing>::from_rational(p, q)
|
||||
}
|
||||
|
||||
/// See [`PerThing::mul_floor`].
|
||||
@@ -561,37 +591,13 @@ macro_rules! implement_per_thing {
|
||||
/// Saturating multiply. Compute `self * rhs`, saturating at the numeric bounds instead of
|
||||
/// overflowing. This operation is lossy.
|
||||
fn saturating_mul(self, rhs: Self) -> Self {
|
||||
let a = self.0 as $upper_type;
|
||||
let b = rhs.0 as $upper_type;
|
||||
let m = <$upper_type>::from($max);
|
||||
let parts = a * b / m;
|
||||
// This will always fit into $type.
|
||||
Self::from_parts(parts as $type)
|
||||
self * rhs
|
||||
}
|
||||
|
||||
/// Saturating exponentiation. Computes `self.pow(exp)`, saturating at the numeric
|
||||
/// bounds instead of overflowing. This operation is lossy.
|
||||
fn saturating_pow(self, exp: usize) -> Self {
|
||||
if self.is_zero() || self.is_one() {
|
||||
self
|
||||
} else {
|
||||
let p = <$name as PerThing>::Upper::from(self.deconstruct());
|
||||
let q = <$name as PerThing>::Upper::from(Self::ACCURACY);
|
||||
let mut s = Self::one();
|
||||
for _ in 0..exp {
|
||||
if s.is_zero() {
|
||||
break;
|
||||
} else {
|
||||
// x^2 always fits in Self::Upper if x fits in Self::Inner.
|
||||
// Verified by a test.
|
||||
s = Self::from_rational_approximation(
|
||||
<$name as PerThing>::Upper::from(s.deconstruct()) * p,
|
||||
q * q,
|
||||
);
|
||||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
self.pow(exp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -607,7 +613,7 @@ macro_rules! implement_per_thing {
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::traits::Bounded for $name {
|
||||
impl Bounded for $name {
|
||||
fn min_value() -> Self {
|
||||
<Self as PerThing>::zero()
|
||||
}
|
||||
@@ -617,13 +623,48 @@ macro_rules! implement_per_thing {
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Mul for $name {
|
||||
type Output = Self;
|
||||
|
||||
fn mul(self, rhs: Self) -> Self::Output {
|
||||
let a = self.0 as $upper_type;
|
||||
let b = rhs.0 as $upper_type;
|
||||
let m = <$upper_type>::from($max);
|
||||
let parts = a * b / m;
|
||||
// This will always fit into $type.
|
||||
Self::from_parts(parts as $type)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pow<usize> for $name {
|
||||
type Output = Self;
|
||||
|
||||
fn pow(self, exp: usize) -> Self::Output {
|
||||
if exp == 0 || self.is_one() {
|
||||
return Self::one()
|
||||
}
|
||||
let mut result = self;
|
||||
let mut exp = exp - 1;
|
||||
while exp > 0 && !result.is_zero() {
|
||||
if exp % 2 == 0 {
|
||||
result = result.square();
|
||||
exp /= 2;
|
||||
} else {
|
||||
result = result * self;
|
||||
exp -= 1;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
Self::from_rational(p, q)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -648,6 +689,13 @@ macro_rules! implement_per_thing {
|
||||
}
|
||||
}
|
||||
|
||||
impl<N> ops::Div<N> for $name where $type: TryFrom<N> {
|
||||
type Output = Self;
|
||||
fn div(self, b: N) -> Self::Output {
|
||||
<$type>::try_from(b).map_or(Self::zero(), |d| Self::from_parts(self.0 / d))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod $test_mod {
|
||||
use codec::{Encode, Decode};
|
||||
@@ -657,13 +705,13 @@ macro_rules! implement_per_thing {
|
||||
#[test]
|
||||
fn macro_expanded_correctly() {
|
||||
// needed for the `from_percent` to work. UPDATE: this is no longer needed; yet note
|
||||
// that tests that use percentage or fractions such as $name::from_fraction(0.2) to
|
||||
// that tests that use percentage or fractions such as $name::from_float(0.2) to
|
||||
// create values will most likely be inaccurate when used with per_things that are
|
||||
// not multiples of 100.
|
||||
// assert!($max >= 100);
|
||||
// assert!($max % 100 == 0);
|
||||
|
||||
// needed for `from_rational_approximation`
|
||||
// needed for `from_rational`
|
||||
assert!(2 * ($max as $upper_type) < <$upper_type>::max_value());
|
||||
assert!(<$upper_type>::from($max) < <$upper_type>::max_value());
|
||||
|
||||
@@ -737,11 +785,11 @@ macro_rules! implement_per_thing {
|
||||
assert_eq!($name::from_percent(100), $name::from_parts($max));
|
||||
assert_eq!($name::from_percent(200), $name::from_parts($max));
|
||||
|
||||
assert_eq!($name::from_fraction(0.0), $name::from_parts(Zero::zero()));
|
||||
assert_eq!($name::from_fraction(0.1), $name::from_parts($max / 10));
|
||||
assert_eq!($name::from_fraction(1.0), $name::from_parts($max));
|
||||
assert_eq!($name::from_fraction(2.0), $name::from_parts($max));
|
||||
assert_eq!($name::from_fraction(-1.0), $name::from_parts(Zero::zero()));
|
||||
assert_eq!($name::from_float(0.0), $name::from_parts(Zero::zero()));
|
||||
assert_eq!($name::from_float(0.1), $name::from_parts($max / 10));
|
||||
assert_eq!($name::from_float(1.0), $name::from_parts($max));
|
||||
assert_eq!($name::from_float(2.0), $name::from_parts($max));
|
||||
assert_eq!($name::from_float(-1.0), $name::from_parts(Zero::zero()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -763,7 +811,7 @@ macro_rules! implement_per_thing {
|
||||
($num_type:tt) => {
|
||||
// multiplication from all sort of from_percent
|
||||
assert_eq!(
|
||||
$name::from_fraction(1.0) * $num_type::max_value(),
|
||||
$name::from_float(1.0) * $num_type::max_value(),
|
||||
$num_type::max_value()
|
||||
);
|
||||
if $max % 100 == 0 {
|
||||
@@ -773,7 +821,7 @@ macro_rules! implement_per_thing {
|
||||
1,
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_fraction(0.5) * $num_type::max_value(),
|
||||
$name::from_float(0.5) * $num_type::max_value(),
|
||||
$num_type::max_value() / 2,
|
||||
);
|
||||
assert_eq_error_rate!(
|
||||
@@ -783,30 +831,30 @@ macro_rules! implement_per_thing {
|
||||
);
|
||||
} else {
|
||||
assert_eq!(
|
||||
$name::from_fraction(0.99) * <$num_type>::max_value(),
|
||||
$name::from_float(0.99) * <$num_type>::max_value(),
|
||||
(
|
||||
(
|
||||
u256ify!($name::from_fraction(0.99).0) *
|
||||
u256ify!($name::from_float(0.99).0) *
|
||||
u256ify!(<$num_type>::max_value()) /
|
||||
u256ify!($max)
|
||||
).as_u128()
|
||||
) as $num_type,
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_fraction(0.50) * <$num_type>::max_value(),
|
||||
$name::from_float(0.50) * <$num_type>::max_value(),
|
||||
(
|
||||
(
|
||||
u256ify!($name::from_fraction(0.50).0) *
|
||||
u256ify!($name::from_float(0.50).0) *
|
||||
u256ify!(<$num_type>::max_value()) /
|
||||
u256ify!($max)
|
||||
).as_u128()
|
||||
) as $num_type,
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_fraction(0.01) * <$num_type>::max_value(),
|
||||
$name::from_float(0.01) * <$num_type>::max_value(),
|
||||
(
|
||||
(
|
||||
u256ify!($name::from_fraction(0.01).0) *
|
||||
u256ify!($name::from_float(0.01).0) *
|
||||
u256ify!(<$num_type>::max_value()) /
|
||||
u256ify!($max)
|
||||
).as_u128()
|
||||
@@ -814,7 +862,7 @@ macro_rules! implement_per_thing {
|
||||
);
|
||||
}
|
||||
|
||||
assert_eq!($name::from_fraction(0.0) * $num_type::max_value(), 0);
|
||||
assert_eq!($name::from_float(0.0) * $num_type::max_value(), 0);
|
||||
|
||||
// // multiplication with bounds
|
||||
assert_eq!($name::one() * $num_type::max_value(), $num_type::max_value());
|
||||
@@ -828,7 +876,7 @@ macro_rules! implement_per_thing {
|
||||
|
||||
// accuracy test
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(1 as $type, 3) * 30 as $type,
|
||||
$name::from_rational(1 as $type, 3) * 30 as $type,
|
||||
10,
|
||||
);
|
||||
|
||||
@@ -837,10 +885,10 @@ macro_rules! implement_per_thing {
|
||||
|
||||
#[test]
|
||||
fn per_thing_mul_rounds_to_nearest_number() {
|
||||
assert_eq!($name::from_fraction(0.33) * 10u64, 3);
|
||||
assert_eq!($name::from_fraction(0.34) * 10u64, 3);
|
||||
assert_eq!($name::from_fraction(0.35) * 10u64, 3);
|
||||
assert_eq!($name::from_fraction(0.36) * 10u64, 4);
|
||||
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);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -858,33 +906,33 @@ macro_rules! implement_per_thing {
|
||||
($num_type:tt) => {
|
||||
// within accuracy boundary
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(1 as $num_type, 0),
|
||||
$name::from_rational(1 as $num_type, 0),
|
||||
$name::one(),
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(1 as $num_type, 1),
|
||||
$name::from_rational(1 as $num_type, 1),
|
||||
$name::one(),
|
||||
);
|
||||
assert_eq_error_rate!(
|
||||
$name::from_rational_approximation(1 as $num_type, 3).0,
|
||||
$name::from_rational(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_fraction(0.10),
|
||||
$name::from_rational(1 as $num_type, 10),
|
||||
$name::from_float(0.10),
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(1 as $num_type, 4),
|
||||
$name::from_fraction(0.25),
|
||||
$name::from_rational(1 as $num_type, 4),
|
||||
$name::from_float(0.25),
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(1 as $num_type, 4),
|
||||
$name::from_rational_approximation(2 as $num_type, 8),
|
||||
$name::from_rational(1 as $num_type, 4),
|
||||
$name::from_rational(2 as $num_type, 8),
|
||||
);
|
||||
// no accurate anymore but won't overflow.
|
||||
assert_eq_error_rate!(
|
||||
$name::from_rational_approximation(
|
||||
$name::from_rational(
|
||||
$num_type::max_value() - 1,
|
||||
$num_type::max_value()
|
||||
).0 as $upper_type,
|
||||
@@ -892,7 +940,7 @@ macro_rules! implement_per_thing {
|
||||
2,
|
||||
);
|
||||
assert_eq_error_rate!(
|
||||
$name::from_rational_approximation(
|
||||
$name::from_rational(
|
||||
$num_type::max_value() / 3,
|
||||
$num_type::max_value()
|
||||
).0 as $upper_type,
|
||||
@@ -900,7 +948,7 @@ macro_rules! implement_per_thing {
|
||||
2,
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(1, $num_type::max_value()),
|
||||
$name::from_rational(1, $num_type::max_value()),
|
||||
$name::zero(),
|
||||
);
|
||||
};
|
||||
@@ -914,28 +962,28 @@ macro_rules! implement_per_thing {
|
||||
|
||||
// almost at the edge
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(max_value - 1, max_value + 1),
|
||||
$name::from_rational(max_value - 1, max_value + 1),
|
||||
$name::from_parts($max - 2),
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(1, $max - 1),
|
||||
$name::from_rational(1, $max - 1),
|
||||
$name::from_parts(1),
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(1, $max),
|
||||
$name::from_rational(1, $max),
|
||||
$name::from_parts(1),
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(2, 2 * max_value - 1),
|
||||
$name::from_rational(2, 2 * max_value - 1),
|
||||
$name::from_parts(1),
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(1, max_value + 1),
|
||||
$name::from_rational(1, max_value + 1),
|
||||
$name::zero(),
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_rational_approximation(3 * max_value / 2, 3 * max_value),
|
||||
$name::from_fraction(0.5),
|
||||
$name::from_rational(3 * max_value / 2, 3 * max_value),
|
||||
$name::from_float(0.5),
|
||||
);
|
||||
|
||||
$(per_thing_from_rationale_approx_test!($test_units);)*
|
||||
@@ -943,66 +991,66 @@ macro_rules! implement_per_thing {
|
||||
|
||||
#[test]
|
||||
fn per_things_mul_operates_in_output_type() {
|
||||
// assert_eq!($name::from_fraction(0.5) * 100u32, 50u32);
|
||||
assert_eq!($name::from_fraction(0.5) * 100u64, 50u64);
|
||||
assert_eq!($name::from_fraction(0.5) * 100u128, 50u128);
|
||||
// assert_eq!($name::from_float(0.5) * 100u32, 50u32);
|
||||
assert_eq!($name::from_float(0.5) * 100u64, 50u64);
|
||||
assert_eq!($name::from_float(0.5) * 100u128, 50u128);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn per_thing_saturating_op_works() {
|
||||
assert_eq_error_rate!(
|
||||
$name::from_fraction(0.5).saturating_add($name::from_fraction(0.4)).0 as $upper_type,
|
||||
$name::from_fraction(0.9).0 as $upper_type,
|
||||
$name::from_float(0.5).saturating_add($name::from_float(0.4)).0 as $upper_type,
|
||||
$name::from_float(0.9).0 as $upper_type,
|
||||
2,
|
||||
);
|
||||
assert_eq_error_rate!(
|
||||
$name::from_fraction(0.5).saturating_add($name::from_fraction(0.5)).0 as $upper_type,
|
||||
$name::from_float(0.5).saturating_add($name::from_float(0.5)).0 as $upper_type,
|
||||
$name::one().0 as $upper_type,
|
||||
2,
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_fraction(0.6).saturating_add($name::from_fraction(0.5)),
|
||||
$name::from_float(0.6).saturating_add($name::from_float(0.5)),
|
||||
$name::one(),
|
||||
);
|
||||
|
||||
assert_eq_error_rate!(
|
||||
$name::from_fraction(0.6).saturating_sub($name::from_fraction(0.5)).0 as $upper_type,
|
||||
$name::from_fraction(0.1).0 as $upper_type,
|
||||
$name::from_float(0.6).saturating_sub($name::from_float(0.5)).0 as $upper_type,
|
||||
$name::from_float(0.1).0 as $upper_type,
|
||||
2,
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_fraction(0.6).saturating_sub($name::from_fraction(0.6)),
|
||||
$name::from_fraction(0.0),
|
||||
$name::from_float(0.6).saturating_sub($name::from_float(0.6)),
|
||||
$name::from_float(0.0),
|
||||
);
|
||||
assert_eq!(
|
||||
$name::from_fraction(0.6).saturating_sub($name::from_fraction(0.7)),
|
||||
$name::from_fraction(0.0),
|
||||
$name::from_float(0.6).saturating_sub($name::from_float(0.7)),
|
||||
$name::from_float(0.0),
|
||||
);
|
||||
|
||||
assert_eq_error_rate!(
|
||||
$name::from_fraction(0.5).saturating_mul($name::from_fraction(0.5)).0 as $upper_type,
|
||||
$name::from_fraction(0.25).0 as $upper_type,
|
||||
$name::from_float(0.5).saturating_mul($name::from_float(0.5)).0 as $upper_type,
|
||||
$name::from_float(0.25).0 as $upper_type,
|
||||
2,
|
||||
);
|
||||
assert_eq_error_rate!(
|
||||
$name::from_fraction(0.2).saturating_mul($name::from_fraction(0.2)).0 as $upper_type,
|
||||
$name::from_fraction(0.04).0 as $upper_type,
|
||||
$name::from_float(0.2).saturating_mul($name::from_float(0.2)).0 as $upper_type,
|
||||
$name::from_float(0.04).0 as $upper_type,
|
||||
2,
|
||||
);
|
||||
assert_eq_error_rate!(
|
||||
$name::from_fraction(0.1).saturating_mul($name::from_fraction(0.1)).0 as $upper_type,
|
||||
$name::from_fraction(0.01).0 as $upper_type,
|
||||
$name::from_float(0.1).saturating_mul($name::from_float(0.1)).0 as $upper_type,
|
||||
$name::from_float(0.01).0 as $upper_type,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn per_thing_square_works() {
|
||||
assert_eq!($name::from_fraction(1.0).square(), $name::from_fraction(1.0));
|
||||
assert_eq!($name::from_fraction(0.5).square(), $name::from_fraction(0.25));
|
||||
assert_eq!($name::from_fraction(0.1).square(), $name::from_fraction(0.01));
|
||||
assert_eq!($name::from_float(1.0).square(), $name::from_float(1.0));
|
||||
assert_eq!($name::from_float(0.5).square(), $name::from_float(0.25));
|
||||
assert_eq!($name::from_float(0.1).square(), $name::from_float(0.01));
|
||||
assert_eq!(
|
||||
$name::from_fraction(0.02).square(),
|
||||
$name::from_float(0.02).square(),
|
||||
$name::from_parts((4 * <$upper_type>::from($max) / 100 / 100) as $type)
|
||||
);
|
||||
}
|
||||
@@ -1011,30 +1059,30 @@ macro_rules! implement_per_thing {
|
||||
fn per_things_div_works() {
|
||||
// normal
|
||||
assert_eq_error_rate!(
|
||||
($name::from_fraction(0.1) / $name::from_fraction(0.20)).0 as $upper_type,
|
||||
$name::from_fraction(0.50).0 as $upper_type,
|
||||
($name::from_float(0.1) / $name::from_float(0.20)).0 as $upper_type,
|
||||
$name::from_float(0.50).0 as $upper_type,
|
||||
2,
|
||||
);
|
||||
assert_eq_error_rate!(
|
||||
($name::from_fraction(0.1) / $name::from_fraction(0.10)).0 as $upper_type,
|
||||
$name::from_fraction(1.0).0 as $upper_type,
|
||||
($name::from_float(0.1) / $name::from_float(0.10)).0 as $upper_type,
|
||||
$name::from_float(1.0).0 as $upper_type,
|
||||
2,
|
||||
);
|
||||
assert_eq_error_rate!(
|
||||
($name::from_fraction(0.1) / $name::from_fraction(0.0)).0 as $upper_type,
|
||||
$name::from_fraction(1.0).0 as $upper_type,
|
||||
($name::from_float(0.1) / $name::from_float(0.0)).0 as $upper_type,
|
||||
$name::from_float(1.0).0 as $upper_type,
|
||||
2,
|
||||
);
|
||||
|
||||
// will not overflow
|
||||
assert_eq_error_rate!(
|
||||
($name::from_fraction(0.10) / $name::from_fraction(0.05)).0 as $upper_type,
|
||||
$name::from_fraction(1.0).0 as $upper_type,
|
||||
($name::from_float(0.10) / $name::from_float(0.05)).0 as $upper_type,
|
||||
$name::from_float(1.0).0 as $upper_type,
|
||||
2,
|
||||
);
|
||||
assert_eq_error_rate!(
|
||||
($name::from_fraction(1.0) / $name::from_fraction(0.5)).0 as $upper_type,
|
||||
$name::from_fraction(1.0).0 as $upper_type,
|
||||
($name::from_float(1.0) / $name::from_float(0.5)).0 as $upper_type,
|
||||
$name::from_float(1.0).0 as $upper_type,
|
||||
2,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -95,15 +95,15 @@ mod tests {
|
||||
Assignment {
|
||||
who: 1u32,
|
||||
distribution: vec![
|
||||
(10u32, Perbill::from_fraction(0.5)),
|
||||
(20, Perbill::from_fraction(0.5)),
|
||||
(10u32, Perbill::from_float(0.5)),
|
||||
(20, Perbill::from_float(0.5)),
|
||||
],
|
||||
},
|
||||
Assignment {
|
||||
who: 2u32,
|
||||
distribution: vec![
|
||||
(10, Perbill::from_fraction(0.33)),
|
||||
(20, Perbill::from_fraction(0.67)),
|
||||
(10, Perbill::from_float(0.33)),
|
||||
(20, Perbill::from_float(0.67)),
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -371,7 +371,7 @@ impl<AccountId: IdentifierT> Voter<AccountId> {
|
||||
.edges
|
||||
.into_iter()
|
||||
.filter_map(|e| {
|
||||
let per_thing = P::from_rational_approximation(e.weight, budget);
|
||||
let per_thing = P::from_rational(e.weight, budget);
|
||||
// trim zero edges.
|
||||
if per_thing.is_zero() { None } else { Some((e.who, per_thing)) }
|
||||
}).collect::<Vec<_>>();
|
||||
@@ -551,7 +551,7 @@ impl<AccountId> StakedAssignment<AccountId> {
|
||||
let distribution = self.distribution
|
||||
.into_iter()
|
||||
.filter_map(|(target, w)| {
|
||||
let per_thing = P::from_rational_approximation(w, stake);
|
||||
let per_thing = P::from_rational(w, stake);
|
||||
if per_thing == Bounded::min_value() {
|
||||
None
|
||||
} else {
|
||||
|
||||
@@ -345,7 +345,7 @@ pub(crate) fn run_and_compare<Output: PerThing128>(
|
||||
for (candidate, per_thingy) in distribution {
|
||||
if let Some(float_assignment) = float_assignments.1.iter().find(|x| x.0 == candidate ) {
|
||||
assert_eq_error_rate!(
|
||||
Output::from_fraction(float_assignment.1).deconstruct(),
|
||||
Output::from_float(float_assignment.1).deconstruct(),
|
||||
per_thingy.deconstruct(),
|
||||
Output::Inner::one(),
|
||||
);
|
||||
|
||||
@@ -101,7 +101,7 @@ pub(crate) fn calculate_max_score<AccountId: IdentifierT, P: PerThing>(
|
||||
for edge in voter.edges.iter() {
|
||||
let edge_candidate = edge.candidate.borrow();
|
||||
if edge_candidate.elected {
|
||||
let edge_contribution: ExtendedBalance = P::from_rational_approximation(
|
||||
let edge_contribution: ExtendedBalance = P::from_rational(
|
||||
edge.weight,
|
||||
edge_candidate.backed_stake,
|
||||
).deconstruct().into();
|
||||
|
||||
@@ -37,7 +37,6 @@ use crate::{
|
||||
use sp_std::{rc::Rc, vec::Vec};
|
||||
use sp_std::collections::btree_map::BTreeMap;
|
||||
use sp_arithmetic::{traits::Zero, Perbill};
|
||||
|
||||
/// The type used as the threshold.
|
||||
///
|
||||
/// Just some reading sugar; Must always be same as [`ExtendedBalance`];
|
||||
@@ -364,7 +363,7 @@ fn slack<AccountId: IdentifierT>(voter: &Voter<AccountId>, t: Threshold) -> Exte
|
||||
let candidate = edge.candidate.borrow();
|
||||
if candidate.elected {
|
||||
let extra =
|
||||
Perbill::one().min(Perbill::from_rational_approximation(t, candidate.backed_stake))
|
||||
Perbill::one().min(Perbill::from_rational(t, candidate.backed_stake))
|
||||
* edge.weight;
|
||||
acc.saturating_add(extra)
|
||||
} else {
|
||||
|
||||
@@ -1095,7 +1095,7 @@ mod score {
|
||||
is_score_better(
|
||||
claim.clone(),
|
||||
initial.clone(),
|
||||
Perbill::from_rational_approximation(1u32, 10_000),
|
||||
Perbill::from_rational(1u32, 10_000),
|
||||
),
|
||||
true,
|
||||
);
|
||||
@@ -1104,7 +1104,7 @@ mod score {
|
||||
is_score_better(
|
||||
claim.clone(),
|
||||
initial.clone(),
|
||||
Perbill::from_rational_approximation(2u32, 10_000),
|
||||
Perbill::from_rational(2u32, 10_000),
|
||||
),
|
||||
true,
|
||||
);
|
||||
@@ -1113,7 +1113,7 @@ mod score {
|
||||
is_score_better(
|
||||
claim.clone(),
|
||||
initial.clone(),
|
||||
Perbill::from_rational_approximation(3u32, 10_000),
|
||||
Perbill::from_rational(3u32, 10_000),
|
||||
),
|
||||
true,
|
||||
);
|
||||
@@ -1122,7 +1122,7 @@ mod score {
|
||||
is_score_better(
|
||||
claim.clone(),
|
||||
initial.clone(),
|
||||
Perbill::from_rational_approximation(4u32, 10_000),
|
||||
Perbill::from_rational(4u32, 10_000),
|
||||
),
|
||||
true,
|
||||
);
|
||||
@@ -1131,7 +1131,7 @@ mod score {
|
||||
is_score_better(
|
||||
claim.clone(),
|
||||
initial.clone(),
|
||||
Perbill::from_rational_approximation(5u32, 10_000),
|
||||
Perbill::from_rational(5u32, 10_000),
|
||||
),
|
||||
false,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user