use std::fmt;
use num_traits::{
CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedSub, FromPrimitive, One, Signed,
};
pub trait Num:
num_traits::Num
+ CheckedAdd
+ CheckedMul
+ CheckedSub
+ CheckedNeg
+ CheckedDiv
+ Clone
+ Ord
+ FromPrimitive
+ fmt::Debug
+ fmt::Display
{
}
impl<
T: num_traits::Num
+ CheckedAdd
+ CheckedMul
+ CheckedSub
+ CheckedNeg
+ CheckedDiv
+ Clone
+ Ord
+ FromPrimitive
+ fmt::Debug
+ fmt::Display,
> Num for T
{
}
pub trait Unsigned: num_traits::Unsigned {
type Signed: TryFrom<Self> + UnsignedAbs<Unsigned = Self> + CheckedNeg;
fn to_signed(&self) -> crate::Result<Self::Signed>
where
Self: Clone,
{
self.clone().try_into().map_err(|_| crate::Error::Convert)
}
fn to_signed_with_sign(&self, negative: bool) -> crate::Result<Self::Signed>
where
Self: Clone,
Self::Signed: CheckedSub,
{
if negative {
self.to_opposite_signed()
} else {
self.to_signed()
}
}
fn to_opposite_signed(&self) -> crate::Result<Self::Signed>
where
Self: Clone,
Self::Signed: CheckedSub,
{
self.to_signed()?
.checked_neg()
.ok_or(crate::Error::Computation("to opposite signed"))
}
fn diff(self, other: Self) -> Self;
fn checked_signed_sub(self, other: Self) -> crate::Result<Self::Signed>
where
Self: Ord + Clone,
Self::Signed: CheckedSub,
{
if self >= other {
self.diff(other).to_signed()
} else {
self.diff(other).to_opposite_signed()
}
}
fn checked_add_with_signed(&self, other: &Self::Signed) -> Option<Self>
where
Self: CheckedAdd + CheckedSub,
{
let value = other.unsigned_abs();
if other.is_positive() {
self.checked_add(&value)
} else {
self.checked_sub(&value)
}
}
fn checked_sub_with_signed(&self, other: &Self::Signed) -> Option<Self>
where
Self: CheckedAdd + CheckedSub,
{
let value = other.unsigned_abs();
if other.is_positive() {
self.checked_sub(&value)
} else {
self.checked_add(&value)
}
}
fn checked_mul_with_signed(&self, other: &Self::Signed) -> Option<Self::Signed>
where
Self: CheckedMul,
{
let value = other.unsigned_abs();
if other.is_negative() {
Some(
Self::Signed::try_from(self.checked_mul(&value)?)
.ok()?
.checked_neg()?,
)
} else {
self.checked_mul(&value)?.try_into().ok()
}
}
fn as_divisor_to_round_up_magnitude_div(&self, dividend: &Self::Signed) -> Option<Self::Signed>
where
Self: Clone,
Self::Signed: CheckedSub + CheckedAdd + CheckedDiv,
{
if self.is_zero() {
return None;
}
let divisor: Self::Signed = self.clone().try_into().ok()?;
if dividend.is_negative() {
dividend
.checked_sub(&divisor)?
.checked_add(&One::one())?
.checked_div(&divisor)
} else {
dividend
.checked_add(&divisor)?
.checked_sub(&One::one())?
.checked_div(&divisor)
}
}
fn checked_round_up_div(&self, divisor: &Self) -> Option<Self>
where
Self: CheckedAdd + CheckedSub + Clone + CheckedDiv,
{
if divisor.is_zero() {
return None;
}
self.checked_add(divisor)?
.checked_sub(&One::one())?
.checked_div(divisor)
}
fn bound_magnitude(value: &Self::Signed, min: &Self, max: &Self) -> crate::Result<Self::Signed>
where
Self: Ord + Clone,
Self::Signed: Clone + CheckedSub,
{
if min > max {
return Err(crate::Error::InvalidArgument("min > max"));
}
let magnitude = value.unsigned_abs();
let negative = value.is_negative();
if magnitude < *min {
min.to_signed_with_sign(negative)
} else if magnitude > *max {
max.to_signed_with_sign(negative)
} else {
Ok(value.clone())
}
}
}
pub trait UnsignedAbs: Signed {
type Unsigned;
fn unsigned_abs(&self) -> Self::Unsigned;
}
pub trait MulDiv: Unsigned {
fn checked_mul_div(&self, numerator: &Self, denominator: &Self) -> Option<Self>;
fn checked_mul_div_ceil(&self, numerator: &Self, denominator: &Self) -> Option<Self>;
fn checked_mul_div_with_signed_numerator(
&self,
numerator: &Self::Signed,
denominator: &Self,
) -> Option<Self::Signed> {
let ans = self
.checked_mul_div(&numerator.unsigned_abs(), denominator)?
.try_into()
.ok()?;
if numerator.is_positive() {
Some(ans)
} else {
ans.checked_neg()
}
}
}
impl Unsigned for u64 {
type Signed = i64;
fn diff(self, other: Self) -> Self {
self.abs_diff(other)
}
}
impl MulDiv for u64 {
#[allow(clippy::arithmetic_side_effects)]
fn checked_mul_div(&self, numerator: &Self, denominator: &Self) -> Option<Self> {
if *denominator == 0 {
return None;
}
let x = *self as u128;
let numerator = *numerator as u128;
let denominator = *denominator as u128;
let ans = x * numerator / denominator;
ans.try_into().ok()
}
#[allow(clippy::arithmetic_side_effects)]
fn checked_mul_div_ceil(&self, numerator: &Self, denominator: &Self) -> Option<Self> {
if *denominator == 0 {
return None;
}
let x = *self as u128;
let numerator = *numerator as u128;
let denominator = *denominator as u128;
let ans = (x * numerator).div_ceil(denominator);
ans.try_into().ok()
}
}
impl UnsignedAbs for i64 {
type Unsigned = u64;
fn unsigned_abs(&self) -> u64 {
(*self).unsigned_abs()
}
}
#[cfg(feature = "u128")]
mod u128 {
use super::{MulDiv, Unsigned, UnsignedAbs};
use ruint::aliases::U256;
impl Unsigned for u128 {
type Signed = i128;
fn diff(self, other: Self) -> Self {
self.abs_diff(other)
}
}
impl UnsignedAbs for i128 {
type Unsigned = u128;
fn unsigned_abs(&self) -> u128 {
(*self).unsigned_abs()
}
}
impl MulDiv for u128 {
#[allow(clippy::arithmetic_side_effects)]
fn checked_mul_div(&self, numerator: &Self, denominator: &Self) -> Option<Self> {
if *denominator == 0 {
return None;
}
let x = U256::from(*self);
let numerator = U256::from(*numerator);
let denominator = U256::from(*denominator);
let ans = x * numerator / denominator;
ans.try_into().ok()
}
#[allow(clippy::arithmetic_side_effects)]
fn checked_mul_div_ceil(&self, numerator: &Self, denominator: &Self) -> Option<Self> {
if *denominator == 0 {
return None;
}
let x = U256::from(*self);
let numerator = U256::from(*numerator);
let denominator = U256::from(*denominator);
let ans = (x * numerator).div_ceil(denominator);
ans.try_into().ok()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_up_magnitude_division() {
let b = 3u64;
let positive = 1i64;
let negative = -1i64;
assert_eq!(b.as_divisor_to_round_up_magnitude_div(&positive), Some(1));
assert_eq!(b.as_divisor_to_round_up_magnitude_div(&negative), Some(-1));
}
#[test]
fn round_up_division() {
let b = 3u64;
let a = 1u64;
assert_eq!(a.checked_round_up_div(&b), Some(1));
}
#[test]
fn mul_div_ceil() {
let a = 650_406_504u64;
let a2 = 650_406_505u64;
let b = 40_000_000_000u64;
let c = 80_000_000_000u64;
assert_eq!(a.checked_mul_div_ceil(&b, &c).unwrap(), 325_203_252);
assert_eq!(a2.checked_mul_div_ceil(&b, &c).unwrap(), 325_203_253);
}
#[cfg(feature = "u128")]
#[test]
fn mul_div_ceil_u128() {
let a = 650_406_504u128;
let a2 = 650_406_505u128;
let b = 40_000_000_000u128;
let c = 80_000_000_000u128;
assert_eq!(a.checked_mul_div_ceil(&b, &c).unwrap(), 325_203_252);
assert_eq!(a2.checked_mul_div_ceil(&b, &c).unwrap(), 325_203_253);
}
#[test]
fn bound_magnitude() {
let a = -123i64;
assert_eq!(Unsigned::bound_magnitude(&a, &0, &124u64).unwrap(), -123);
assert_eq!(Unsigned::bound_magnitude(&a, &0, &120u64).unwrap(), -120);
assert_eq!(Unsigned::bound_magnitude(&a, &124, &256u64).unwrap(), -124);
assert_eq!(Unsigned::bound_magnitude(&a, &125, &125u64).unwrap(), -125);
let b = 123i64;
assert_eq!(Unsigned::bound_magnitude(&b, &0, &124u64).unwrap(), 123);
assert_eq!(Unsigned::bound_magnitude(&b, &0, &120u64).unwrap(), 120);
assert_eq!(Unsigned::bound_magnitude(&b, &124, &256u64).unwrap(), 124);
assert_eq!(Unsigned::bound_magnitude(&b, &125, &125u64).unwrap(), 125);
let c = 0i64;
assert_eq!(Unsigned::bound_magnitude(&c, &1, &124u64).unwrap(), 1);
let d = -0i64;
assert_eq!(Unsigned::bound_magnitude(&d, &1, &124u64).unwrap(), 1);
let result = Unsigned::bound_magnitude(&0, &u64::MAX, &u64::MAX);
assert!(result.is_err());
}
}