Per-things trait. (#4904)

* Give perthigns the trait it always deserved.

* Make staking and phragmen work with the new generic per_thing

* Make everything work together 🔨

* a bit of cleanup

* Clean usage

* Bump.

* Fix name

* fix grumbles

* hopefully fix the ui test

* Some grumbles

* revamp traits again

* Better naming again.
This commit is contained in:
Kian Paimani
2020-02-13 13:09:33 +01:00
committed by GitHub
parent e6454eb091
commit c871eaacbc
42 changed files with 346 additions and 241 deletions
+1 -1
View File
@@ -40,5 +40,5 @@ mod fixed64;
mod rational128;
pub use fixed64::Fixed64;
pub use per_things::{Percent, Permill, Perbill, Perquintill};
pub use per_things::{PerThing, Percent, Permill, Perbill, Perquintill};
pub use rational128::Rational128;
+130 -62
View File
@@ -19,34 +19,148 @@ use serde::{Serialize, Deserialize};
use sp_std::{ops, prelude::*, convert::TryInto};
use codec::{Encode, Decode, CompactAs};
use crate::traits::{SaturatedConversion, UniqueSaturatedInto, Saturating};
use crate::traits::{
SaturatedConversion, UniqueSaturatedInto, Saturating, BaseArithmetic,
};
use sp_debug_derive::RuntimeDebug;
/// Something that implements a fixed point ration with an arbitrary granularity `X`, as _parts per
/// `X`_.
pub trait PerThing: Sized + Saturating + Copy {
/// The data type used to build this per-thingy.
type Inner: BaseArithmetic + Copy;
/// accuracy of this type
const ACCURACY: Self::Inner;
/// NoThing
fn zero() -> Self;
/// `true` if this is nothing.
fn is_zero(&self) -> bool;
/// Everything.
fn one() -> Self;
/// Consume self and deconstruct into a raw numeric type.
fn deconstruct(self) -> Self::Inner;
/// From an explicitly defined number of parts per maximum of the type.
fn from_parts(parts: Self::Inner) -> Self;
/// Converts a percent into `Self`. Equal to `x / 100`.
fn from_percent(x: Self::Inner) -> Self;
/// Return the product of multiplication of this value by itself.
fn square(self) -> Self;
/// Converts a fraction into `Self`.
#[cfg(feature = "std")]
fn from_fraction(x: f64) -> Self;
/// Approximate the fraction `p/q` into a per-thing fraction. This will never overflow.
///
/// The computation of this approximation is performed in the generic type `N`. Given
/// `M` as the data type that can hold the maximum value of this per-thing (e.g. u32 for
/// perbill), this can only work if `N == M` or `N: From<M> + TryInto<M>`.
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 $(,)?) => {
/// A fixed point representation of a number between in the range [0, 1].
///
#[doc = $title]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, CompactAs)]
#[derive(Encode, Decode, Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, RuntimeDebug, CompactAs)]
pub struct $name($type);
impl $name {
impl PerThing for $name {
type Inner = $type;
/// The accuracy of this type.
const ACCURACY: Self::Inner = $max;
/// Nothing.
pub fn zero() -> Self { Self(0) }
fn zero() -> Self { Self(0) }
/// `true` if this is nothing.
pub fn is_zero(&self) -> bool { self.0 == 0 }
fn is_zero(&self) -> bool { self.0 == 0 }
/// Everything.
pub fn one() -> Self { Self($max) }
fn one() -> Self { Self($max) }
/// Consume self and deconstruct into a raw numeric type.
pub fn deconstruct(self) -> $type { self.0 }
fn deconstruct(self) -> Self::Inner { self.0 }
/// Return the scale at which this per-thing is working.
pub const fn accuracy() -> $type { $max }
/// From an explicitly defined number of parts per maximum of the type.
fn from_parts(parts: Self::Inner) -> Self {
Self([parts, $max][(parts > $max) as usize])
}
/// Converts a percent into `Self`. Equal to `x / 100`.
fn from_percent(x: Self::Inner) -> Self {
Self([x, 100][(x > 100) as usize] * ($max / 100))
}
/// Return the product of multiplication of this value by itself.
fn square(self) -> Self {
// both can be safely casted and multiplied.
let p: $upper_type = self.0 as $upper_type * self.0 as $upper_type;
let q: $upper_type = <$upper_type>::from($max) * <$upper_type>::from($max);
Self::from_rational_approximation(p, q)
}
/// Converts a fraction into `Self`.
#[cfg(feature = "std")]
fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as Self::Inner) }
/// Approximate the fraction `p/q` into a per-thing fraction. This will never overflow.
///
/// The computation of this approximation is performed in the generic type `N`. Given
/// `M` as the data type that can hold the maximum value of this per-thing (e.g. u32 for
/// perbill), this can only work if `N == M` or `N: From<M> + TryInto<M>`.
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>
{
// q cannot be zero.
let q = q.max((1 as Self::Inner).into());
// p should not be bigger than q.
let p = p.min(q.clone());
let factor = (q.clone() / $max.into()).max((1 as Self::Inner).into());
// q cannot overflow: (q / (q/$max)) < 2 * $max. p < q hence p also cannot overflow.
// this implies that Self::Inner must be able to fit 2 * $max.
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"
);
// `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)
}
}
/// Implement const functions
impl $name {
/// From an explicitly defined number of parts per maximum of the type.
///
/// This can be called at compile time.
@@ -61,58 +175,12 @@ macro_rules! implement_per_thing {
Self([x, 100][(x > 100) as usize] * ($max / 100))
}
/// Return the product of multiplication of this value by itself.
pub fn square(self) -> Self {
// both can be safely casted and multiplied.
let p: $upper_type = self.0 as $upper_type * self.0 as $upper_type;
let q: $upper_type = <$upper_type>::from($max) * <$upper_type>::from($max);
Self::from_rational_approximation(p, q)
}
/// Converts a fraction into `Self`.
#[cfg(feature = "std")]
pub fn from_fraction(x: f64) -> Self { Self((x * ($max as f64)) as $type) }
/// Approximate the fraction `p/q` into a per-thing fraction. This will never overflow.
/// Everything.
///
/// 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>`.
pub fn from_rational_approximation<N>(p: N, q: N) -> Self
where N: Clone + Ord + From<$type> + TryInto<$type> + ops::Div<N, Output=N>
{
// q cannot be zero.
let q = q.max((1 as $type).into());
// p should not be bigger than q.
let p = p.min(q.clone());
let factor = (q.clone() / $max.into()).max((1 as $type).into());
// q cannot overflow: (q / (q/$max)) < 2 * $max. p < q hence p also cannot overflow.
// this implies that $type must be able to fit 2 * $max.
let q_reduce: $type = (q / factor.clone())
.try_into()
.map_err(|_| "Failed to convert")
.expect(
"q / (q/$max) < (2 * $max). Macro prevents any type being created that \
does not satisfy this; qed"
);
let p_reduce: $type = (p / factor.clone())
.try_into()
.map_err(|_| "Failed to convert")
.expect(
"q / (q/$max) < (2 * $max). Macro prevents any type being created that \
does not satisfy this; qed"
);
// `p_reduced` and `q_reduced` are withing $type. Mul by another $max will always
// fit in $upper_type. This is guaranteed by the macro tests.
let part =
p_reduce as $upper_type
* <$upper_type>::from($max)
/ q_reduce as $upper_type;
$name(part as $type)
/// To avoid having to import `PerThing` when one needs to be used in test mocks.
#[cfg(feature = "std")]
pub fn one() -> Self {
<Self as PerThing>::one()
}
}
@@ -190,7 +258,7 @@ macro_rules! implement_per_thing {
#[cfg(test)]
mod $test_mod {
use codec::{Encode, Decode};
use super::{$name, Saturating, RuntimeDebug};
use super::{$name, Saturating, RuntimeDebug, PerThing};
use crate::traits::Zero;
@@ -248,7 +316,7 @@ macro_rules! implement_per_thing {
// some really basic stuff
assert_eq!($name::zero(), $name::from_parts(Zero::zero()));
assert_eq!($name::one(), $name::from_parts($max));
assert_eq!($name::accuracy(), $max);
assert_eq!($name::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));
+46 -35
View File
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! Primitives for the runtime modules.
//! Primitive traits for the runtime arithmetic.
use sp_std::{self, convert::{TryFrom, TryInto}};
use codec::HasCompact;
@@ -28,44 +28,55 @@ use sp_std::ops::{
RemAssign, Shl, Shr
};
/// A meta trait for arithmetic type operations, regardless of any limitation on size.
pub trait BaseArithmetic:
From<u8> +
Zero + One + IntegerSquareRoot +
Add<Self, Output = Self> + AddAssign<Self> +
Sub<Self, Output = Self> + SubAssign<Self> +
Mul<Self, Output = Self> + MulAssign<Self> +
Div<Self, Output = Self> + DivAssign<Self> +
Rem<Self, Output = Self> + RemAssign<Self> +
Shl<u32, Output = Self> + Shr<u32, Output = Self> +
CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv + Saturating +
PartialOrd<Self> + Ord + Bounded + HasCompact + Sized +
TryFrom<u8> + TryInto<u8> + TryFrom<u16> + TryInto<u16> + TryFrom<u32> + TryInto<u32> +
TryFrom<u64> + TryInto<u64> + TryFrom<u128> + TryInto<u128> + TryFrom<usize> + TryInto<usize> +
UniqueSaturatedFrom<u8> + UniqueSaturatedInto<u8> +
UniqueSaturatedFrom<u16> + UniqueSaturatedInto<u16> +
UniqueSaturatedFrom<u32> + UniqueSaturatedInto<u32> +
UniqueSaturatedFrom<u64> + UniqueSaturatedInto<u64> +
UniqueSaturatedFrom<u128> + UniqueSaturatedInto<u128>
{}
impl<T:
From<u8> +
Zero + One + IntegerSquareRoot +
Add<Self, Output = Self> + AddAssign<Self> +
Sub<Self, Output = Self> + SubAssign<Self> +
Mul<Self, Output = Self> + MulAssign<Self> +
Div<Self, Output = Self> + DivAssign<Self> +
Rem<Self, Output = Self> + RemAssign<Self> +
Shl<u32, Output = Self> + Shr<u32, Output = Self> +
CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv + Saturating +
PartialOrd<Self> + Ord + Bounded + HasCompact + Sized +
TryFrom<u8> + TryInto<u8> + TryFrom<u16> + TryInto<u16> + TryFrom<u32> + TryInto<u32> +
TryFrom<u64> + TryInto<u64> + TryFrom<u128> + TryInto<u128> + TryFrom<usize> + TryInto<usize> +
UniqueSaturatedFrom<u8> + UniqueSaturatedInto<u8> +
UniqueSaturatedFrom<u16> + UniqueSaturatedInto<u16> +
UniqueSaturatedFrom<u32> + UniqueSaturatedInto<u32> +
UniqueSaturatedFrom<u64> + UniqueSaturatedInto<u64> +
UniqueSaturatedFrom<u128> + UniqueSaturatedInto<u128>
> BaseArithmetic for T {}
/// A meta trait for arithmetic.
///
/// Arithmetic types do all the usual stuff you'd expect numbers to do. They are guaranteed to
/// be able to represent at least `u32` values without loss, hence the trait implies `From<u32>`
/// and smaller ints. All other conversions are fallible.
pub trait SimpleArithmetic:
Zero + One + IntegerSquareRoot +
From<u8> + From<u16> + From<u32> + TryInto<u8> + TryInto<u16> + TryInto<u32> +
TryFrom<u64> + TryInto<u64> + TryFrom<u128> + TryInto<u128> + TryFrom<usize> + TryInto<usize> +
UniqueSaturatedInto<u8> + UniqueSaturatedInto<u16> + UniqueSaturatedInto<u32> +
UniqueSaturatedFrom<u64> + UniqueSaturatedInto<u64> + UniqueSaturatedFrom<u128> + UniqueSaturatedInto<u128> +
Add<Self, Output = Self> + AddAssign<Self> +
Sub<Self, Output = Self> + SubAssign<Self> +
Mul<Self, Output = Self> + MulAssign<Self> +
Div<Self, Output = Self> + DivAssign<Self> +
Rem<Self, Output = Self> + RemAssign<Self> +
Shl<u32, Output = Self> + Shr<u32, Output = Self> +
CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv +
Saturating + PartialOrd<Self> + Ord + Bounded +
HasCompact + Sized
{}
impl<T:
Zero + One + IntegerSquareRoot +
From<u8> + From<u16> + From<u32> + TryInto<u8> + TryInto<u16> + TryInto<u32> +
TryFrom<u64> + TryInto<u64> + TryFrom<u128> + TryInto<u128> + TryFrom<usize> + TryInto<usize> +
UniqueSaturatedInto<u8> + UniqueSaturatedInto<u16> + UniqueSaturatedInto<u32> +
UniqueSaturatedFrom<u64> + UniqueSaturatedInto<u64> + UniqueSaturatedFrom<u128> +
UniqueSaturatedInto<u128> + UniqueSaturatedFrom<usize> + UniqueSaturatedInto<usize> +
Add<Self, Output = Self> + AddAssign<Self> +
Sub<Self, Output = Self> + SubAssign<Self> +
Mul<Self, Output = Self> + MulAssign<Self> +
Div<Self, Output = Self> + DivAssign<Self> +
Rem<Self, Output = Self> + RemAssign<Self> +
Shl<u32, Output = Self> + Shr<u32, Output = Self> +
CheckedShl + CheckedShr + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv +
Saturating + PartialOrd<Self> + Ord + Bounded +
HasCompact + Sized
> SimpleArithmetic for T {}
/// and smaller integers. All other conversions are fallible.
pub trait AtLeast32Bit: BaseArithmetic + From<u16> + From<u32> {}
impl<T: BaseArithmetic + From<u16> + From<u32>> AtLeast32Bit for T {}
/// Just like `From` except that if the source value is too big to fit into the destination type
/// then it'll saturate the destination.
+46 -35
View File
@@ -33,10 +33,14 @@
#![cfg_attr(not(feature = "std"), no_std)]
use sp_std::{prelude::*, collections::btree_map::BTreeMap};
use sp_runtime::RuntimeDebug;
use sp_runtime::{helpers_128bit::multiply_by_rational, Perbill, Rational128};
use sp_runtime::traits::{Zero, Convert, Member, SimpleArithmetic, Saturating, Bounded};
use sp_std::{prelude::*, collections::btree_map::BTreeMap, convert::TryFrom};
use sp_runtime::{
PerThing, Rational128, RuntimeDebug,
helpers_128bit::multiply_by_rational,
};
use sp_runtime::traits::{
Zero, Convert, Member, AtLeast32Bit, SaturatedConversion, Bounded, Saturating,
};
#[cfg(test)]
mod mock;
@@ -93,21 +97,21 @@ pub struct Edge<AccountId> {
candidate_index: usize,
}
/// Means a particular `AccountId` was backed by `Perbill`th of a nominator's stake.
pub type PhragmenAssignment<AccountId> = (AccountId, Perbill);
/// Particular `AccountId` was backed by `T`-ratio of a nominator's stake.
pub type PhragmenAssignment<AccountId, T> = (AccountId, T);
/// Means a particular `AccountId` was backed by `ExtendedBalance` of a nominator's stake.
/// Particular `AccountId` was backed by `ExtendedBalance` of a nominator's stake.
pub type PhragmenStakedAssignment<AccountId> = (AccountId, ExtendedBalance);
/// Final result of the phragmen election.
#[derive(RuntimeDebug)]
pub struct PhragmenResult<AccountId> {
pub struct PhragmenResult<AccountId, T: PerThing> {
/// Just winners zipped with their approval stake. Note that the approval stake is merely the
/// sub of their received stake and could be used for very basic sorting and approval voting.
pub winners: Vec<(AccountId, ExtendedBalance)>,
/// Individual assignments. for each tuple, the first elements is a voter and the second
/// is the list of candidates that it supports.
pub assignments: Vec<(AccountId, Vec<PhragmenAssignment<AccountId>>)>
pub assignments: Vec<(AccountId, Vec<PhragmenAssignment<AccountId, T>>)>
}
/// A structure to demonstrate the phragmen result from the perspective of the candidate, i.e. how
@@ -145,23 +149,24 @@ pub type SupportMap<A> = BTreeMap<A, Support<A>>;
/// responsibility of the caller to make sure only those candidates who have a sensible economic
/// value are passed in. From the perspective of this function, a candidate can easily be among the
/// winner with no backing stake.
pub fn elect<AccountId, Balance, FS, C>(
pub fn elect<AccountId, Balance, FS, C, R>(
candidate_count: usize,
minimum_candidate_count: usize,
initial_candidates: Vec<AccountId>,
initial_voters: Vec<(AccountId, Vec<AccountId>)>,
stake_of: FS,
) -> Option<PhragmenResult<AccountId>> where
) -> Option<PhragmenResult<AccountId, R>> where
AccountId: Default + Ord + Member,
Balance: Default + Copy + SimpleArithmetic,
Balance: Default + Copy + AtLeast32Bit,
for<'r> FS: Fn(&'r AccountId) -> Balance,
C: Convert<Balance, u64> + Convert<u128, Balance>,
R: PerThing,
{
let to_votes = |b: Balance| <C as Convert<Balance, u64>>::convert(b) as ExtendedBalance;
// return structures
let mut elected_candidates: Vec<(AccountId, ExtendedBalance)>;
let mut assigned: Vec<(AccountId, Vec<PhragmenAssignment<AccountId>>)>;
let mut assigned: Vec<(AccountId, Vec<PhragmenAssignment<AccountId, R>>)>;
// used to cache and access candidates index.
let mut c_idx_cache = BTreeMap::<AccountId, usize>::new();
@@ -272,20 +277,29 @@ pub fn elect<AccountId, Balance, FS, C>(
let mut assignment = (n.who.clone(), vec![]);
for e in &mut n.edges {
if elected_candidates.iter().position(|(ref c, _)| *c == e.who).is_some() {
let per_bill_parts =
let per_bill_parts: R::Inner =
{
if n.load == e.load {
// Full support. No need to calculate.
Perbill::accuracy().into()
R::ACCURACY
} else {
if e.load.d() == n.load.d() {
// return e.load / n.load.
let desired_scale: u128 = Perbill::accuracy().into();
multiply_by_rational(
let desired_scale: u128 = R::ACCURACY.saturated_into();
let parts = multiply_by_rational(
desired_scale,
e.load.n(),
n.load.n(),
).unwrap_or(Bounded::max_value())
)
// If result cannot fit in u128. Not much we can do about it.
.unwrap_or(Bounded::max_value());
TryFrom::try_from(parts)
// If the result cannot fit into R::Inner. Defensive only. This can
// never happen. `desired_scale * e / n`, where `e / n < 1` always
// yields a value smaller than `desired_scale`, which will fit into
// R::Inner.
.unwrap_or(Bounded::max_value())
} else {
// defensive only. Both edge and nominator loads are built from
// scores, hence MUST have the same denominator.
@@ -293,10 +307,7 @@ pub fn elect<AccountId, Balance, FS, C>(
}
}
};
// safer to .min() inside as well to argue as u32 is safe.
let per_thing = Perbill::from_parts(
per_bill_parts.min(Perbill::accuracy().into()) as u32
);
let per_thing = R::from_parts(per_bill_parts);
assignment.1.push((e.who.clone(), per_thing));
}
}
@@ -304,20 +315,19 @@ pub fn elect<AccountId, Balance, FS, C>(
if assignment.1.len() > 0 {
// To ensure an assertion indicating: no stake from the nominator going to waste,
// we add a minimal post-processing to equally assign all of the leftover stake ratios.
let vote_count = assignment.1.len() as u32;
let vote_count: R::Inner = assignment.1.len().saturated_into();
let len = assignment.1.len();
let sum = assignment.1.iter()
.map(|a| a.1.deconstruct())
.sum::<u32>();
let accuracy = Perbill::accuracy();
let diff = accuracy.checked_sub(sum).unwrap_or(0);
let mut sum: R::Inner = Zero::zero();
assignment.1.iter().for_each(|a| sum = sum.saturating_add(a.1.deconstruct()));
let accuracy = R::ACCURACY;
let diff = accuracy.saturating_sub(sum);
let diff_per_vote = (diff / vote_count).min(accuracy);
if diff_per_vote > 0 {
if !diff_per_vote.is_zero() {
for i in 0..len {
let current_ratio = assignment.1[i % len].1;
let next_ratio = current_ratio
.saturating_add(Perbill::from_parts(diff_per_vote));
.saturating_add(R::from_parts(diff_per_vote));
assignment.1[i % len].1 = next_ratio;
}
}
@@ -325,9 +335,9 @@ pub fn elect<AccountId, Balance, FS, C>(
// `remainder` is set to be less than maximum votes of a nominator (currently 16).
// safe to cast it to usize.
let remainder = diff - diff_per_vote * vote_count;
for i in 0..remainder as usize {
for i in 0..remainder.saturated_into::<usize>() {
let current_ratio = assignment.1[i % len].1;
let next_ratio = current_ratio.saturating_add(Perbill::from_parts(1));
let next_ratio = current_ratio.saturating_add(R::from_parts(1u8.into()));
assignment.1[i % len].1 = next_ratio;
}
assigned.push(assignment);
@@ -341,15 +351,16 @@ pub fn elect<AccountId, Balance, FS, C>(
}
/// Build the support map from the given phragmen result.
pub fn build_support_map<Balance, AccountId, FS, C>(
pub fn build_support_map<Balance, AccountId, FS, C, R>(
elected_stashes: &Vec<AccountId>,
assignments: &Vec<(AccountId, Vec<PhragmenAssignment<AccountId>>)>,
assignments: &Vec<(AccountId, Vec<PhragmenAssignment<AccountId, R>>)>,
stake_of: FS,
) -> SupportMap<AccountId> where
AccountId: Default + Ord + Member,
Balance: Default + Copy + SimpleArithmetic,
Balance: Default + Copy + AtLeast32Bit,
C: Convert<Balance, u64> + Convert<u128, Balance>,
for<'r> FS: Fn(&'r AccountId) -> Balance,
R: PerThing + sp_std::ops::Mul<ExtendedBalance, Output=ExtendedBalance>,
{
let to_votes = |b: Balance| <C as Convert<Balance, u64>>::convert(b) as ExtendedBalance;
// Initialize the support of each candidate.
+4 -4
View File
@@ -20,7 +20,7 @@
use crate::{elect, PhragmenResult, PhragmenAssignment};
use sp_runtime::{
assert_eq_error_rate, Perbill,
assert_eq_error_rate, Perbill, PerThing,
traits::{Convert, Member, SaturatedConversion}
};
use sp_std::collections::btree_map::BTreeMap;
@@ -320,10 +320,10 @@ pub(crate) fn create_stake_of(stakes: &[(AccountId, Balance)])
}
pub fn check_assignments(assignments: Vec<(AccountId, Vec<PhragmenAssignment<AccountId>>)>) {
pub fn check_assignments(assignments: Vec<(AccountId, Vec<PhragmenAssignment<AccountId, Perbill>>)>) {
for (_, a) in assignments {
let sum: u32 = a.iter().map(|(_, p)| p.deconstruct()).sum();
assert_eq_error_rate!(sum, Perbill::accuracy(), 5);
assert_eq_error_rate!(sum, Perbill::ACCURACY, 5);
}
}
@@ -335,7 +335,7 @@ pub(crate) fn run_and_compare(
min_to_elect: usize,
) {
// run fixed point code.
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>(
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Perbill>(
to_elect,
min_to_elect,
candidates.clone(),
+14 -11
View File
@@ -23,6 +23,8 @@ use crate::{elect, PhragmenResult, PhragmenStakedAssignment, build_support_map,
use substrate_test_utils::assert_eq_uvec;
use sp_runtime::Perbill;
type Output = Perbill;
#[test]
fn float_phragmen_poc_works() {
let candidates = vec![1, 2, 3];
@@ -78,7 +80,7 @@ fn phragmen_poc_works() {
(30, vec![2, 3]),
];
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>(
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>(
2,
2,
candidates,
@@ -147,7 +149,7 @@ fn phragmen_accuracy_on_large_scale_only_validators() {
(5, (u64::max_value() - 2).into()),
]);
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>(
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>(
2,
2,
candidates.clone(),
@@ -178,7 +180,7 @@ fn phragmen_accuracy_on_large_scale_validators_and_nominators() {
(14, u64::max_value().into()),
]);
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>(
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>(
2,
2,
candidates,
@@ -210,7 +212,7 @@ fn phragmen_accuracy_on_small_scale_self_vote() {
(30, 1),
]);
let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote>(
let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Output>(
3,
3,
candidates,
@@ -241,7 +243,7 @@ fn phragmen_accuracy_on_small_scale_no_self_vote() {
(3, 1),
]);
let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote>(
let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Output>(
3,
3,
candidates,
@@ -275,7 +277,7 @@ fn phragmen_large_scale_test() {
(50, 990000000000000000),
]);
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>(
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>(
2,
2,
candidates,
@@ -302,7 +304,7 @@ fn phragmen_large_scale_test_2() {
(50, nom_budget.into()),
]);
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote>(
let PhragmenResult { winners, assignments } = elect::<_, _, _, TestCurrencyToVote, Output>(
2,
2,
candidates,
@@ -367,7 +369,7 @@ fn elect_has_no_entry_barrier() {
(2, 10),
]);
let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote>(
let PhragmenResult { winners, assignments: _ } = elect::<_, _, _, TestCurrencyToVote, Output>(
3,
3,
candidates,
@@ -395,7 +397,7 @@ fn minimum_to_elect_is_respected() {
(2, 10),
]);
let maybe_result = elect::<_, _, _, TestCurrencyToVote>(
let maybe_result = elect::<_, _, _, TestCurrencyToVote, Output>(
10,
10,
candidates,
@@ -422,7 +424,7 @@ fn self_votes_should_be_kept() {
(1, 8),
]);
let result = elect::<_, _, _, TestCurrencyToVote>(
let result = elect::<_, _, _, TestCurrencyToVote, Output>(
2,
2,
candidates,
@@ -448,7 +450,8 @@ fn self_votes_should_be_kept() {
Balance,
AccountId,
_,
TestCurrencyToVote
TestCurrencyToVote,
Output,
>(
&result.winners.into_iter().map(|(who, _)| who).collect(),
&result.assignments,
+3 -3
View File
@@ -16,7 +16,7 @@
//! Provides some utilities to define a piecewise linear function.
use crate::{Perbill, traits::{SimpleArithmetic, SaturatedConversion}};
use crate::{Perbill, PerThing, traits::{AtLeast32Bit, SaturatedConversion}};
use core::ops::Sub;
/// Piecewise Linear function in [0, 1] -> [0, 1].
@@ -35,7 +35,7 @@ fn abs_sub<N: Ord + Sub<Output=N> + Clone>(a: N, b: N) -> N where {
impl<'a> PiecewiseLinear<'a> {
/// Compute `f(n/d)*d` with `n <= d`. This is useful to avoid loss of precision.
pub fn calculate_for_fraction_times_denominator<N>(&self, n: N, d: N) -> N where
N: SimpleArithmetic + Clone
N: AtLeast32Bit + Clone
{
let n = n.min(d.clone());
@@ -79,7 +79,7 @@ impl<'a> PiecewiseLinear<'a> {
// This is guaranteed not to overflow on whatever values nor lose precision.
// `q` must be superior to zero.
fn multiply_by_rational_saturating<N>(value: N, p: u32, q: u32) -> N
where N: SimpleArithmetic + Clone
where N: AtLeast32Bit + Clone
{
let q = q.max(1);
@@ -20,7 +20,7 @@
use serde::{Deserialize, Serialize};
use crate::codec::{Decode, Encode, Codec, Input, Output, HasCompact, EncodeAsRef, Error};
use crate::traits::{
self, Member, SimpleArithmetic, SimpleBitOps, Hash as HashT,
self, Member, AtLeast32Bit, SimpleBitOps, Hash as HashT,
MaybeSerializeDeserialize, MaybeSerialize, MaybeDisplay,
MaybeMallocSizeOf,
};
@@ -122,7 +122,7 @@ impl<Number, Hash> codec::EncodeLike for Header<Number, Hash> where
impl<Number, Hash> traits::Header for Header<Number, Hash> where
Number: Member + MaybeSerializeDeserialize + Debug + sp_std::hash::Hash + MaybeDisplay +
SimpleArithmetic + Codec + Copy + Into<U256> + TryFrom<U256> + sp_std::str::FromStr +
AtLeast32Bit + Codec + Copy + Into<U256> + TryFrom<U256> + sp_std::str::FromStr +
MaybeMallocSizeOf,
Hash: HashT,
Hash::Output: Default + sp_std::hash::Hash + Copy + Member + Ord +
@@ -170,7 +170,7 @@ impl<Number, Hash> traits::Header for Header<Number, Hash> where
}
impl<Number, Hash> Header<Number, Hash> where
Number: Member + sp_std::hash::Hash + Copy + MaybeDisplay + SimpleArithmetic + Codec + Into<U256> + TryFrom<U256>,
Number: Member + sp_std::hash::Hash + Copy + MaybeDisplay + AtLeast32Bit + Codec + Into<U256> + TryFrom<U256>,
Hash: HashT,
Hash::Output: Default + sp_std::hash::Hash + Copy + Member + MaybeDisplay + SimpleBitOps + Codec,
{
+1 -1
View File
@@ -68,7 +68,7 @@ pub use sp_application_crypto::{RuntimeAppPublic, BoundToRuntimeAppPublic};
pub use sp_core::RuntimeDebug;
/// Re-export top-level arithmetic stuff.
pub use sp_arithmetic::{Perquintill, Perbill, Permill, Percent, Rational128, Fixed64};
pub use sp_arithmetic::{Perquintill, Perbill, Permill, Percent, Rational128, Fixed64, PerThing};
/// Re-export 128 bit helpers.
pub use sp_arithmetic::helpers_128bit;
/// Re-export big_uint stuff.
+2 -2
View File
@@ -33,7 +33,7 @@ use crate::transaction_validity::{
};
use crate::generic::{Digest, DigestItem};
pub use sp_arithmetic::traits::{
SimpleArithmetic, UniqueSaturatedInto, UniqueSaturatedFrom, Saturating, SaturatedConversion,
AtLeast32Bit, UniqueSaturatedInto, UniqueSaturatedFrom, Saturating, SaturatedConversion,
Zero, One, Bounded, CheckedAdd, CheckedSub, CheckedMul, CheckedDiv,
CheckedShl, CheckedShr, IntegerSquareRoot
};
@@ -502,7 +502,7 @@ pub trait Header:
{
/// Header number.
type Number: Member + MaybeSerializeDeserialize + Debug + sp_std::hash::Hash
+ Copy + MaybeDisplay + SimpleArithmetic + Codec + sp_std::str::FromStr
+ Copy + MaybeDisplay + AtLeast32Bit + Codec + sp_std::str::FromStr
+ MaybeMallocSizeOf;
/// Header hash type
type Hash: Member + MaybeSerializeDeserialize + Debug + sp_std::hash::Hash + Ord