diff --git a/primitives/arithmetic/src/traits.rs b/primitives/arithmetic/src/traits.rs index 7fa64d28669d4..9e9dff8e6f9ea 100644 --- a/primitives/arithmetic/src/traits.rs +++ b/primitives/arithmetic/src/traits.rs @@ -57,6 +57,7 @@ pub trait BaseArithmetic: + CheckedMul + CheckedDiv + CheckedRem + + Ensure + Saturating + PartialOrd + Ord @@ -113,6 +114,7 @@ impl< + CheckedMul + CheckedDiv + CheckedRem + + Ensure + Saturating + PartialOrd + Ord @@ -344,13 +346,24 @@ mod ensure { use crate::{ArithmeticError, FixedPointNumber, FixedPointOperand}; /// Performs addition that returns [`ArithmeticError`] instead of wrapping around on overflow. - pub trait EnsureAdd: CheckedAdd + PartialOrd + Zero + Copy { + pub trait EnsureAdd: EnsureAddAssign { /// Adds two numbers, checking for overflow. /// /// If it fails, [`ArithmeticError`] is returned. /// /// Similar to [`CheckedAdd::checked_add()`] but returning an [`ArithmeticError`] error. /// + /// # Examples + /// + /// ``` + /// use sp_arithmetic::traits::EnsureAdd; + /// + /// let a: i32 = 10; + /// let b: i32 = 20; + /// + /// assert_eq!(a.ensure_add(b), Ok(30)); + /// ``` + /// /// ``` /// use sp_arithmetic::{traits::EnsureAdd, ArithmeticError}; /// @@ -367,20 +380,32 @@ mod ensure { /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); /// ``` - fn ensure_add(self, v: Self) -> Result { - self.checked_add(&v).ok_or_else(|| error::equivalent(v)) + fn ensure_add(mut self, v: Self) -> Result { + self.ensure_add_assign(v)?; + Ok(self) } } /// Performs subtraction that returns [`ArithmeticError`] instead of wrapping around on /// underflow. - pub trait EnsureSub: CheckedSub + PartialOrd + Zero + Copy { + pub trait EnsureSub: EnsureSubAssign { /// Subtracts two numbers, checking for overflow. /// /// If it fails, [`ArithmeticError`] is returned. /// /// Similar to [`CheckedSub::checked_sub()`] but returning an [`ArithmeticError`] error. /// + /// # Examples + /// + /// ``` + /// use sp_arithmetic::traits::EnsureSub; + /// + /// let a: i32 = 10; + /// let b: i32 = 20; + /// + /// assert_eq!(a.ensure_sub(b), Ok(-10)); + /// ``` + /// /// ``` /// use sp_arithmetic::{traits::EnsureSub, ArithmeticError}; /// @@ -397,20 +422,32 @@ mod ensure { /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); /// ``` - fn ensure_sub(self, v: Self) -> Result { - self.checked_sub(&v).ok_or_else(|| error::inverse(v)) + fn ensure_sub(mut self, v: Self) -> Result { + self.ensure_sub_assign(v)?; + Ok(self) } } /// Performs multiplication that returns [`ArithmeticError`] instead of wrapping around on /// overflow. - pub trait EnsureMul: CheckedMul + PartialOrd + Zero + Copy { + pub trait EnsureMul: EnsureMulAssign { /// Multiplies two numbers, checking for overflow. /// /// If it fails, [`ArithmeticError`] is returned. /// /// Similar to [`CheckedMul::checked_mul()`] but returning an [`ArithmeticError`] error. /// + /// # Examples + /// + /// ``` + /// use sp_arithmetic::traits::EnsureMul; + /// + /// let a: i32 = 10; + /// let b: i32 = 20; + /// + /// assert_eq!(a.ensure_mul(b), Ok(200)); + /// ``` + /// /// ``` /// use sp_arithmetic::{traits::EnsureMul, ArithmeticError}; /// @@ -427,19 +464,31 @@ mod ensure { /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); /// ``` - fn ensure_mul(self, v: Self) -> Result { - self.checked_mul(&v).ok_or_else(|| error::multiplication(self, v)) + fn ensure_mul(mut self, v: Self) -> Result { + self.ensure_mul_assign(v)?; + Ok(self) } } /// Performs division that returns [`ArithmeticError`] instead of wrapping around on overflow. - pub trait EnsureDiv: CheckedDiv + PartialOrd + Zero + Copy { + pub trait EnsureDiv: EnsureDivAssign { /// Divides two numbers, checking for overflow. /// /// If it fails, [`ArithmeticError`] is returned. /// /// Similar to [`CheckedDiv::checked_div()`] but returning an [`ArithmeticError`] error. /// + /// # Examples + /// + /// ``` + /// use sp_arithmetic::traits::EnsureDiv; + /// + /// let a: i32 = 20; + /// let b: i32 = 10; + /// + /// assert_eq!(a.ensure_div(b), Ok(2)); + /// ``` + /// /// ``` /// use sp_arithmetic::{traits::EnsureDiv, ArithmeticError}; /// @@ -456,15 +505,16 @@ mod ensure { /// assert_eq!(extrinsic_zero(), Err(ArithmeticError::DivisionByZero)); /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); /// ``` - fn ensure_div(self, v: Self) -> Result { - self.checked_div(&v).ok_or_else(|| error::division(self, v)) + fn ensure_div(mut self, v: Self) -> Result { + self.ensure_div_assign(v)?; + Ok(self) } } - impl EnsureAdd for T {} - impl EnsureSub for T {} - impl EnsureMul for T {} - impl EnsureDiv for T {} + impl EnsureAdd for T {} + impl EnsureSub for T {} + impl EnsureMul for T {} + impl EnsureDiv for T {} /// Meta trait that supports all immutable arithmetic `Ensure*` operations pub trait EnsureOp: EnsureAdd + EnsureSub + EnsureMul + EnsureDiv {} @@ -472,11 +522,23 @@ mod ensure { /// Performs self addition that returns [`ArithmeticError`] instead of wrapping around on /// overflow. - pub trait EnsureAddAssign: EnsureAdd { + pub trait EnsureAddAssign: CheckedAdd + PartialOrd + Zero { /// Adds two numbers overwriting the left hand one, checking for overflow. /// /// If it fails, [`ArithmeticError`] is returned. /// + /// # Examples + /// + /// ``` + /// use sp_arithmetic::traits::EnsureAddAssign; + /// + /// let mut a: i32 = 10; + /// let b: i32 = 20; + /// + /// a.ensure_add_assign(b).unwrap(); + /// assert_eq!(a, 30); + /// ``` + /// /// ``` /// use sp_arithmetic::{traits::EnsureAddAssign, ArithmeticError}; /// @@ -496,18 +558,30 @@ mod ensure { /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); /// ``` fn ensure_add_assign(&mut self, v: Self) -> Result<(), ArithmeticError> { - *self = self.ensure_add(v)?; + *self = self.checked_add(&v).ok_or_else(|| error::equivalent(&v))?; Ok(()) } } /// Performs self subtraction that returns [`ArithmeticError`] instead of wrapping around on /// underflow. - pub trait EnsureSubAssign: EnsureSub { + pub trait EnsureSubAssign: CheckedSub + PartialOrd + Zero { /// Subtracts two numbers overwriting the left hand one, checking for overflow. /// /// If it fails, [`ArithmeticError`] is returned. /// + /// # Examples + /// + /// ``` + /// use sp_arithmetic::traits::EnsureSubAssign; + /// + /// let mut a: i32 = 10; + /// let b: i32 = 20; + /// + /// a.ensure_sub_assign(b).unwrap(); + /// assert_eq!(a, -10); + /// ``` + /// /// ``` /// use sp_arithmetic::{traits::EnsureSubAssign, ArithmeticError}; /// @@ -527,18 +601,30 @@ mod ensure { /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); /// ``` fn ensure_sub_assign(&mut self, v: Self) -> Result<(), ArithmeticError> { - *self = self.ensure_sub(v)?; + *self = self.checked_sub(&v).ok_or_else(|| error::inverse(&v))?; Ok(()) } } /// Performs self multiplication that returns [`ArithmeticError`] instead of wrapping around on /// overflow. - pub trait EnsureMulAssign: EnsureMul { + pub trait EnsureMulAssign: CheckedMul + PartialOrd + Zero { /// Multiplies two numbers overwriting the left hand one, checking for overflow. /// /// If it fails, [`ArithmeticError`] is returned. /// + /// # Examples + /// + /// ``` + /// use sp_arithmetic::traits::EnsureMulAssign; + /// + /// let mut a: i32 = 10; + /// let b: i32 = 20; + /// + /// a.ensure_mul_assign(b).unwrap(); + /// assert_eq!(a, 200); + /// ``` + /// /// ``` /// use sp_arithmetic::{traits::EnsureMulAssign, ArithmeticError}; /// @@ -558,18 +644,30 @@ mod ensure { /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); /// ``` fn ensure_mul_assign(&mut self, v: Self) -> Result<(), ArithmeticError> { - *self = self.ensure_mul(v)?; + *self = self.checked_mul(&v).ok_or_else(|| error::multiplication(self, &v))?; Ok(()) } } /// Performs self division that returns [`ArithmeticError`] instead of wrapping around on /// overflow. - pub trait EnsureDivAssign: EnsureDiv { + pub trait EnsureDivAssign: CheckedDiv + PartialOrd + Zero { /// Divides two numbers overwriting the left hand one, checking for overflow. /// /// If it fails, [`ArithmeticError`] is returned. /// + /// # Examples + /// + /// ``` + /// use sp_arithmetic::traits::EnsureDivAssign; + /// + /// let mut a: i32 = 20; + /// let b: i32 = 10; + /// + /// a.ensure_div_assign(b).unwrap(); + /// assert_eq!(a, 2); + /// ``` + /// /// ``` /// use sp_arithmetic::{traits::EnsureDivAssign, ArithmeticError, FixedI64}; /// @@ -589,15 +687,15 @@ mod ensure { /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); /// ``` fn ensure_div_assign(&mut self, v: Self) -> Result<(), ArithmeticError> { - *self = self.ensure_div(v)?; + *self = self.checked_div(&v).ok_or_else(|| error::division(self, &v))?; Ok(()) } } - impl EnsureAddAssign for T {} - impl EnsureSubAssign for T {} - impl EnsureMulAssign for T {} - impl EnsureDivAssign for T {} + impl EnsureAddAssign for T {} + impl EnsureSubAssign for T {} + impl EnsureMulAssign for T {} + impl EnsureDivAssign for T {} /// Meta trait that supports all assigned arithmetic `Ensure*` operations pub trait EnsureOpAssign: @@ -609,7 +707,6 @@ mod ensure { { } - /// Meta trait that supports all arithmetic operations pub trait Ensure: EnsureOp + EnsureOpAssign {} impl Ensure for T {} @@ -643,7 +740,7 @@ mod ensure { d: D, ) -> Result { ::checked_from_rational(n, d) - .ok_or_else(|| error::division(n, d)) + .ok_or_else(|| error::division(&n, &d)) } /// Ensure multiplication for integer type `N`. Equal to `self * n`. @@ -670,7 +767,7 @@ mod ensure { /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); /// ``` fn ensure_mul_int(self, n: N) -> Result { - self.checked_mul_int(n).ok_or_else(|| error::multiplication(self, n)) + self.checked_mul_int(n).ok_or_else(|| error::multiplication(&self, &n)) } /// Ensure division for integer type `N`. Equal to `self / d`. @@ -697,16 +794,14 @@ mod ensure { /// assert_eq!(overflow(), Err(ArithmeticError::Overflow)); /// ``` fn ensure_div_int(self, d: D) -> Result { - self.checked_div_int(d).ok_or_else(|| error::division(self, d)) + self.checked_div_int(d).ok_or_else(|| error::division(&self, &d)) } } impl EnsureFixedPointNumber for T {} /// Similar to [`TryFrom`] but returning an [`ArithmeticError`] error. - pub trait EnsureFrom: - TryFrom + PartialOrd + Zero + Copy - { + pub trait EnsureFrom: TryFrom + PartialOrd + Zero { /// Performs the conversion returning an [`ArithmeticError`] if fails. /// /// Similar to [`TryFrom::try_from()`] but returning an [`ArithmeticError`] error. @@ -728,14 +823,13 @@ mod ensure { /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); /// ``` fn ensure_from(other: T) -> Result { - Self::try_from(other).map_err(|_| error::equivalent(other)) + let err = error::equivalent(&other); + Self::try_from(other).map_err(|_| err) } } /// Similar to [`TryInto`] but returning an [`ArithmeticError`] error. - pub trait EnsureInto: - TryInto + PartialOrd + Zero + Copy - { + pub trait EnsureInto: TryInto + PartialOrd + Zero { /// Performs the conversion returning an [`ArithmeticError`] if fails. /// /// Similar to [`TryInto::try_into()`] but returning an [`ArithmeticError`] error @@ -757,12 +851,13 @@ mod ensure { /// assert_eq!(underflow(), Err(ArithmeticError::Underflow)); /// ``` fn ensure_into(self) -> Result { - self.try_into().map_err(|_| error::equivalent(self)) + let err = error::equivalent(&self); + self.try_into().map_err(|_| err) } } - impl + PartialOrd + Zero + Copy, S: PartialOrd + Zero + Copy> EnsureFrom for T {} - impl + PartialOrd + Zero + Copy, S: PartialOrd + Zero + Copy> EnsureInto for T {} + impl + PartialOrd + Zero, S: PartialOrd + Zero> EnsureFrom for T {} + impl + PartialOrd + Zero, S: PartialOrd + Zero> EnsureInto for T {} mod error { use super::{ArithmeticError, Zero}; @@ -773,9 +868,9 @@ mod ensure { Positive, } - impl From for Signum { - fn from(value: T) -> Self { - if value < Zero::zero() { + impl From<&T> for Signum { + fn from(value: &T) -> Self { + if value < &Zero::zero() { Signum::Negative } else { Signum::Positive @@ -795,23 +890,23 @@ mod ensure { } } - pub fn equivalent(r: R) -> ArithmeticError { + pub fn equivalent(r: &R) -> ArithmeticError { match Signum::from(r) { Signum::Negative => ArithmeticError::Underflow, Signum::Positive => ArithmeticError::Overflow, } } - pub fn inverse(r: R) -> ArithmeticError { + pub fn inverse(r: &R) -> ArithmeticError { match Signum::from(r) { Signum::Negative => ArithmeticError::Overflow, Signum::Positive => ArithmeticError::Underflow, } } - pub fn multiplication( - l: L, - r: R, + pub fn multiplication( + l: &L, + r: &R, ) -> ArithmeticError { match Signum::from(l) * Signum::from(r) { Signum::Negative => ArithmeticError::Underflow, @@ -819,9 +914,9 @@ mod ensure { } } - pub fn division( - n: N, - d: D, + pub fn division( + n: &N, + d: &D, ) -> ArithmeticError { if d.is_zero() { ArithmeticError::DivisionByZero @@ -831,3 +926,106 @@ mod ensure { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::ArithmeticError; + use rand::{seq::SliceRandom, thread_rng, Rng}; + + #[test] + fn ensure_add_works() { + test_ensure(values(), &EnsureAdd::ensure_add, &CheckedAdd::checked_add); + } + + #[test] + fn ensure_sub_works() { + test_ensure(values(), &EnsureSub::ensure_sub, &CheckedSub::checked_sub); + } + + #[test] + fn ensure_mul_works() { + test_ensure(values(), &EnsureMul::ensure_mul, &CheckedMul::checked_mul); + } + + #[test] + fn ensure_div_works() { + test_ensure(values(), &EnsureDiv::ensure_div, &CheckedDiv::checked_div); + } + + #[test] + fn ensure_add_assign_works() { + test_ensure_assign(values(), &EnsureAddAssign::ensure_add_assign, &EnsureAdd::ensure_add); + } + + #[test] + fn ensure_sub_assign_works() { + test_ensure_assign(values(), &EnsureSubAssign::ensure_sub_assign, &EnsureSub::ensure_sub); + } + + #[test] + fn ensure_mul_assign_works() { + test_ensure_assign(values(), &EnsureMulAssign::ensure_mul_assign, &&EnsureMul::ensure_mul); + } + + #[test] + fn ensure_div_assign_works() { + test_ensure_assign(values(), &EnsureDivAssign::ensure_div_assign, &EnsureDiv::ensure_div); + } + + /// Test that the ensured function returns the expected un-ensured value. + fn test_ensure(pairs: Vec<(V, V)>, ensured: E, unensured: P) + where + V: Ensure + core::fmt::Debug + Copy, + E: Fn(V, V) -> Result, + P: Fn(&V, &V) -> Option, + { + for (a, b) in pairs.into_iter() { + match ensured(a, b) { + Ok(c) => { + assert_eq!(unensured(&a, &b), Some(c)) + }, + Err(_) => { + assert!(unensured(&a, &b).is_none()); + }, + } + } + } + + /// Test that the ensured function modifies `self` to the expected un-ensured value. + fn test_ensure_assign(pairs: Vec<(V, V)>, ensured: E, unensured: P) + where + V: Ensure + std::panic::RefUnwindSafe + std::panic::UnwindSafe + core::fmt::Debug + Copy, + E: Fn(&mut V, V) -> Result<(), ArithmeticError>, + P: Fn(V, V) -> Result + std::panic::RefUnwindSafe, + { + for (mut a, b) in pairs.into_iter() { + let old_a = a; + + match ensured(&mut a, b) { + Ok(()) => { + assert_eq!(unensured(old_a, b), Ok(a)); + }, + Err(err) => { + assert_eq!(a, old_a, "A stays unmodified in the error case"); + assert_eq!(unensured(old_a, b), Err(err)); + }, + } + } + } + + /// Generates some good values for testing integer arithmetic. + fn values() -> Vec<(i32, i32)> { + let mut rng = thread_rng(); + let mut one_dimension = || { + let mut ret = vec![0i32; 1007]; + // Some hard-coded interesting values. + ret[..7].copy_from_slice(&[-1, 0, 1, i32::MIN, i32::MAX, i32::MAX - 1, i32::MIN + 1]); + // … and some random ones. + rng.fill(&mut ret[7..]); + ret.shuffle(&mut rng); + ret + }; + one_dimension().into_iter().zip(one_dimension().into_iter()).collect() + } +}