use ruint::aliases::U256;
use ruint::Uint;
pub trait FullMulDiv {
fn full_mul_div(self, rhs: Self, div: Self) -> Self;
}
macro_rules! impl_primitive {
($primary:ty, $intermediate:ty) => {
impl FullMulDiv for $primary {
fn full_mul_div(self, rhs: Self, div: Self) -> Self {
let numer = (<$intermediate>::from(self))
.checked_mul(<$intermediate>::from(rhs))
.unwrap();
let denom = <$intermediate>::from(div);
let out = numer.checked_div(denom).unwrap();
out.try_into().unwrap()
}
}
};
}
impl_primitive!(u8, u16);
impl_primitive!(i8, i16);
impl_primitive!(u16, u32);
impl_primitive!(i16, i32);
impl_primitive!(u32, u64);
impl_primitive!(i32, i64);
impl_primitive!(u64, u128);
impl_primitive!(i64, i128);
impl FullMulDiv for u128 {
fn full_mul_div(self, rhs: Self, div: Self) -> Self {
let out: U256 = Uint::from(self)
.checked_mul(Uint::from(rhs))
.unwrap()
.checked_div(Uint::from(div))
.unwrap();
out.try_into().unwrap()
}
}
impl FullMulDiv for i128 {
fn full_mul_div(self, rhs: Self, div: Self) -> Self {
if let Some(out) = self
.checked_mul(rhs)
.map(|numer| numer.checked_div(div).unwrap())
{
return out;
}
#[allow(clippy::arithmetic_side_effects)]
let sign = self.signum() * rhs.signum() * div.signum();
let this = U256::from(self.unsigned_abs());
let rhs = U256::from(rhs.unsigned_abs());
let div = U256::from(div.unsigned_abs());
let unsigned = this.checked_mul(rhs).unwrap().checked_div(div).unwrap();
match sign {
1 => i128::try_from(unsigned).unwrap(),
-1 => {
let unsigned = u128::try_from(unsigned).unwrap();
let twos_complement = (!unsigned).overflowing_add(1).0;
i128::from_le_bytes(twos_complement.to_le_bytes())
}
_ => unreachable!(),
}
}
}
#[cfg(test)]
mod tests {
use malachite::Integer;
use proptest::prelude::*;
use super::*;
#[test]
fn u128_full_mul_div() {
proptest!(|(a: u128, b: u128, div: u128)| {
if div == 0 {
return Ok(());
}
let reference = Integer::from(a) * Integer::from(b) / Integer::from(div);
match u128::try_from(&reference) {
Ok(reference) => assert_eq!(u128::full_mul_div(a, b, div), reference),
Err(_) => {}
};
});
}
#[test]
fn i128_full_mul_div() {
proptest!(|(a: i128, b: i128, div: i128)| {
if div == 0 {
return Ok(());
}
let reference = Integer::from(a) * Integer::from(b) / Integer::from(div);
match i128::try_from(&reference) {
Ok(reference) => assert_eq!(i128::full_mul_div(a, b, div), reference),
Err(_) => {}
};
});
}
}