mod test;
use multitype::Integral;
pub(crate) trait CarryingMulAdd<Other = Self>: Integral {
fn carrying_mul_add(
self,
mul: Other,
add: Other,
carry: Other,
) -> (Self::Unsigned, Self);
}
macro_rules! impl_carrying_mul_add {
{ $($Ty:ty { Wide = $Wide:ty$(,)? }),*$(,)? } => {$(
impl ::multitype::CarryingMulAdd for $Ty {
#[inline(always)]
#[track_caller]
fn carrying_mul_add(
self,
mul: Self,
add: Self,
carry: Self,
) -> (Self::Unsigned, Self) {
let wide = self as $Wide * mul as $Wide + add as $Wide + carry as $Wide;
let low = wide as Self::Unsigned;
let high = (wide >> Self::BITS) as Self;
(low, high)
}
}
)*};
}
impl_carrying_mul_add! {
u8 { Wide = u16 },
u16 { Wide = u32 },
u32 { Wide = u64 },
u64 { Wide = u128 },
i8 { Wide = i16 },
i16 { Wide = i32 },
i32 { Wide = i64 },
i64 { Wide = i128 },
}
#[cfg(target_pointer_width = "16")]
impl_carrying_mul_add! {
usize { Wide = u32 },
isize { Wide = i32 },
}
#[cfg(target_pointer_width = "32")]
impl_carrying_mul_add! {
usize { Wide = u64 },
isize { Wide = i64 },
}
#[cfg(target_pointer_width = "64")]
impl_carrying_mul_add! {
usize { Wide = u128 },
isize { Wide = i128 },
}
impl CarryingMulAdd for u128 {
#[inline]
fn carrying_mul_add(
self,
mul: Self,
add: Self,
carry: Self,
) -> (Self::Unsigned, Self) {
#[inline]
#[must_use]
const fn u128_to_parts(value: u128) -> (u128, u128) {
let low = value & u64::MAX as u128;
let high = value >> u64::BITS;
(low, high)
}
let lhs = u128_to_parts(self);
let mul = u128_to_parts(mul);
let tmp0 = lhs.0.wrapping_mul(mul.0);
let tmp1 = lhs.0.wrapping_mul(mul.1);
let tmp2 = lhs.1.wrapping_mul(mul.0);
let tmp3 = lhs.1.wrapping_mul(mul.1);
let carry0;
let carry1;
let mut low = tmp0;
(low, carry0) = low.overflowing_add(tmp1 << (Self::BITS / 2));
(low, carry1) = low.overflowing_add(tmp2 << (Self::BITS / 2));
let carry2;
(low, carry2) = low.overflowing_add(add);
let carry3;
(low, carry3) = low.overflowing_add(carry);
let mut high = tmp3;
high = high.wrapping_add(tmp1 >> (Self::BITS / 2));
high = high.wrapping_add(tmp2 >> (Self::BITS / 2));
high = high.wrapping_add(carry0.into());
high = high.wrapping_add(carry1.into());
high = high.wrapping_add(carry2.into());
high = high.wrapping_add(carry3.into());
(low, high)
}
}
impl CarryingMulAdd for i128 {
#[inline]
fn carrying_mul_add(
self,
mul: Self,
add: Self,
carry: Self,
) -> (Self::Unsigned, Self) {
let (low, high) = CarryingMulAdd::carrying_mul_add(
self as u128,
mul as u128,
carry as u128,
add as u128,
);
let high = high as Self;
(low, high)
}
}