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