mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-28 07:27:55 +00:00
PhragMMS election. (#6685)
* Revamp npos-elections and implement phragmms * Update primitives/npos-elections/src/phragmms.rs * Fix build * Some review grumbles * Add some stuff for remote testing * fix some of the grumbles. * Add remote testing stuff. * Cleanup * fix docs * Update primitives/arithmetic/src/rational.rs Co-authored-by: Dan Forbes <dan@danforbes.dev> * Small config change * Better handling of approval_stake == 0 * Final touhces. * Clean fuzzer a bit * Clean fuzzer a bit * Update primitives/npos-elections/src/balancing.rs Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com> * Fix fuzzer. * Better api for normalize * Add noramlize_up * A large number of small fixes. * make it merge ready * Fix warns * bump * Fix fuzzers a bit. * Fix warns as well. * Fix more tests. Co-authored-by: Dan Forbes <dan@danforbes.dev> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
@@ -17,12 +17,13 @@
|
||||
|
||||
//! Infinite precision unsigned integer for substrate runtime.
|
||||
|
||||
use num_traits::Zero;
|
||||
use num_traits::{Zero, One};
|
||||
use sp_std::{cmp::Ordering, ops, prelude::*, vec, cell::RefCell, convert::TryFrom};
|
||||
|
||||
// A sensible value for this would be half of the dword size of the host machine. Since the
|
||||
// runtime is compiled to 32bit webassembly, using 32 and 64 for single and double respectively
|
||||
// should yield the most performance.
|
||||
|
||||
/// Representation of a single limb.
|
||||
pub type Single = u32;
|
||||
/// Representation of two limbs.
|
||||
@@ -75,7 +76,7 @@ fn div_single(a: Double, b: Single) -> (Double, Single) {
|
||||
/// Simple wrapper around an infinitely large integer, represented as limbs of [`Single`].
|
||||
#[derive(Clone, Default)]
|
||||
pub struct BigUint {
|
||||
/// digits (limbs) of this number (sorted as msb -> lsd).
|
||||
/// digits (limbs) of this number (sorted as msb -> lsb).
|
||||
pub(crate) digits: Vec<Single>,
|
||||
}
|
||||
|
||||
@@ -515,6 +516,12 @@ impl Zero for BigUint {
|
||||
}
|
||||
}
|
||||
|
||||
impl One for BigUint {
|
||||
fn one() -> Self {
|
||||
Self { digits: vec![Single::one()] }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_try_from_number_for {
|
||||
($([$type:ty, $len:expr]),+) => {
|
||||
$(
|
||||
@@ -550,15 +557,21 @@ macro_rules! impl_from_for_smaller_than_word {
|
||||
})*
|
||||
}
|
||||
}
|
||||
impl_from_for_smaller_than_word!(u8, u16, Single);
|
||||
impl_from_for_smaller_than_word!(u8, u16, u32);
|
||||
|
||||
impl From<Double> for BigUint {
|
||||
impl From<u64> for BigUint {
|
||||
fn from(a: Double) -> Self {
|
||||
let (ah, al) = split(a);
|
||||
Self { digits: vec![ah, al] }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u128> for BigUint {
|
||||
fn from(a: u128) -> Self {
|
||||
crate::helpers_128bit::to_big_uint(a)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -36,13 +36,13 @@ macro_rules! assert_eq_error_rate {
|
||||
pub mod biguint;
|
||||
pub mod helpers_128bit;
|
||||
pub mod traits;
|
||||
mod per_things;
|
||||
mod fixed_point;
|
||||
mod rational128;
|
||||
pub mod per_things;
|
||||
pub mod fixed_point;
|
||||
pub mod rational;
|
||||
|
||||
pub use fixed_point::{FixedPointNumber, FixedPointOperand, FixedI64, FixedI128, FixedU128};
|
||||
pub use per_things::{PerThing, InnerOf, UpperOf, Percent, PerU16, Permill, Perbill, Perquintill};
|
||||
pub use rational128::Rational128;
|
||||
pub use rational::{Rational128, RationalInfinite};
|
||||
|
||||
use sp_std::{prelude::*, cmp::Ordering, fmt::Debug, convert::TryInto};
|
||||
use traits::{BaseArithmetic, One, Zero, SaturatedConversion, Unsigned};
|
||||
@@ -114,13 +114,22 @@ impl_normalize_for_numeric!(u8, u16, u32, u64, u128);
|
||||
|
||||
impl<P: PerThing> Normalizable<P> for Vec<P> {
|
||||
fn normalize(&self, targeted_sum: P) -> Result<Vec<P>, &'static str> {
|
||||
let inners = self.iter().map(|p| p.clone().deconstruct().into()).collect::<Vec<_>>();
|
||||
let inners = self
|
||||
.iter()
|
||||
.map(|p| p.clone().deconstruct().into())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let normalized = normalize(inners.as_ref(), targeted_sum.deconstruct().into())?;
|
||||
Ok(normalized.into_iter().map(|i: UpperOf<P>| P::from_parts(i.saturated_into())).collect())
|
||||
|
||||
Ok(
|
||||
normalized
|
||||
.into_iter()
|
||||
.map(|i: UpperOf<P>| P::from_parts(i.saturated_into()))
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Normalize `input` so that the sum of all elements reaches `targeted_sum`.
|
||||
///
|
||||
/// This implementation is currently in a balanced position between being performant and accurate.
|
||||
@@ -143,8 +152,8 @@ impl<P: PerThing> Normalizable<P> for Vec<P> {
|
||||
/// `leftover` value. This ensures that the result will always stay accurate, yet it might cause the
|
||||
/// execution to become increasingly slow, since leftovers are applied one by one.
|
||||
///
|
||||
/// All in all, the complicated case above is rare to happen in all substrate use cases, hence we
|
||||
/// opt for it due to its simplicity.
|
||||
/// All in all, the complicated case above is rare to happen in most use cases within this repo ,
|
||||
/// hence we opt for it due to its simplicity.
|
||||
///
|
||||
/// This function will return an error is if length of `input` cannot fit in `T`, or if `sum(input)`
|
||||
/// cannot fit inside `T`.
|
||||
|
||||
+107
-4
@@ -17,19 +17,106 @@
|
||||
|
||||
use sp_std::{cmp::Ordering, prelude::*};
|
||||
use crate::helpers_128bit;
|
||||
use num_traits::Zero;
|
||||
use sp_debug_derive::RuntimeDebug;
|
||||
use num_traits::{Zero, One, Bounded};
|
||||
use crate::biguint::BigUint;
|
||||
|
||||
/// A wrapper for any rational number with infinitely large numerator and denominator.
|
||||
///
|
||||
/// This type exists to facilitate `cmp` operation
|
||||
/// on values like `a/b < c/d` where `a, b, c, d` are all `BigUint`.
|
||||
#[derive(Clone, Default, Eq)]
|
||||
pub struct RationalInfinite(BigUint, BigUint);
|
||||
|
||||
impl RationalInfinite {
|
||||
/// Return the numerator reference.
|
||||
pub fn n(&self) -> &BigUint {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Return the denominator reference.
|
||||
pub fn d(&self) -> &BigUint {
|
||||
&self.1
|
||||
}
|
||||
|
||||
/// Build from a raw `n/d`.
|
||||
pub fn from(n: BigUint, d: BigUint) -> Self {
|
||||
Self(n, d.max(BigUint::one()))
|
||||
}
|
||||
|
||||
/// Zero.
|
||||
pub fn zero() -> Self {
|
||||
Self(BigUint::zero(), BigUint::one())
|
||||
}
|
||||
|
||||
/// One.
|
||||
pub fn one() -> Self {
|
||||
Self(BigUint::one(), BigUint::one())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for RationalInfinite {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for RationalInfinite {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
// handle some edge cases.
|
||||
if self.d() == other.d() {
|
||||
self.n().cmp(&other.n())
|
||||
} else if self.d().is_zero() {
|
||||
Ordering::Greater
|
||||
} else if other.d().is_zero() {
|
||||
Ordering::Less
|
||||
} else {
|
||||
// (a/b) cmp (c/d) => (a*d) cmp (c*b)
|
||||
self.n().clone().mul(&other.d()).cmp(&other.n().clone().mul(&self.d()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for RationalInfinite {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cmp(other) == Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Rational128> for RationalInfinite {
|
||||
fn from(t: Rational128) -> Self {
|
||||
Self(t.0.into(), t.1.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper for any rational number with a 128 bit numerator and denominator.
|
||||
#[derive(Clone, Copy, Default, Eq, RuntimeDebug)]
|
||||
#[derive(Clone, Copy, Default, Eq)]
|
||||
pub struct Rational128(u128, u128);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl sp_std::fmt::Debug for Rational128 {
|
||||
fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result {
|
||||
write!(f, "Rational128({:.4})", self.0 as f32 / self.1 as f32)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl sp_std::fmt::Debug for Rational128 {
|
||||
fn fmt(&self, f: &mut sp_std::fmt::Formatter<'_>) -> sp_std::fmt::Result {
|
||||
write!(f, "Rational128(..)")
|
||||
}
|
||||
}
|
||||
|
||||
impl Rational128 {
|
||||
/// Nothing.
|
||||
/// Zero.
|
||||
pub fn zero() -> Self {
|
||||
Self(0, 1)
|
||||
}
|
||||
|
||||
/// One
|
||||
pub fn one() -> Self {
|
||||
Self(1, 1)
|
||||
}
|
||||
|
||||
/// If it is zero or not
|
||||
pub fn is_zero(&self) -> bool {
|
||||
self.0.is_zero()
|
||||
@@ -122,6 +209,22 @@ impl Rational128 {
|
||||
}
|
||||
}
|
||||
|
||||
impl Bounded for Rational128 {
|
||||
fn min_value() -> Self {
|
||||
Self(0, 1)
|
||||
}
|
||||
|
||||
fn max_value() -> Self {
|
||||
Self(Bounded::max_value(), 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<u128>> From<T> for Rational128 {
|
||||
fn from(t: T) -> Self {
|
||||
Self::from(t.into(), 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Rational128 {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
Reference in New Issue
Block a user