use core::ops::{
Add,
AddAssign,
Div,
DivAssign,
Mul,
MulAssign,
Sub,
SubAssign,
};
use num_traits::{
checked_pow,
Bounded,
CheckedMul,
One,
Unsigned,
Zero,
};
pub trait BaseArithmetic:
Sized
+ From<u8>
+ Bounded
+ Ord
+ PartialOrd<Self>
+ Zero
+ One
+ Bounded
+ Add<Self, Output = Self>
+ AddAssign<Self>
+ Sub<Self, Output = Self>
+ SubAssign<Self>
+ Mul<Self, Output = Self>
+ MulAssign<Self>
+ Div<Self, Output = Self>
+ DivAssign<Self>
+ CheckedMul
+ Saturating
+ TryFrom<u16>
+ TryFrom<u32>
+ TryFrom<u64>
+ TryFrom<u128>
+ TryFrom<usize>
+ TryInto<u16>
+ TryInto<u32>
+ TryInto<u64>
+ TryInto<u128>
+ TryInto<usize>
{
}
impl<T> BaseArithmetic for T where
T: Sized
+ From<u8>
+ Bounded
+ Ord
+ PartialOrd<Self>
+ Zero
+ One
+ Add<Self, Output = Self>
+ AddAssign<Self>
+ Sub<Self, Output = Self>
+ SubAssign<Self>
+ Mul<Self, Output = Self>
+ MulAssign<Self>
+ Div<Self, Output = Self>
+ DivAssign<Self>
+ CheckedMul
+ Saturating
+ TryFrom<u16>
+ TryFrom<u32>
+ TryFrom<u64>
+ TryFrom<u128>
+ TryFrom<usize>
+ TryInto<u16>
+ TryInto<u32>
+ TryInto<u64>
+ TryInto<u128>
+ TryInto<usize>
{
}
pub trait AtLeast32Bit: BaseArithmetic + From<u16> + From<u32> {}
impl<T> AtLeast32Bit for T where T: BaseArithmetic + From<u16> + From<u32> {}
pub trait AtLeast32BitUnsigned: AtLeast32Bit + Unsigned {}
impl<T> AtLeast32BitUnsigned for T where T: AtLeast32Bit + Unsigned {}
pub trait Saturating {
fn saturating_add(self, rhs: Self) -> Self;
fn saturating_sub(self, rhs: Self) -> Self;
fn saturating_mul(self, rhs: Self) -> Self;
fn saturating_pow(self, exp: usize) -> Self;
}
impl<T> Saturating for T
where
T: Clone + Zero + One + PartialOrd + CheckedMul + Bounded + num_traits::Saturating,
{
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 % 2 != 0;
checked_pow(self, exp).unwrap_or_else(|| {
if neg {
Bounded::min_value()
} else {
Bounded::max_value()
}
})
}
}
#[cfg(test)]
mod tests {
use super::Saturating;
#[test]
fn saturating_add() {
assert_eq!(
u64::max_value(),
Saturating::saturating_add(u64::max_value(), 1)
)
}
#[test]
fn saturatiung_sub() {
assert_eq!(
u64::min_value(),
Saturating::saturating_sub(u64::min_value(), 1)
)
}
#[test]
fn saturating_mul() {
assert_eq!(
u64::max_value(),
Saturating::saturating_mul(u64::max_value(), 2)
);
assert_eq!(
i64::max_value(),
Saturating::saturating_mul(i64::max_value(), 2)
);
assert_eq!(
i64::min_value(),
Saturating::saturating_mul(i64::min_value(), 2)
);
assert_eq!(
i64::min_value(),
Saturating::saturating_mul(2, i64::min_value())
);
}
#[test]
fn saturating_pow() {
assert_eq!(
u64::max_value(),
Saturating::saturating_pow(u64::max_value(), 2)
);
assert_eq!(
i64::max_value(),
Saturating::saturating_pow(i64::min_value(), 2)
);
assert_eq!(
i64::min_value(),
Saturating::saturating_pow(i64::min_value(), 3)
);
}
}