rust_decimal/
arithmetic_impls.rs

1// #[rustfmt::skip] is being used because `rustfmt` poorly formats `#[doc = concat!(..)]`. See
2// https://github.com/rust-lang/rustfmt/issues/5062 for more information.
3
4use crate::{decimal::CalculationResult, ops, Decimal};
5use core::ops::{Add, Div, Mul, Rem, Sub};
6use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedRem, CheckedSub, Inv};
7
8// Macros and `Decimal` implementations
9
10#[rustfmt::skip]
11macro_rules! impl_checked {
12    ($long:literal, $short:literal, $fun:ident, $impl:ident) => {
13        #[doc = concat!(
14            "Checked ",
15            $long,
16            ". Computes `self ",
17            $short,
18            " other`, returning `None` if overflow occurred."
19        )]
20        #[inline(always)]
21        #[must_use]
22        pub fn $fun(self, other: Decimal) -> Option<Decimal> {
23            match ops::$impl(&self, &other) {
24                CalculationResult::Ok(result) => Some(result),
25                _ => None,
26            }
27        }
28    };
29}
30
31#[rustfmt::skip]
32macro_rules! impl_saturating {
33    ($long:literal, $short:literal, $fun:ident, $impl:ident, $cmp:ident) => {
34        #[doc = concat!(
35            "Saturating ",
36            $long,
37            ". Computes `self ",
38            $short,
39            " other`, saturating at the relevant upper or lower boundary.",
40        )]
41        #[inline(always)]
42        #[must_use]
43        pub fn $fun(self, other: Decimal) -> Decimal {
44            if let Some(elem) = self.$impl(other) {
45                elem
46            } else {
47                $cmp(&self, &other)
48            }
49        }
50    };
51}
52
53macro_rules! impl_checked_and_saturating {
54    (
55        $op_long:literal,
56        $op_short:literal,
57        $checked_fun:ident,
58        $checked_impl:ident,
59
60        $saturating_fun:ident,
61        $saturating_cmp:ident
62    ) => {
63        impl_checked!($op_long, $op_short, $checked_fun, $checked_impl);
64        impl_saturating!(
65            $op_long,
66            $op_short,
67            $saturating_fun,
68            $checked_fun,
69            $saturating_cmp
70        );
71    };
72}
73
74impl Decimal {
75    impl_checked_and_saturating!(
76        "addition",
77        "+",
78        checked_add,
79        add_impl,
80        saturating_add,
81        if_a_is_positive_then_max
82    );
83    impl_checked_and_saturating!(
84        "multiplication",
85        "*",
86        checked_mul,
87        mul_impl,
88        saturating_mul,
89        if_xnor_then_max
90    );
91    impl_checked_and_saturating!(
92        "subtraction",
93        "-",
94        checked_sub,
95        sub_impl,
96        saturating_sub,
97        if_a_is_positive_then_max
98    );
99
100    impl_checked!("division", "/", checked_div, div_impl);
101    impl_checked!("remainder", "%", checked_rem, rem_impl);
102}
103
104// Macros and trait implementations
105
106macro_rules! forward_all_binop {
107    (impl $imp:ident for $res:ty, $method:ident) => {
108        forward_val_val_binop!(impl $imp for $res, $method);
109        forward_ref_val_binop!(impl $imp for $res, $method);
110        forward_val_ref_binop!(impl $imp for $res, $method);
111    };
112}
113
114macro_rules! forward_ref_val_binop {
115    (impl $imp:ident for $res:ty, $method:ident) => {
116        impl<'a> $imp<$res> for &'a $res {
117            type Output = $res;
118
119            #[inline]
120            fn $method(self, other: $res) -> $res {
121                self.$method(&other)
122            }
123        }
124    };
125}
126
127macro_rules! forward_val_ref_binop {
128    (impl $imp:ident for $res:ty, $method:ident) => {
129        impl<'a> $imp<&'a $res> for $res {
130            type Output = $res;
131
132            #[inline]
133            fn $method(self, other: &$res) -> $res {
134                (&self).$method(other)
135            }
136        }
137    };
138}
139
140macro_rules! forward_val_val_binop {
141    (impl $imp:ident for $res:ty, $method:ident) => {
142        impl $imp<$res> for $res {
143            type Output = $res;
144
145            #[inline]
146            fn $method(self, other: $res) -> $res {
147                (&self).$method(&other)
148            }
149        }
150    };
151}
152
153forward_all_binop!(impl Add for Decimal, add);
154impl Add<&Decimal> for &Decimal {
155    type Output = Decimal;
156
157    #[inline(always)]
158    fn add(self, other: &Decimal) -> Decimal {
159        match ops::add_impl(self, other) {
160            CalculationResult::Ok(sum) => sum,
161            _ => panic!("Addition overflowed"),
162        }
163    }
164}
165
166impl CheckedAdd for Decimal {
167    #[inline]
168    fn checked_add(&self, v: &Decimal) -> Option<Decimal> {
169        Decimal::checked_add(*self, *v)
170    }
171}
172
173impl CheckedSub for Decimal {
174    #[inline]
175    fn checked_sub(&self, v: &Decimal) -> Option<Decimal> {
176        Decimal::checked_sub(*self, *v)
177    }
178}
179
180impl CheckedMul for Decimal {
181    #[inline]
182    fn checked_mul(&self, v: &Decimal) -> Option<Decimal> {
183        Decimal::checked_mul(*self, *v)
184    }
185}
186
187impl CheckedDiv for Decimal {
188    #[inline]
189    fn checked_div(&self, v: &Decimal) -> Option<Decimal> {
190        Decimal::checked_div(*self, *v)
191    }
192}
193
194impl CheckedRem for Decimal {
195    #[inline]
196    fn checked_rem(&self, v: &Decimal) -> Option<Decimal> {
197        Decimal::checked_rem(*self, *v)
198    }
199}
200
201impl Inv for Decimal {
202    type Output = Self;
203
204    #[inline]
205    fn inv(self) -> Self {
206        Decimal::ONE / self
207    }
208}
209
210forward_all_binop!(impl Div for Decimal, div);
211impl Div<&Decimal> for &Decimal {
212    type Output = Decimal;
213
214    #[inline]
215    fn div(self, other: &Decimal) -> Decimal {
216        match ops::div_impl(self, other) {
217            CalculationResult::Ok(quot) => quot,
218            CalculationResult::Overflow => panic!("Division overflowed"),
219            CalculationResult::DivByZero => panic!("Division by zero"),
220        }
221    }
222}
223
224forward_all_binop!(impl Mul for Decimal, mul);
225impl Mul<&Decimal> for &Decimal {
226    type Output = Decimal;
227
228    #[inline]
229    fn mul(self, other: &Decimal) -> Decimal {
230        match ops::mul_impl(self, other) {
231            CalculationResult::Ok(prod) => prod,
232            _ => panic!("Multiplication overflowed"),
233        }
234    }
235}
236
237forward_all_binop!(impl Rem for Decimal, rem);
238impl Rem<&Decimal> for &Decimal {
239    type Output = Decimal;
240
241    #[inline]
242    fn rem(self, other: &Decimal) -> Decimal {
243        match ops::rem_impl(self, other) {
244            CalculationResult::Ok(rem) => rem,
245            CalculationResult::Overflow => panic!("Division overflowed"),
246            CalculationResult::DivByZero => panic!("Division by zero"),
247        }
248    }
249}
250
251forward_all_binop!(impl Sub for Decimal, sub);
252impl Sub<&Decimal> for &Decimal {
253    type Output = Decimal;
254
255    #[inline(always)]
256    fn sub(self, other: &Decimal) -> Decimal {
257        match ops::sub_impl(self, other) {
258            CalculationResult::Ok(sum) => sum,
259            _ => panic!("Subtraction overflowed"),
260        }
261    }
262}
263
264// This function signature is expected by `impl_saturating`, thus the reason of `_b`.
265#[inline(always)]
266const fn if_a_is_positive_then_max(a: &Decimal, _b: &Decimal) -> Decimal {
267    if a.is_sign_positive() {
268        Decimal::MAX
269    } else {
270        Decimal::MIN
271    }
272}
273
274// Used by saturating multiplications.
275//
276// If the `a` and `b` combination represents a XNOR bit operation, returns MAX. Otherwise,
277// returns MIN.
278#[inline(always)]
279const fn if_xnor_then_max(a: &Decimal, b: &Decimal) -> Decimal {
280    match (a.is_sign_positive(), b.is_sign_positive()) {
281        (true, true) => Decimal::MAX,
282        (true, false) => Decimal::MIN,
283        (false, true) => Decimal::MIN,
284        (false, false) => Decimal::MAX,
285    }
286}
287
288#[cfg(test)]
289mod tests {
290    use crate::Decimal;
291
292    #[test]
293    fn checked_methods_have_correct_output() {
294        assert_eq!(Decimal::MAX.checked_add(Decimal::MAX), None);
295        assert_eq!(Decimal::MAX.checked_add(Decimal::MIN), Some(Decimal::ZERO));
296        assert_eq!(Decimal::MAX.checked_div(Decimal::ZERO), None);
297        assert_eq!(Decimal::MAX.checked_mul(Decimal::MAX), None);
298        assert_eq!(Decimal::MAX.checked_mul(Decimal::MIN), None);
299        assert_eq!(Decimal::MAX.checked_rem(Decimal::ZERO), None);
300        assert_eq!(Decimal::MAX.checked_sub(Decimal::MAX), Some(Decimal::ZERO));
301        assert_eq!(Decimal::MAX.checked_sub(Decimal::MIN), None);
302
303        assert_eq!(Decimal::MIN.checked_add(Decimal::MAX), Some(Decimal::ZERO));
304        assert_eq!(Decimal::MIN.checked_add(Decimal::MIN), None);
305        assert_eq!(Decimal::MIN.checked_div(Decimal::ZERO), None);
306        assert_eq!(Decimal::MIN.checked_mul(Decimal::MAX), None);
307        assert_eq!(Decimal::MIN.checked_mul(Decimal::MIN), None);
308        assert_eq!(Decimal::MIN.checked_rem(Decimal::ZERO), None);
309        assert_eq!(Decimal::MIN.checked_sub(Decimal::MAX), None);
310        assert_eq!(Decimal::MIN.checked_sub(Decimal::MIN), Some(Decimal::ZERO));
311    }
312
313    #[test]
314    fn saturated_methods_have_correct_output() {
315        assert_eq!(Decimal::MAX.saturating_add(Decimal::MAX), Decimal::MAX);
316        assert_eq!(Decimal::MAX.saturating_add(Decimal::MIN), Decimal::ZERO);
317        assert_eq!(Decimal::MAX.saturating_mul(Decimal::MAX), Decimal::MAX);
318        assert_eq!(Decimal::MAX.saturating_mul(Decimal::MIN), Decimal::MIN);
319        assert_eq!(Decimal::MAX.saturating_sub(Decimal::MAX), Decimal::ZERO);
320        assert_eq!(Decimal::MAX.saturating_sub(Decimal::MIN), Decimal::MAX);
321
322        assert_eq!(Decimal::MIN.saturating_add(Decimal::MAX), Decimal::ZERO);
323        assert_eq!(Decimal::MIN.saturating_add(Decimal::MIN), Decimal::MIN);
324        assert_eq!(Decimal::MIN.saturating_mul(Decimal::MAX), Decimal::MIN);
325        assert_eq!(Decimal::MIN.saturating_mul(Decimal::MIN), Decimal::MAX);
326        assert_eq!(Decimal::MIN.saturating_sub(Decimal::MAX), Decimal::MIN);
327        assert_eq!(Decimal::MIN.saturating_sub(Decimal::MIN), Decimal::ZERO);
328    }
329}