1use crate::{decimal::CalculationResult, ops, Decimal};
5use core::ops::{Add, Div, Mul, Rem, Sub};
6use num_traits::{CheckedAdd, CheckedDiv, CheckedMul, CheckedRem, CheckedSub, Inv};
7
8#[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
104macro_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#[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#[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}