Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 56 additions & 53 deletions primitives/arithmetic/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub trait BaseArithmetic:
+ CheckedMul
+ CheckedDiv
+ CheckedRem
+ Ensure
+ Saturating
+ PartialOrd<Self>
+ Ord
Expand Down Expand Up @@ -113,6 +114,7 @@ impl<
+ CheckedMul
+ CheckedDiv
+ CheckedRem
+ Ensure
+ Saturating
+ PartialOrd<Self>
+ Ord
Expand Down Expand Up @@ -344,7 +346,7 @@ 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.
Expand All @@ -367,14 +369,15 @@ mod ensure {
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_add(self, v: Self) -> Result<Self, ArithmeticError> {
self.checked_add(&v).ok_or_else(|| error::equivalent(v))
fn ensure_add(mut self, v: Self) -> Result<Self, ArithmeticError> {
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.
Expand All @@ -397,14 +400,15 @@ mod ensure {
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// ```
fn ensure_sub(self, v: Self) -> Result<Self, ArithmeticError> {
self.checked_sub(&v).ok_or_else(|| error::inverse(v))
fn ensure_sub(mut self, v: Self) -> Result<Self, ArithmeticError> {
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.
Expand All @@ -427,13 +431,14 @@ mod ensure {
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_mul(self, v: Self) -> Result<Self, ArithmeticError> {
self.checked_mul(&v).ok_or_else(|| error::multiplication(self, v))
fn ensure_mul(mut self, v: Self) -> Result<Self, ArithmeticError> {
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.
Expand All @@ -456,23 +461,24 @@ mod ensure {
/// assert_eq!(extrinsic_zero(), Err(ArithmeticError::DivisionByZero));
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// ```
fn ensure_div(self, v: Self) -> Result<Self, ArithmeticError> {
self.checked_div(&v).ok_or_else(|| error::division(self, v))
fn ensure_div(mut self, v: Self) -> Result<Self, ArithmeticError> {
self.ensure_div_assign(v)?;
Ok(self)
}
}

impl<T: CheckedAdd + PartialOrd + Zero + Copy> EnsureAdd for T {}
impl<T: CheckedSub + PartialOrd + Zero + Copy> EnsureSub for T {}
impl<T: CheckedMul + PartialOrd + Zero + Copy> EnsureMul for T {}
impl<T: CheckedDiv + PartialOrd + Zero + Copy> EnsureDiv for T {}
impl<T: EnsureAddAssign> EnsureAdd for T {}
impl<T: EnsureSubAssign> EnsureSub for T {}
impl<T: EnsureMulAssign> EnsureMul for T {}
impl<T: EnsureDivAssign> EnsureDiv for T {}

/// Meta trait that supports all immutable arithmetic `Ensure*` operations
pub trait EnsureOp: EnsureAdd + EnsureSub + EnsureMul + EnsureDiv {}
impl<T: EnsureAdd + EnsureSub + EnsureMul + EnsureDiv> EnsureOp for T {}

/// 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.
Expand All @@ -496,14 +502,14 @@ 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.
Expand All @@ -527,14 +533,14 @@ mod ensure {
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// ```
fn ensure_sub_assign(&mut self, v: Self) -> Result<(), ArithmeticError> {
*self = self.ensure_sub(v)?;
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.
Expand All @@ -558,14 +564,14 @@ mod ensure {
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_mul_assign(&mut self, v: Self) -> Result<(), ArithmeticError> {
*self = self.ensure_mul(v)?;
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.
Expand All @@ -589,15 +595,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.checked_div(&v).ok_or_else(|| error::division(self, &v))?;
Ok(())
}
}

impl<T: EnsureAdd> EnsureAddAssign for T {}
impl<T: EnsureSub> EnsureSubAssign for T {}
impl<T: EnsureMul> EnsureMulAssign for T {}
impl<T: EnsureDiv> EnsureDivAssign for T {}
impl<T: CheckedAdd + PartialOrd + Zero> EnsureAddAssign for T {}
impl<T: CheckedSub + PartialOrd + Zero> EnsureSubAssign for T {}
impl<T: CheckedMul + PartialOrd + Zero> EnsureMulAssign for T {}
impl<T: CheckedDiv + PartialOrd + Zero> EnsureDivAssign for T {}

/// Meta trait that supports all assigned arithmetic `Ensure*` operations
pub trait EnsureOpAssign:
Expand All @@ -609,7 +615,6 @@ mod ensure {
{
}

/// Meta trait that supports all arithmetic operations
pub trait Ensure: EnsureOp + EnsureOpAssign {}
impl<T: EnsureOp + EnsureOpAssign> Ensure for T {}

Expand Down Expand Up @@ -643,7 +648,7 @@ mod ensure {
d: D,
) -> Result<Self, ArithmeticError> {
<Self as FixedPointNumber>::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`.
Expand All @@ -670,7 +675,7 @@ mod ensure {
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_mul_int<N: FixedPointOperand>(self, n: N) -> Result<N, ArithmeticError> {
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`.
Expand All @@ -697,16 +702,14 @@ mod ensure {
/// assert_eq!(overflow(), Err(ArithmeticError::Overflow));
/// ```
fn ensure_div_int<D: FixedPointOperand>(self, d: D) -> Result<D, ArithmeticError> {
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<T: FixedPointNumber> EnsureFixedPointNumber for T {}

/// Similar to [`TryFrom`] but returning an [`ArithmeticError`] error.
pub trait EnsureFrom<T: PartialOrd + Zero + Copy>:
TryFrom<T> + PartialOrd + Zero + Copy
{
pub trait EnsureFrom<T: PartialOrd + Zero>: TryFrom<T> + PartialOrd + Zero {
/// Performs the conversion returning an [`ArithmeticError`] if fails.
///
/// Similar to [`TryFrom::try_from()`] but returning an [`ArithmeticError`] error.
Expand All @@ -728,14 +731,13 @@ mod ensure {
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_from(other: T) -> Result<Self, ArithmeticError> {
Self::try_from(other).map_err(|_| error::equivalent(other))
let err = error::equivalent(&other);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the performance of this, it seems like the assembly resulting code of this can be easily reordered by the compiler and should not be penalty

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you check this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not empirically (not sure how to do it well).

To extend my argument, error::equivalent has no side effects, and if the resulting value is only read under a condition, the compiler can only execute that for that condition. I understand that this works for any T that is not really consumed by the try_from(), which means that T is in fact a Copy type. The compiler could not optimize this if T is not Copy.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay ;)

Self::try_from(other).map_err(|_| err)
}
}

/// Similar to [`TryInto`] but returning an [`ArithmeticError`] error.
pub trait EnsureInto<T: PartialOrd + Zero + Copy>:
TryInto<T> + PartialOrd + Zero + Copy
{
pub trait EnsureInto<T: PartialOrd + Zero>: TryInto<T> + PartialOrd + Zero {
/// Performs the conversion returning an [`ArithmeticError`] if fails.
///
/// Similar to [`TryInto::try_into()`] but returning an [`ArithmeticError`] error
Expand All @@ -757,12 +759,13 @@ mod ensure {
/// assert_eq!(underflow(), Err(ArithmeticError::Underflow));
/// ```
fn ensure_into(self) -> Result<T, ArithmeticError> {
self.try_into().map_err(|_| error::equivalent(self))
let err = error::equivalent(&self);
self.try_into().map_err(|_| err)
}
}

impl<T: TryFrom<S> + PartialOrd + Zero + Copy, S: PartialOrd + Zero + Copy> EnsureFrom<S> for T {}
impl<T: TryInto<S> + PartialOrd + Zero + Copy, S: PartialOrd + Zero + Copy> EnsureInto<S> for T {}
impl<T: TryFrom<S> + PartialOrd + Zero, S: PartialOrd + Zero> EnsureFrom<S> for T {}
impl<T: TryInto<S> + PartialOrd + Zero, S: PartialOrd + Zero> EnsureInto<S> for T {}

mod error {
use super::{ArithmeticError, Zero};
Expand All @@ -773,9 +776,9 @@ mod ensure {
Positive,
}

impl<T: PartialOrd + Zero> From<T> for Signum {
fn from(value: T) -> Self {
if value < Zero::zero() {
impl<T: PartialOrd + Zero> From<&T> for Signum {
fn from(value: &T) -> Self {
if value < &Zero::zero() {
Signum::Negative
} else {
Signum::Positive
Expand All @@ -795,33 +798,33 @@ mod ensure {
}
}

pub fn equivalent<R: PartialOrd + Zero + Copy>(r: R) -> ArithmeticError {
pub fn equivalent<R: PartialOrd + Zero>(r: &R) -> ArithmeticError {
match Signum::from(r) {
Signum::Negative => ArithmeticError::Underflow,
Signum::Positive => ArithmeticError::Overflow,
}
}

pub fn inverse<R: PartialOrd + Zero + Copy>(r: R) -> ArithmeticError {
pub fn inverse<R: PartialOrd + Zero>(r: &R) -> ArithmeticError {
match Signum::from(r) {
Signum::Negative => ArithmeticError::Overflow,
Signum::Positive => ArithmeticError::Underflow,
}
}

pub fn multiplication<L: PartialOrd + Zero + Copy, R: PartialOrd + Zero + Copy>(
l: L,
r: R,
pub fn multiplication<L: PartialOrd + Zero, R: PartialOrd + Zero>(
l: &L,
r: &R,
) -> ArithmeticError {
match Signum::from(l) * Signum::from(r) {
Signum::Negative => ArithmeticError::Underflow,
Signum::Positive => ArithmeticError::Overflow,
}
}

pub fn division<N: PartialOrd + Zero + Copy, D: PartialOrd + Zero + Copy>(
n: N,
d: D,
pub fn division<N: PartialOrd + Zero, D: PartialOrd + Zero>(
n: &N,
d: &D,
) -> ArithmeticError {
if d.is_zero() {
ArithmeticError::DivisionByZero
Expand Down