use codec::HasCompact;
use core::ops::{
Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Shl, Shr, Sub, SubAssign,
};
pub use ensure::{
ensure_pow, Ensure, EnsureAdd, EnsureAddAssign, EnsureDiv, EnsureDivAssign,
EnsureFixedPointNumber, EnsureFrom, EnsureInto, EnsureMul, EnsureMulAssign, EnsureOp,
EnsureOpAssign, EnsureSub, EnsureSubAssign,
};
pub use integer_sqrt::IntegerSquareRoot;
pub use num_traits::{
checked_pow, Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl,
CheckedShr, CheckedSub, One, Signed, Unsigned, Zero,
};
use crate::MultiplyRational;
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
+ CheckedRem
+ CheckedNeg
+ Ensure
+ Saturating
+ PartialOrd<Self>
+ Ord
+ Bounded
+ HasCompact
+ Sized
+ Clone
+ 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
+ CheckedRem
+ CheckedNeg
+ Ensure
+ Saturating
+ PartialOrd<Self>
+ Ord
+ Bounded
+ HasCompact
+ Sized
+ Clone
+ 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
{
}
pub trait AtLeast8Bit: BaseArithmetic + From<u8> {}
impl<T: BaseArithmetic + From<u8>> AtLeast8Bit for T {}
pub trait AtLeast8BitUnsigned: AtLeast8Bit + Unsigned {}
impl<T: AtLeast8Bit + Unsigned> AtLeast8BitUnsigned for T {}
pub trait AtLeast16Bit: BaseArithmetic + From<u16> {}
impl<T: BaseArithmetic + From<u16>> AtLeast16Bit for T {}
pub trait AtLeast16BitUnsigned: AtLeast16Bit + Unsigned {}
impl<T: AtLeast16Bit + Unsigned> AtLeast16BitUnsigned for T {}
pub trait AtLeast32Bit: BaseArithmetic + From<u16> + From<u32> {}
impl<T: BaseArithmetic + From<u16> + From<u32>> AtLeast32Bit for T {}
pub trait AtLeast32BitUnsigned: AtLeast32Bit + Unsigned + MultiplyRational {}
impl<T: AtLeast32Bit + Unsigned + MultiplyRational> AtLeast32BitUnsigned for T {}
pub trait UniqueSaturatedFrom<T: Sized>: Sized {
#[must_use]
fn unique_saturated_from(t: T) -> Self;
}
pub trait UniqueSaturatedInto<T: Sized>: Sized {
#[must_use]
fn unique_saturated_into(self) -> T;
}
impl<T: Sized, S: TryFrom<T> + Bounded + Sized> UniqueSaturatedFrom<T> for S {
fn unique_saturated_from(t: T) -> Self {
S::try_from(t).unwrap_or_else(|_| Bounded::max_value())
}
}
impl<T: Bounded + Sized, S: TryInto<T> + Sized> UniqueSaturatedInto<T> for S {
fn unique_saturated_into(self) -> T {
self.try_into().unwrap_or_else(|_| Bounded::max_value())
}
}
pub trait Saturating {
#[must_use]
fn saturating_add(self, rhs: Self) -> Self;
#[must_use]
fn saturating_sub(self, rhs: Self) -> Self;
#[must_use]
fn saturating_mul(self, rhs: Self) -> Self;
#[must_use]
fn saturating_pow(self, exp: usize) -> Self;
#[must_use]
fn saturating_less_one(mut self) -> Self
where
Self: One,
{
self.saturating_dec();
self
}
#[must_use]
fn saturating_plus_one(mut self) -> Self
where
Self: One,
{
self.saturating_inc();
self
}
fn saturating_inc(&mut self)
where
Self: One,
{
let mut o = Self::one();
core::mem::swap(&mut o, self);
*self = o.saturating_add(One::one());
}
fn saturating_dec(&mut self)
where
Self: One,
{
let mut o = Self::one();
core::mem::swap(&mut o, self);
*self = o.saturating_sub(One::one());
}
fn saturating_accrue(&mut self, amount: Self)
where
Self: One,
{
let mut o = Self::one();
core::mem::swap(&mut o, self);
*self = o.saturating_add(amount);
}
fn saturating_reduce(&mut self, amount: Self)
where
Self: One,
{
let mut o = Self::one();
core::mem::swap(&mut o, self);
*self = o.saturating_sub(amount);
}
}
impl<T: Clone + Zero + One + PartialOrd + CheckedMul + Bounded + num_traits::Saturating> Saturating
for T
{
fn saturating_add(self, o: Self) -> Self {
<Self as num_traits::Saturating>::saturating_add(self, o)
}
fn saturating_sub(self, o: Self) -> Self {
<Self as num_traits::Saturating>::saturating_sub(self, o)
}
fn saturating_mul(self, o: Self) -> Self {
self.checked_mul(&o).unwrap_or_else(|| {
if (self < T::zero()) != (o < T::zero()) {
Bounded::min_value()
} else {
Bounded::max_value()
}
})
}
fn saturating_pow(self, exp: usize) -> Self {
let neg = self < T::zero() && !exp.is_multiple_of(2);
checked_pow(self, exp).unwrap_or_else(|| {
if neg {
Bounded::min_value()
} else {
Bounded::max_value()
}
})
}
}
pub trait SaturatedConversion {
#[must_use]
fn saturated_from<T>(t: T) -> Self
where
Self: UniqueSaturatedFrom<T>,
{
<Self as UniqueSaturatedFrom<T>>::unique_saturated_from(t)
}
#[must_use]
fn saturated_into<T>(self) -> T
where
Self: UniqueSaturatedInto<T>,
{
<Self as UniqueSaturatedInto<T>>::unique_saturated_into(self)
}
}
impl<T: Sized> SaturatedConversion for T {}
mod ensure {
use super::{checked_pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, One, Zero};
use crate::{ArithmeticError, FixedPointNumber, FixedPointOperand};
pub trait EnsureAdd: EnsureAddAssign {
fn ensure_add(mut self, v: Self) -> Result<Self, ArithmeticError> {
self.ensure_add_assign(v)?;
Ok(self)
}
}
pub trait EnsureSub: EnsureSubAssign {
fn ensure_sub(mut self, v: Self) -> Result<Self, ArithmeticError> {
self.ensure_sub_assign(v)?;
Ok(self)
}
}
pub trait EnsureMul: EnsureMulAssign {
fn ensure_mul(mut self, v: Self) -> Result<Self, ArithmeticError> {
self.ensure_mul_assign(v)?;
Ok(self)
}
}
pub trait EnsureDiv: EnsureDivAssign {
fn ensure_div(mut self, v: Self) -> Result<Self, ArithmeticError> {
self.ensure_div_assign(v)?;
Ok(self)
}
}
pub fn ensure_pow<T: One + CheckedMul + Clone>(
base: T,
exp: usize,
) -> Result<T, ArithmeticError> {
checked_pow(base, exp).ok_or(ArithmeticError::Overflow)
}
impl<T: EnsureAddAssign> EnsureAdd for T {}
impl<T: EnsureSubAssign> EnsureSub for T {}
impl<T: EnsureMulAssign> EnsureMul for T {}
impl<T: EnsureDivAssign> EnsureDiv for T {}
pub trait EnsureOp: EnsureAdd + EnsureSub + EnsureMul + EnsureDiv {}
impl<T: EnsureAdd + EnsureSub + EnsureMul + EnsureDiv> EnsureOp for T {}
pub trait EnsureAddAssign: CheckedAdd + PartialOrd + Zero {
fn ensure_add_assign(&mut self, v: Self) -> Result<(), ArithmeticError> {
*self = self.checked_add(&v).ok_or_else(|| error::equivalent(&v))?;
Ok(())
}
}
pub trait EnsureSubAssign: CheckedSub + PartialOrd + Zero {
fn ensure_sub_assign(&mut self, v: Self) -> Result<(), ArithmeticError> {
*self = self.checked_sub(&v).ok_or_else(|| error::inverse(&v))?;
Ok(())
}
}
pub trait EnsureMulAssign: CheckedMul + PartialOrd + Zero {
fn ensure_mul_assign(&mut self, v: Self) -> Result<(), ArithmeticError> {
*self = self.checked_mul(&v).ok_or_else(|| error::multiplication(self, &v))?;
Ok(())
}
}
pub trait EnsureDivAssign: CheckedDiv + PartialOrd + Zero {
fn ensure_div_assign(&mut self, v: Self) -> Result<(), ArithmeticError> {
*self = self.checked_div(&v).ok_or_else(|| error::division(self, &v))?;
Ok(())
}
}
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 {}
pub trait EnsureOpAssign:
EnsureAddAssign + EnsureSubAssign + EnsureMulAssign + EnsureDivAssign
{
}
impl<T: EnsureAddAssign + EnsureSubAssign + EnsureMulAssign + EnsureDivAssign> EnsureOpAssign
for T
{
}
pub trait Ensure: EnsureOp + EnsureOpAssign {}
impl<T: EnsureOp + EnsureOpAssign> Ensure for T {}
pub trait EnsureFixedPointNumber: FixedPointNumber {
fn ensure_from_rational<N: FixedPointOperand, D: FixedPointOperand>(
n: N,
d: D,
) -> Result<Self, ArithmeticError> {
<Self as FixedPointNumber>::checked_from_rational(n, d)
.ok_or_else(|| error::division(&n, &d))
}
fn ensure_mul_int<N: FixedPointOperand>(self, n: N) -> Result<N, ArithmeticError> {
self.checked_mul_int(n).ok_or_else(|| error::multiplication(&self, &n))
}
fn ensure_div_int<D: FixedPointOperand>(self, d: D) -> Result<D, ArithmeticError> {
self.checked_div_int(d).ok_or_else(|| error::division(&self, &d))
}
}
impl<T: FixedPointNumber> EnsureFixedPointNumber for T {}
pub trait EnsureFrom<T: PartialOrd + Zero>: TryFrom<T> + PartialOrd + Zero {
fn ensure_from(other: T) -> Result<Self, ArithmeticError> {
let err = error::equivalent(&other);
Self::try_from(other).map_err(|_| err)
}
}
pub trait EnsureInto<T: PartialOrd + Zero>: TryInto<T> + PartialOrd + Zero {
fn ensure_into(self) -> Result<T, ArithmeticError> {
let err = error::equivalent(&self);
self.try_into().map_err(|_| err)
}
}
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};
#[derive(PartialEq)]
enum Signum {
Negative,
Positive,
}
impl<T: PartialOrd + Zero> From<&T> for Signum {
fn from(value: &T) -> Self {
if value < &Zero::zero() {
Signum::Negative
} else {
Signum::Positive
}
}
}
impl core::ops::Mul for Signum {
type Output = Self;
fn mul(self, rhs: Self) -> Self {
if self != rhs {
Signum::Negative
} else {
Signum::Positive
}
}
}
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>(r: &R) -> ArithmeticError {
match Signum::from(r) {
Signum::Negative => ArithmeticError::Overflow,
Signum::Positive => ArithmeticError::Underflow,
}
}
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, D: PartialOrd + Zero>(
n: &N,
d: &D,
) -> ArithmeticError {
if d.is_zero() {
ArithmeticError::DivisionByZero
} else {
multiplication(n, d)
}
}
}
}
#[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_pow_works() {
test_ensure(
values().into_iter().map(|(base, exp)| (base, exp as usize)).collect(),
ensure_pow,
|&a, &b| checked_pow(a, b),
);
}
#[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);
}
fn test_ensure<V, W, E, P>(pairs: Vec<(V, W)>, ensured: E, unensured: P)
where
V: Ensure + core::fmt::Debug + Copy,
W: Ensure + core::fmt::Debug + Copy,
E: Fn(V, W) -> Result<V, ArithmeticError>,
P: Fn(&V, &W) -> Option<V>,
{
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());
},
}
}
}
fn test_ensure_assign<V, W, E, P>(pairs: Vec<(V, W)>, ensured: E, unensured: P)
where
V: Ensure + std::panic::RefUnwindSafe + std::panic::UnwindSafe + core::fmt::Debug + Copy,
W: Ensure + std::panic::RefUnwindSafe + std::panic::UnwindSafe + core::fmt::Debug + Copy,
E: Fn(&mut V, W) -> Result<(), ArithmeticError>,
P: Fn(V, W) -> Result<V, ArithmeticError> + 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));
},
}
}
}
fn values() -> Vec<(i32, i32)> {
let mut rng = thread_rng();
let mut one_dimension = || {
let mut ret = vec![0i32; 1007];
ret[..7].copy_from_slice(&[-1, 0, 1, i32::MIN, i32::MAX, i32::MAX - 1, i32::MIN + 1]);
rng.fill(&mut ret[7..]);
ret.shuffle(&mut rng);
ret
};
one_dimension().into_iter().zip(one_dimension().into_iter()).collect()
}
}