use crate::core_type::D38;
impl<const SCALE: u32> D38<SCALE> {
#[inline]
#[must_use]
pub fn checked_mul(self, rhs: Self) -> Option<Self> {
crate::mg_divide::mul_div_pow10::<SCALE>(self.0, rhs.0).map(Self)
}
#[inline]
#[must_use]
pub fn wrapping_mul(self, rhs: Self) -> Self {
match crate::mg_divide::mul_div_pow10::<SCALE>(self.0, rhs.0) {
Some(q) => Self(q),
None => Self(
self.0
.wrapping_mul(rhs.0)
.wrapping_div(Self::multiplier()),
),
}
}
#[inline]
#[must_use]
pub fn saturating_mul(self, rhs: Self) -> Self {
if let Some(q) = crate::mg_divide::mul_div_pow10::<SCALE>(self.0, rhs.0) { Self(q) } else {
let neg = (self.0 < 0) ^ (rhs.0 < 0);
if neg { Self::MIN } else { Self::MAX }
}
}
#[inline]
#[must_use]
pub fn overflowing_mul(self, rhs: Self) -> (Self, bool) {
match crate::mg_divide::mul_div_pow10::<SCALE>(self.0, rhs.0) {
Some(q) => (Self(q), false),
None => (self.wrapping_mul(rhs), true),
}
}
#[inline]
#[must_use]
pub fn checked_div(self, rhs: Self) -> Option<Self> {
crate::mg_divide::div_pow10_div::<SCALE>(self.0, rhs.0).map(Self)
}
#[inline]
#[must_use]
pub fn wrapping_div(self, rhs: Self) -> Self {
assert!(rhs.0 != 0, "attempt to divide by zero");
match crate::mg_divide::div_pow10_div::<SCALE>(self.0, rhs.0) {
Some(q) => Self(q),
None => Self(
self.0
.wrapping_mul(Self::multiplier())
.wrapping_div(rhs.0),
),
}
}
#[inline]
#[must_use]
pub fn saturating_div(self, rhs: Self) -> Self {
assert!(rhs.0 != 0, "attempt to divide by zero");
if let Some(q) = crate::mg_divide::div_pow10_div::<SCALE>(self.0, rhs.0) { Self(q) } else {
let neg = (self.0 < 0) ^ (rhs.0 < 0);
if neg { Self::MIN } else { Self::MAX }
}
}
#[inline]
#[must_use]
pub fn overflowing_div(self, rhs: Self) -> (Self, bool) {
assert!(rhs.0 != 0, "attempt to divide by zero");
match crate::mg_divide::div_pow10_div::<SCALE>(self.0, rhs.0) {
Some(q) => (Self(q), false),
None => (self.wrapping_div(rhs), true),
}
}
}
#[cfg(test)]
#[allow(clippy::arithmetic_side_effects)]
mod tests {
use crate::core_type::{D38, D38s12};
fn neg_one() -> D38s12 {
-D38s12::ONE
}
fn two() -> D38s12 {
D38s12::from_bits(2_000_000_000_000)
}
fn three() -> D38s12 {
D38s12::from_bits(3_000_000_000_000)
}
#[test]
fn checked_add_normal() {
assert_eq!(D38s12::ONE.checked_add(D38s12::ONE), Some(two()));
}
#[test]
fn checked_add_overflow_returns_none() {
assert_eq!(D38s12::MAX.checked_add(D38s12::ONE), None);
assert_eq!(
D38s12::MAX.checked_add(D38s12::from_bits(1)),
None
);
}
#[test]
fn checked_add_negative_overflow_returns_none() {
assert_eq!(D38s12::MIN.checked_add(neg_one()), None);
assert_eq!(
D38s12::MIN.checked_add(D38s12::from_bits(-1)),
None
);
}
#[test]
fn wrapping_add_normal_matches_op() {
assert_eq!(D38s12::ONE.wrapping_add(D38s12::ONE), two());
}
#[test]
fn wrapping_add_overflow_wraps_to_min() {
assert_eq!(
D38s12::MAX.wrapping_add(D38s12::from_bits(1)),
D38s12::MIN
);
}
#[test]
fn wrapping_add_negative_overflow_wraps_to_max() {
assert_eq!(
D38s12::MIN.wrapping_add(D38s12::from_bits(-1)),
D38s12::MAX
);
}
#[test]
fn saturating_add_normal_matches_op() {
assert_eq!(D38s12::ONE.saturating_add(D38s12::ONE), two());
}
#[test]
fn saturating_add_overflow_clamps_to_max() {
assert_eq!(D38s12::MAX.saturating_add(D38s12::ONE), D38s12::MAX);
}
#[test]
fn saturating_add_negative_overflow_clamps_to_min() {
assert_eq!(D38s12::MIN.saturating_add(neg_one()), D38s12::MIN);
}
#[test]
fn overflowing_add_normal_no_overflow() {
assert_eq!(
D38s12::ONE.overflowing_add(D38s12::ONE),
(two(), false)
);
}
#[test]
fn overflowing_add_overflow_flagged() {
assert_eq!(
D38s12::MAX.overflowing_add(D38s12::from_bits(1)),
(D38s12::MIN, true)
);
}
#[test]
fn overflowing_add_negative_overflow_flagged() {
assert_eq!(
D38s12::MIN.overflowing_add(D38s12::from_bits(-1)),
(D38s12::MAX, true)
);
}
#[test]
fn checked_sub_normal() {
assert_eq!(three().checked_sub(D38s12::ONE), Some(two()));
}
#[test]
fn checked_sub_underflow_returns_none() {
assert_eq!(D38s12::MIN.checked_sub(D38s12::ONE), None);
}
#[test]
fn checked_sub_positive_overflow_returns_none() {
assert_eq!(D38s12::MAX.checked_sub(neg_one()), None);
}
#[test]
fn wrapping_sub_normal() {
assert_eq!(three().wrapping_sub(D38s12::ONE), two());
}
#[test]
fn wrapping_sub_underflow_wraps_to_max() {
assert_eq!(
D38s12::MIN.wrapping_sub(D38s12::from_bits(1)),
D38s12::MAX
);
}
#[test]
fn saturating_sub_normal() {
assert_eq!(three().saturating_sub(D38s12::ONE), two());
}
#[test]
fn saturating_sub_underflow_clamps_to_min() {
assert_eq!(D38s12::MIN.saturating_sub(D38s12::ONE), D38s12::MIN);
}
#[test]
fn saturating_sub_overflow_clamps_to_max() {
assert_eq!(D38s12::MAX.saturating_sub(neg_one()), D38s12::MAX);
}
#[test]
fn overflowing_sub_normal() {
assert_eq!(
three().overflowing_sub(D38s12::ONE),
(two(), false)
);
}
#[test]
fn overflowing_sub_underflow_flagged() {
assert_eq!(
D38s12::MIN.overflowing_sub(D38s12::from_bits(1)),
(D38s12::MAX, true)
);
}
#[test]
fn checked_neg_normal() {
assert_eq!(D38s12::ONE.checked_neg(), Some(neg_one()));
assert_eq!(neg_one().checked_neg(), Some(D38s12::ONE));
assert_eq!(D38s12::ZERO.checked_neg(), Some(D38s12::ZERO));
}
#[test]
fn checked_neg_min_returns_none() {
assert_eq!(D38s12::MIN.checked_neg(), None);
}
#[test]
fn checked_neg_max_succeeds() {
let neg_max = D38s12::from_bits(-i128::MAX);
assert_eq!(D38s12::MAX.checked_neg(), Some(neg_max));
}
#[test]
fn wrapping_neg_normal() {
assert_eq!(D38s12::ONE.wrapping_neg(), neg_one());
assert_eq!(D38s12::ZERO.wrapping_neg(), D38s12::ZERO);
}
#[test]
fn wrapping_neg_min_returns_min() {
assert_eq!(D38s12::MIN.wrapping_neg(), D38s12::MIN);
}
#[test]
fn saturating_neg_normal() {
assert_eq!(D38s12::ONE.saturating_neg(), neg_one());
assert_eq!(D38s12::ZERO.saturating_neg(), D38s12::ZERO);
}
#[test]
fn saturating_neg_min_returns_max() {
assert_eq!(D38s12::MIN.saturating_neg(), D38s12::MAX);
}
#[test]
fn overflowing_neg_normal() {
assert_eq!(
D38s12::ONE.overflowing_neg(),
(neg_one(), false)
);
assert_eq!(
D38s12::ZERO.overflowing_neg(),
(D38s12::ZERO, false)
);
}
#[test]
fn overflowing_neg_min_flagged() {
assert_eq!(
D38s12::MIN.overflowing_neg(),
(D38s12::MIN, true)
);
}
#[test]
fn checked_mul_normal() {
let half = D38s12::from_bits(500_000_000_000);
let quarter = D38s12::from_bits(250_000_000_000);
assert_eq!(half.checked_mul(half), Some(quarter));
}
#[test]
fn checked_mul_zero() {
assert_eq!(D38s12::MAX.checked_mul(D38s12::ZERO), Some(D38s12::ZERO));
assert_eq!(D38s12::ZERO.checked_mul(D38s12::ZERO), Some(D38s12::ZERO));
}
#[test]
fn checked_mul_one_identity() {
let v = D38s12::from_bits(7_500_000_000_000); assert_eq!(v.checked_mul(D38s12::ONE), Some(v));
assert_eq!(D38s12::ONE.checked_mul(v), Some(v));
}
#[test]
fn checked_mul_overflow_returns_none() {
assert_eq!(D38s12::MAX.checked_mul(two()), None);
}
#[test]
fn checked_mul_min_overflow_returns_none() {
assert_eq!(D38s12::MIN.checked_mul(two()), None);
}
#[test]
fn wrapping_mul_normal() {
let half = D38s12::from_bits(500_000_000_000);
let quarter = D38s12::from_bits(250_000_000_000);
assert_eq!(half.wrapping_mul(half), quarter);
}
#[test]
fn wrapping_mul_overflow_does_not_panic() {
let _ = D38s12::MAX.wrapping_mul(two());
let _ = D38s12::MIN.wrapping_mul(two());
}
#[test]
fn saturating_mul_normal() {
let half = D38s12::from_bits(500_000_000_000);
let quarter = D38s12::from_bits(250_000_000_000);
assert_eq!(half.saturating_mul(half), quarter);
}
#[test]
fn saturating_mul_positive_overflow_clamps_to_max() {
assert_eq!(D38s12::MAX.saturating_mul(two()), D38s12::MAX);
}
#[test]
fn saturating_mul_negative_overflow_clamps_to_min() {
assert_eq!(
D38s12::MAX.saturating_mul(-two()),
D38s12::MIN
);
}
#[test]
fn saturating_mul_min_times_two_clamps_to_min() {
assert_eq!(D38s12::MIN.saturating_mul(two()), D38s12::MIN);
}
#[test]
fn saturating_mul_min_times_neg_two_clamps_to_max() {
assert_eq!(D38s12::MIN.saturating_mul(-two()), D38s12::MAX);
}
#[test]
fn overflowing_mul_normal_no_overflow() {
let half = D38s12::from_bits(500_000_000_000);
let quarter = D38s12::from_bits(250_000_000_000);
assert_eq!(half.overflowing_mul(half), (quarter, false));
}
#[test]
fn overflowing_mul_overflow_flagged() {
let (_, ovf) = D38s12::MAX.overflowing_mul(two());
assert!(ovf);
}
#[test]
fn checked_div_normal() {
let six = D38s12::from_bits(6_000_000_000_000);
assert_eq!(six.checked_div(two()), Some(three()));
}
#[test]
fn checked_div_by_zero_returns_none() {
assert_eq!(D38s12::ONE.checked_div(D38s12::ZERO), None);
}
#[test]
fn checked_div_overflow_returns_none() {
let half = D38s12::from_bits(500_000_000_000);
assert_eq!(D38s12::MAX.checked_div(half), None);
}
#[test]
fn checked_div_negative_normal() {
let neg_six = D38s12::from_bits(-6_000_000_000_000);
assert_eq!(neg_six.checked_div(two()), Some(-three()));
}
#[test]
fn wrapping_div_normal() {
let six = D38s12::from_bits(6_000_000_000_000);
assert_eq!(six.wrapping_div(two()), three());
}
#[test]
#[should_panic(expected = "attempt to divide by zero")]
fn wrapping_div_by_zero_panics() {
let _ = D38s12::ONE.wrapping_div(D38s12::ZERO);
}
#[test]
fn wrapping_div_overflow_does_not_panic() {
let half = D38s12::from_bits(500_000_000_000);
let _ = D38s12::MAX.wrapping_div(half);
}
#[test]
fn saturating_div_normal() {
let six = D38s12::from_bits(6_000_000_000_000);
assert_eq!(six.saturating_div(two()), three());
}
#[test]
#[should_panic(expected = "attempt to divide by zero")]
fn saturating_div_by_zero_panics() {
let _ = D38s12::ONE.saturating_div(D38s12::ZERO);
}
#[test]
fn saturating_div_overflow_clamps_to_max() {
let half = D38s12::from_bits(500_000_000_000);
assert_eq!(D38s12::MAX.saturating_div(half), D38s12::MAX);
}
#[test]
fn saturating_div_negative_overflow_clamps_to_min() {
let neg_half = D38s12::from_bits(-500_000_000_000);
assert_eq!(
D38s12::MAX.saturating_div(neg_half),
D38s12::MIN
);
}
#[test]
fn overflowing_div_normal() {
let six = D38s12::from_bits(6_000_000_000_000);
assert_eq!(six.overflowing_div(two()), (three(), false));
}
#[test]
fn overflowing_div_overflow_flagged() {
let half = D38s12::from_bits(500_000_000_000);
let (_, ovf) = D38s12::MAX.overflowing_div(half);
assert!(ovf);
}
#[test]
#[should_panic(expected = "attempt to divide by zero")]
fn overflowing_div_by_zero_panics() {
let _ = D38s12::ONE.overflowing_div(D38s12::ZERO);
}
#[test]
fn checked_rem_normal() {
let a = D38s12::from_bits(5_500_000_000_000);
let expected = D38s12::from_bits(1_500_000_000_000);
assert_eq!(a.checked_rem(two()), Some(expected));
}
#[test]
fn checked_rem_by_zero_returns_none() {
assert_eq!(D38s12::ONE.checked_rem(D38s12::ZERO), None);
}
#[test]
fn checked_rem_min_neg_one_lsb_returns_none() {
let neg_one_lsb = D38s12::from_bits(-1);
assert_eq!(D38s12::MIN.checked_rem(neg_one_lsb), None);
}
#[test]
fn wrapping_rem_normal() {
let a = D38s12::from_bits(5_500_000_000_000);
let expected = D38s12::from_bits(1_500_000_000_000);
assert_eq!(a.wrapping_rem(two()), expected);
}
#[test]
#[should_panic(expected = "attempt to calculate the remainder with a divisor of zero")]
fn wrapping_rem_by_zero_panics() {
let _ = D38s12::ONE.wrapping_rem(D38s12::ZERO);
}
#[test]
fn wrapping_rem_min_neg_one_lsb_returns_zero() {
let neg_one_lsb = D38s12::from_bits(-1);
assert_eq!(
D38s12::MIN.wrapping_rem(neg_one_lsb),
D38s12::ZERO
);
}
#[test]
fn overflowing_rem_normal() {
let a = D38s12::from_bits(5_500_000_000_000);
let expected = D38s12::from_bits(1_500_000_000_000);
assert_eq!(a.overflowing_rem(two()), (expected, false));
}
#[test]
fn overflowing_rem_min_neg_one_lsb_flagged() {
let neg_one_lsb = D38s12::from_bits(-1);
assert_eq!(
D38s12::MIN.overflowing_rem(neg_one_lsb),
(D38s12::ZERO, true)
);
}
#[test]
fn variants_at_scale_6() {
type D6 = D38<6>;
let one = D6::ONE;
let two_d6 = D6::from_bits(2_000_000); let one_lsb = D6::from_bits(1);
assert_eq!(one.checked_add(one), Some(two_d6));
assert_eq!(D6::MAX.checked_add(one_lsb), None);
assert_eq!(D6::MAX.saturating_add(one_lsb), D6::MAX);
assert_eq!(D6::MAX.wrapping_add(one_lsb), D6::MIN);
assert_eq!(
D6::MAX.overflowing_add(one_lsb),
(D6::MIN, true)
);
assert_eq!(D6::MIN.checked_neg(), None);
assert_eq!(D6::MIN.wrapping_neg(), D6::MIN);
assert_eq!(D6::MIN.saturating_neg(), D6::MAX);
}
#[test]
fn checked_matches_op_in_range() {
let a = D38s12::from_bits(7_500_000_000_000); let b = two();
assert_eq!(a.checked_add(b), Some(a + b));
assert_eq!(a.checked_sub(b), Some(a - b));
assert_eq!(a.checked_mul(b), Some(a * b));
assert_eq!(a.checked_div(b), Some(a / b));
assert_eq!(a.checked_rem(b), Some(a % b));
}
#[test]
fn overflowing_flag_matches_checked_none() {
let (_, ovf) = D38s12::MAX.overflowing_add(D38s12::ONE);
assert_eq!(ovf, D38s12::MAX.checked_add(D38s12::ONE).is_none());
let (_, ovf) = D38s12::MIN.overflowing_sub(D38s12::ONE);
assert_eq!(ovf, D38s12::MIN.checked_sub(D38s12::ONE).is_none());
let (_, ovf) = D38s12::MAX.overflowing_mul(two());
assert_eq!(ovf, D38s12::MAX.checked_mul(two()).is_none());
let (_, ovf) = D38s12::MIN.overflowing_neg();
assert_eq!(ovf, D38s12::MIN.checked_neg().is_none());
let neg_one_lsb = D38s12::from_bits(-1);
let (_, ovf) = D38s12::MIN.overflowing_rem(neg_one_lsb);
assert_eq!(
ovf,
D38s12::MIN.checked_rem(neg_one_lsb).is_none()
);
}
#[test]
fn saturating_never_escapes_bounds() {
let extremes = [
D38s12::MIN,
D38s12::from_bits(-1),
D38s12::ZERO,
D38s12::ONE,
D38s12::MAX,
];
for &a in &extremes {
for &b in &extremes {
let s_add = a.saturating_add(b);
let s_sub = a.saturating_sub(b);
let s_mul = a.saturating_mul(b);
assert!(s_add >= D38s12::MIN && s_add <= D38s12::MAX);
assert!(s_sub >= D38s12::MIN && s_sub <= D38s12::MAX);
assert!(s_mul >= D38s12::MIN && s_mul <= D38s12::MAX);
}
}
}
}