diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/rust_decimal/src/arithmetic_impls.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/rust_decimal/src/arithmetic_impls.rs')
-rw-r--r-- | third_party/rust/rust_decimal/src/arithmetic_impls.rs | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/third_party/rust/rust_decimal/src/arithmetic_impls.rs b/third_party/rust/rust_decimal/src/arithmetic_impls.rs new file mode 100644 index 0000000000..81cfa305d2 --- /dev/null +++ b/third_party/rust/rust_decimal/src/arithmetic_impls.rs @@ -0,0 +1,329 @@ +// #[rustfmt::skip] is being used because `rustfmt` poorly formats `#[doc = concat!(..)]`. See +// https://github.com/rust-lang/rustfmt/issues/5062 for more information. + +use crate::{decimal::CalculationResult, ops, Decimal}; +use core::ops::{Add, Div, Mul, Rem, Sub}; +use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedRem, CheckedSub, Inv}; + +// Macros and `Decimal` implementations + +#[rustfmt::skip] +macro_rules! impl_checked { + ($long:literal, $short:literal, $fun:ident, $impl:ident) => { + #[doc = concat!( + "Checked ", + $long, + ". Computes `self ", + $short, + " other`, returning `None` if overflow occurred." + )] + #[inline(always)] + #[must_use] + pub fn $fun(self, other: Decimal) -> Option<Decimal> { + match ops::$impl(&self, &other) { + CalculationResult::Ok(result) => Some(result), + _ => None, + } + } + }; +} + +#[rustfmt::skip] +macro_rules! impl_saturating { + ($long:literal, $short:literal, $fun:ident, $impl:ident, $cmp:ident) => { + #[doc = concat!( + "Saturating ", + $long, + ". Computes `self ", + $short, + " other`, saturating at the relevant upper or lower boundary.", + )] + #[inline(always)] + #[must_use] + pub fn $fun(self, other: Decimal) -> Decimal { + if let Some(elem) = self.$impl(other) { + elem + } else { + $cmp(&self, &other) + } + } + }; +} + +macro_rules! impl_checked_and_saturating { + ( + $op_long:literal, + $op_short:literal, + $checked_fun:ident, + $checked_impl:ident, + + $saturating_fun:ident, + $saturating_cmp:ident + ) => { + impl_checked!($op_long, $op_short, $checked_fun, $checked_impl); + impl_saturating!( + $op_long, + $op_short, + $saturating_fun, + $checked_fun, + $saturating_cmp + ); + }; +} + +impl Decimal { + impl_checked_and_saturating!( + "addition", + "+", + checked_add, + add_impl, + saturating_add, + if_a_is_positive_then_max + ); + impl_checked_and_saturating!( + "multiplication", + "*", + checked_mul, + mul_impl, + saturating_mul, + if_xnor_then_max + ); + impl_checked_and_saturating!( + "subtraction", + "-", + checked_sub, + sub_impl, + saturating_sub, + if_a_is_positive_then_max + ); + + impl_checked!("division", "/", checked_div, div_impl); + impl_checked!("remainder", "%", checked_rem, rem_impl); +} + +// Macros and trait implementations + +macro_rules! forward_all_binop { + (impl $imp:ident for $res:ty, $method:ident) => { + forward_val_val_binop!(impl $imp for $res, $method); + forward_ref_val_binop!(impl $imp for $res, $method); + forward_val_ref_binop!(impl $imp for $res, $method); + }; +} + +macro_rules! forward_ref_val_binop { + (impl $imp:ident for $res:ty, $method:ident) => { + impl<'a> $imp<$res> for &'a $res { + type Output = $res; + + #[inline] + fn $method(self, other: $res) -> $res { + self.$method(&other) + } + } + }; +} + +macro_rules! forward_val_ref_binop { + (impl $imp:ident for $res:ty, $method:ident) => { + impl<'a> $imp<&'a $res> for $res { + type Output = $res; + + #[inline] + fn $method(self, other: &$res) -> $res { + (&self).$method(other) + } + } + }; +} + +macro_rules! forward_val_val_binop { + (impl $imp:ident for $res:ty, $method:ident) => { + impl $imp<$res> for $res { + type Output = $res; + + #[inline] + fn $method(self, other: $res) -> $res { + (&self).$method(&other) + } + } + }; +} + +forward_all_binop!(impl Add for Decimal, add); +impl<'a, 'b> Add<&'b Decimal> for &'a Decimal { + type Output = Decimal; + + #[inline(always)] + fn add(self, other: &Decimal) -> Decimal { + match ops::add_impl(self, other) { + CalculationResult::Ok(sum) => sum, + _ => panic!("Addition overflowed"), + } + } +} + +impl CheckedAdd for Decimal { + #[inline] + fn checked_add(&self, v: &Decimal) -> Option<Decimal> { + Decimal::checked_add(*self, *v) + } +} + +impl CheckedSub for Decimal { + #[inline] + fn checked_sub(&self, v: &Decimal) -> Option<Decimal> { + Decimal::checked_sub(*self, *v) + } +} + +impl CheckedMul for Decimal { + #[inline] + fn checked_mul(&self, v: &Decimal) -> Option<Decimal> { + Decimal::checked_mul(*self, *v) + } +} + +impl CheckedDiv for Decimal { + #[inline] + fn checked_div(&self, v: &Decimal) -> Option<Decimal> { + Decimal::checked_div(*self, *v) + } +} + +impl CheckedRem for Decimal { + #[inline] + fn checked_rem(&self, v: &Decimal) -> Option<Decimal> { + Decimal::checked_rem(*self, *v) + } +} + +impl Inv for Decimal { + type Output = Self; + + #[inline] + fn inv(self) -> Self { + Decimal::ONE / self + } +} + +forward_all_binop!(impl Div for Decimal, div); +impl<'a, 'b> Div<&'b Decimal> for &'a Decimal { + type Output = Decimal; + + #[inline] + fn div(self, other: &Decimal) -> Decimal { + match ops::div_impl(self, other) { + CalculationResult::Ok(quot) => quot, + CalculationResult::Overflow => panic!("Division overflowed"), + CalculationResult::DivByZero => panic!("Division by zero"), + } + } +} + +forward_all_binop!(impl Mul for Decimal, mul); +impl<'a, 'b> Mul<&'b Decimal> for &'a Decimal { + type Output = Decimal; + + #[inline] + fn mul(self, other: &Decimal) -> Decimal { + match ops::mul_impl(self, other) { + CalculationResult::Ok(prod) => prod, + _ => panic!("Multiplication overflowed"), + } + } +} + +forward_all_binop!(impl Rem for Decimal, rem); +impl<'a, 'b> Rem<&'b Decimal> for &'a Decimal { + type Output = Decimal; + + #[inline] + fn rem(self, other: &Decimal) -> Decimal { + match ops::rem_impl(self, other) { + CalculationResult::Ok(rem) => rem, + CalculationResult::Overflow => panic!("Division overflowed"), + CalculationResult::DivByZero => panic!("Division by zero"), + } + } +} + +forward_all_binop!(impl Sub for Decimal, sub); +impl<'a, 'b> Sub<&'b Decimal> for &'a Decimal { + type Output = Decimal; + + #[inline(always)] + fn sub(self, other: &Decimal) -> Decimal { + match ops::sub_impl(self, other) { + CalculationResult::Ok(sum) => sum, + _ => panic!("Subtraction overflowed"), + } + } +} + +// This function signature is expected by `impl_saturating`, thus the reason of `_b`. +#[inline(always)] +const fn if_a_is_positive_then_max(a: &Decimal, _b: &Decimal) -> Decimal { + if a.is_sign_positive() { + Decimal::MAX + } else { + Decimal::MIN + } +} + +// Used by saturating multiplications. +// +// If the `a` and `b` combination represents a XNOR bit operation, returns MAX. Otherwise, +// returns MIN. +#[inline(always)] +const fn if_xnor_then_max(a: &Decimal, b: &Decimal) -> Decimal { + match (a.is_sign_positive(), b.is_sign_positive()) { + (true, true) => Decimal::MAX, + (true, false) => Decimal::MIN, + (false, true) => Decimal::MIN, + (false, false) => Decimal::MAX, + } +} + +#[cfg(test)] +mod tests { + use crate::Decimal; + + #[test] + fn checked_methods_have_correct_output() { + assert_eq!(Decimal::MAX.checked_add(Decimal::MAX), None); + assert_eq!(Decimal::MAX.checked_add(Decimal::MIN), Some(Decimal::ZERO)); + assert_eq!(Decimal::MAX.checked_div(Decimal::ZERO), None); + assert_eq!(Decimal::MAX.checked_mul(Decimal::MAX), None); + assert_eq!(Decimal::MAX.checked_mul(Decimal::MIN), None); + assert_eq!(Decimal::MAX.checked_rem(Decimal::ZERO), None); + assert_eq!(Decimal::MAX.checked_sub(Decimal::MAX), Some(Decimal::ZERO)); + assert_eq!(Decimal::MAX.checked_sub(Decimal::MIN), None); + + assert_eq!(Decimal::MIN.checked_add(Decimal::MAX), Some(Decimal::ZERO)); + assert_eq!(Decimal::MIN.checked_add(Decimal::MIN), None); + assert_eq!(Decimal::MIN.checked_div(Decimal::ZERO), None); + assert_eq!(Decimal::MIN.checked_mul(Decimal::MAX), None); + assert_eq!(Decimal::MIN.checked_mul(Decimal::MIN), None); + assert_eq!(Decimal::MIN.checked_rem(Decimal::ZERO), None); + assert_eq!(Decimal::MIN.checked_sub(Decimal::MAX), None); + assert_eq!(Decimal::MIN.checked_sub(Decimal::MIN), Some(Decimal::ZERO)); + } + + #[test] + fn saturated_methods_have_correct_output() { + assert_eq!(Decimal::MAX.saturating_add(Decimal::MAX), Decimal::MAX); + assert_eq!(Decimal::MAX.saturating_add(Decimal::MIN), Decimal::ZERO); + assert_eq!(Decimal::MAX.saturating_mul(Decimal::MAX), Decimal::MAX); + assert_eq!(Decimal::MAX.saturating_mul(Decimal::MIN), Decimal::MIN); + assert_eq!(Decimal::MAX.saturating_sub(Decimal::MAX), Decimal::ZERO); + assert_eq!(Decimal::MAX.saturating_sub(Decimal::MIN), Decimal::MAX); + + assert_eq!(Decimal::MIN.saturating_add(Decimal::MAX), Decimal::ZERO); + assert_eq!(Decimal::MIN.saturating_add(Decimal::MIN), Decimal::MIN); + assert_eq!(Decimal::MIN.saturating_mul(Decimal::MAX), Decimal::MIN); + assert_eq!(Decimal::MIN.saturating_mul(Decimal::MIN), Decimal::MAX); + assert_eq!(Decimal::MIN.saturating_sub(Decimal::MAX), Decimal::MIN); + assert_eq!(Decimal::MIN.saturating_sub(Decimal::MIN), Decimal::ZERO); + } +} |