rust_fixed_point_decimal/binops/
add_sub.rs

1// ---------------------------------------------------------------------------
2// Copyright:   (c) 2021 ff. Michael Amrhein (michael@adrhinum.de)
3// License:     This program is part of a larger application. For license
4//              details please read the file LICENSE.TXT provided together
5//              with the application.
6// ---------------------------------------------------------------------------
7// $Source: src/binops/add_sub.rs $
8// $Revision: 2021-09-30T18:03:50+02:00 $
9
10use std::{
11    cmp::Ordering,
12    ops::{Add, AddAssign, Sub, SubAssign},
13};
14
15use num::Zero;
16use rust_fixed_point_decimal_core::mul_pow_ten;
17
18use crate::{
19    binops::const_max_u8,
20    prec_constraints::{PrecLimitCheck, True},
21    Decimal, MAX_PREC,
22};
23
24impl<const P: u8> Zero for Decimal<P>
25where
26    PrecLimitCheck<{ P <= MAX_PREC }>: True,
27    Decimal<P>: Add<Output = Decimal<P>>,
28{
29    #[inline(always)]
30    fn zero() -> Self {
31        Self::ZERO
32    }
33
34    #[inline(always)]
35    fn is_zero(&self) -> bool {
36        self.coeff.is_zero()
37    }
38}
39
40#[cfg(test)]
41mod zero_tests {
42    use super::*;
43
44    #[test]
45    fn test_zero() {
46        assert!(Decimal::<0>::is_zero(&Decimal::<0>::zero()));
47        assert!(Decimal::<1>::is_zero(&Decimal::<1>::zero()));
48        assert!(Decimal::<2>::is_zero(&Decimal::<2>::zero()));
49        assert!(Decimal::<3>::is_zero(&Decimal::<3>::zero()));
50        assert!(Decimal::<4>::is_zero(&Decimal::<4>::zero()));
51        assert!(Decimal::<5>::is_zero(&Decimal::<5>::zero()));
52        assert!(Decimal::<6>::is_zero(&Decimal::<6>::zero()));
53        assert!(Decimal::<7>::is_zero(&Decimal::<7>::zero()));
54        assert!(Decimal::<8>::is_zero(&Decimal::<8>::zero()));
55        assert!(Decimal::<9>::is_zero(&Decimal::<9>::zero()));
56    }
57}
58
59macro_rules! impl_add_sub_decimal {
60    (impl $imp:ident, $method:ident) => {
61        impl<const P: u8, const Q: u8> $imp<Decimal<Q>> for Decimal<P>
62        where
63            PrecLimitCheck<{ P <= MAX_PREC }>: True,
64            PrecLimitCheck<{ Q <= MAX_PREC }>: True,
65            PrecLimitCheck<{ const_max_u8(P, Q) <= MAX_PREC }>: True,
66        {
67            type Output = Decimal<{ const_max_u8(P, Q) }>;
68
69            #[inline(always)]
70            fn $method(self, other: Decimal<Q>) -> Self::Output {
71                match P.cmp(&Q) {
72                    Ordering::Equal => Self::Output {
73                        coeff: $imp::$method(self.coeff, other.coeff),
74                    },
75                    Ordering::Greater => Self::Output {
76                        coeff: $imp::$method(
77                            self.coeff,
78                            mul_pow_ten(other.coeff, P - Q),
79                        ),
80                    },
81                    Ordering::Less => Self::Output {
82                        coeff: $imp::$method(
83                            mul_pow_ten(self.coeff, Q - P),
84                            other.coeff,
85                        ),
86                    },
87                }
88            }
89        }
90
91        forward_ref_binop!(impl $imp, $method);
92    };
93}
94
95impl_add_sub_decimal!(impl Add, add);
96
97impl_add_sub_decimal!(impl Sub, sub);
98
99#[cfg(test)]
100mod add_sub_decimal_tests {
101    use super::*;
102
103    #[test]
104    fn test_add_same_prec() {
105        let x = Decimal::<3>::new_raw(1234567890);
106        let y = x + x;
107        assert_eq!(y.coeff, 2 * x.coeff);
108        let z = x + Decimal::<3>::NEG_ONE;
109        assert_eq!(z.coeff, x.coeff - 1000);
110    }
111
112    #[test]
113    fn test_add_different_prec() {
114        let x = Decimal::<5>::new_raw(1234567890);
115        let y = Decimal::<1>::new_raw(890);
116        let z = x + y;
117        assert_eq!(z.coeff, x.coeff + y.coeff * 10000);
118        let z = y + x;
119        assert_eq!(z.coeff, x.coeff + y.coeff * 10000);
120        let z = x + Decimal::<3>::NEG_ONE;
121        assert_eq!(z.coeff, x.coeff - Decimal::<5>::ONE.coeff);
122    }
123
124    #[test]
125    #[should_panic]
126    fn test_add_pos_overflow() {
127        let x = Decimal::<4>::new_raw(i128::MAX - 19999);
128        let _y = x + Decimal::<4>::TWO;
129    }
130
131    #[test]
132    #[should_panic]
133    fn test_add_neg_overflow() {
134        let x = Decimal::<2>::new_raw(i128::MIN + 99);
135        let _y = x + Decimal::<2>::NEG_ONE;
136    }
137
138    #[test]
139    #[allow(clippy::eq_op)]
140    fn test_sub_same_prec() {
141        let x = Decimal::<3>::new_raw(1234567890);
142        let y = x - x;
143        assert_eq!(y.coeff, 0);
144        let z = x - Decimal::<3>::NEG_ONE;
145        assert_eq!(z.coeff, x.coeff + 1000);
146    }
147
148    #[test]
149    fn test_sub_different_prec() {
150        let x = Decimal::<2>::new_raw(1234567890);
151        let y = Decimal::<1>::new_raw(890);
152        let z = x - y;
153        assert_eq!(z.coeff, x.coeff - y.coeff * 10);
154        let z = y - x;
155        assert_eq!(z.coeff, y.coeff * 10 - x.coeff);
156        let z = x - Decimal::<3>::NEG_ONE;
157        assert_eq!(z.coeff, x.coeff * 10 + Decimal::<3>::ONE.coeff);
158    }
159
160    #[test]
161    #[should_panic]
162    fn test_sub_pos_overflow() {
163        let x = Decimal::<0>::new_raw(i128::MIN + 10);
164        let _y = Decimal::<0>::TEN - x;
165    }
166
167    #[test]
168    #[should_panic]
169    fn test_sub_neg_overflow() {
170        let x = Decimal::<4>::new_raw(i128::MIN + 99999);
171        let _y = x - Decimal::<4>::TEN;
172    }
173
174    #[test]
175    fn test_add_ref() {
176        let x = Decimal::<3>::new_raw(12345);
177        let y = Decimal::<1>::new_raw(12345);
178        let z = x + y;
179        assert_eq!(z.coeff, (&x + y).coeff);
180        assert_eq!(z.coeff, (x + &y).coeff);
181        assert_eq!(z.coeff, (&x + &y).coeff);
182    }
183
184    #[test]
185    fn test_sub_ref() {
186        let x = Decimal::<3>::new_raw(12345);
187        let y = Decimal::<1>::new_raw(12345);
188        let z = x - y;
189        assert_eq!(z.coeff, (&x - y).coeff);
190        assert_eq!(z.coeff, (x - &y).coeff);
191        assert_eq!(z.coeff, (&x - &y).coeff);
192    }
193}
194
195macro_rules! impl_add_sub_decimal_and_int {
196    (impl $imp:ident, $method:ident) => {
197        impl_add_sub_decimal_and_int!(
198            impl $imp, $method, u8, i8, u16, i16, u32, i32, u64, i64, i128
199        );
200    };
201    (impl $imp:ident, $method:ident, $($t:ty),*) => {
202        $(
203        impl<const P: u8> $imp<$t> for Decimal<P>
204        where
205            PrecLimitCheck<{ P <= MAX_PREC }>: True,
206        {
207            type Output = Decimal<P>;
208
209            #[inline(always)]
210            fn $method(self, other: $t) -> Self::Output {
211                if P == 0 {
212                    Self::Output{
213                        coeff: $imp::$method(self.coeff, other as i128)
214                    }
215                } else {
216                    Self::Output{
217                        coeff: $imp::$method(self.coeff,
218                                             mul_pow_ten(other as i128, P))
219                    }
220                }
221            }
222        }
223
224        impl<const P: u8> $imp<Decimal<P>> for $t
225        where
226            PrecLimitCheck<{ P <= MAX_PREC }>: True,
227        {
228            type Output = Decimal<P>;
229
230            #[inline(always)]
231            fn $method(self, other: Decimal<P>) -> Self::Output {
232                if P == 0 {
233                    Self::Output{
234                        coeff: $imp::$method(self as i128, other.coeff)
235                    }
236                } else {
237                    Self::Output{
238                        coeff: $imp::$method(mul_pow_ten(self as i128, P),
239                                             other.coeff)
240                    }
241                }
242            }
243        }
244        )*
245    }
246}
247
248impl_add_sub_decimal_and_int!(impl Add, add);
249forward_ref_binop_decimal_int!(impl Add, add);
250
251impl_add_sub_decimal_and_int!(impl Sub, sub);
252forward_ref_binop_decimal_int!(impl Sub, sub);
253
254#[cfg(test)]
255mod add_sub_integer_tests {
256    use rust_fixed_point_decimal_core::ten_pow;
257
258    use super::*;
259
260    macro_rules! gen_add_integer_tests {
261        ($func:ident, $t:ty, $p:expr, $coeff:expr) => {
262            #[test]
263            fn $func() {
264                let d = Decimal::<$p>::new_raw($coeff);
265                let i = <$t>::MAX;
266                let r = d + i;
267                assert_eq!(r.precision(), d.precision());
268                assert_eq!(r.coeff, i as i128 * ten_pow($p) + $coeff);
269                assert_eq!(r.coeff, (&d + i).coeff);
270                assert_eq!(r.coeff, (d + &i).coeff);
271                assert_eq!(r.coeff, (&d + &i).coeff);
272                let z = i + d;
273                assert_eq!(z.precision(), r.precision());
274                assert_eq!(z.coeff, r.coeff);
275                assert_eq!(z.coeff, (&i + d).coeff);
276                assert_eq!(z.coeff, (i + &d).coeff);
277                assert_eq!(z.coeff, (&i + &d).coeff);
278            }
279        };
280    }
281
282    gen_add_integer_tests!(test_add_u8, u8, 2, 1);
283    gen_add_integer_tests!(test_add_i8, i8, 0, 123);
284    gen_add_integer_tests!(test_add_u16, u16, 4, 11);
285    gen_add_integer_tests!(test_add_i16, i16, 4, 1234567);
286    gen_add_integer_tests!(test_add_u32, u32, 1, 0);
287    gen_add_integer_tests!(test_add_i32, i32, 9, 1234);
288    gen_add_integer_tests!(test_add_u64, u64, 3, 321);
289    gen_add_integer_tests!(test_add_i64, i64, 7, 12345678901234567890);
290
291    #[test]
292    fn test_add_i128() {
293        let d = Decimal::<2>::new_raw(1);
294        let i = 12345_i128;
295        let r = d + i;
296        assert_eq!(r.coeff, i * 100 + 1);
297        assert_eq!(r.coeff, (&d + i).coeff);
298        assert_eq!(r.coeff, (d + &i).coeff);
299        assert_eq!(r.coeff, (&d + &i).coeff);
300        let z = i + d;
301        assert_eq!(z.precision(), r.precision());
302        assert_eq!(z.coeff, r.coeff);
303        assert_eq!(z.coeff, (&i + d).coeff);
304        assert_eq!(z.coeff, (i + &d).coeff);
305        assert_eq!(z.coeff, (&i + &d).coeff);
306    }
307
308    macro_rules! gen_sub_integer_tests {
309        ($func:ident, $t:ty, $p:expr, $coeff:expr) => {
310            #[test]
311            fn $func() {
312                let d = Decimal::<$p>::new_raw($coeff);
313                let i = <$t>::MAX;
314                let r = d - i;
315                assert_eq!(r.precision(), d.precision());
316                assert_eq!(r.coeff, $coeff - i as i128 * ten_pow($p));
317                assert_eq!(r.coeff, (&d - i).coeff);
318                assert_eq!(r.coeff, (d - &i).coeff);
319                assert_eq!(r.coeff, (&d - &i).coeff);
320                let z = i - d;
321                assert_eq!(z.precision(), r.precision());
322                assert_eq!(z.coeff, i as i128 * ten_pow($p) - $coeff);
323                assert_eq!(z.coeff, (&i - d).coeff);
324                assert_eq!(z.coeff, (i - &d).coeff);
325                assert_eq!(z.coeff, (&i - &d).coeff);
326            }
327        };
328    }
329
330    gen_sub_integer_tests!(test_sub_u8, u8, 2, 1);
331    gen_sub_integer_tests!(test_sub_i8, i8, 0, 123);
332    gen_sub_integer_tests!(test_sub_u16, u16, 4, 11);
333    gen_sub_integer_tests!(test_sub_i16, i16, 4, 1234567);
334    gen_sub_integer_tests!(test_sub_u32, u32, 1, 0);
335    gen_sub_integer_tests!(test_sub_i32, i32, 9, 1234);
336    gen_sub_integer_tests!(test_sub_u64, u64, 3, 321);
337    gen_sub_integer_tests!(test_sub_i64, i64, 7, 12345678901234567890);
338
339    #[test]
340    fn test_sub_i128() {
341        let d = Decimal::<2>::new_raw(501);
342        let i = 12345_i128;
343        let r = d - i;
344        assert_eq!(r.coeff, -i * 100 + 501);
345        assert_eq!(r.coeff, (&d - i).coeff);
346        assert_eq!(r.coeff, (d - &i).coeff);
347        assert_eq!(r.coeff, (&d - &i).coeff);
348        let z = i - d;
349        assert_eq!(z.coeff, i * 100 - 501);
350        assert_eq!(z.coeff, (&i - d).coeff);
351        assert_eq!(z.coeff, (i - &d).coeff);
352        assert_eq!(z.coeff, (&i - &d).coeff);
353    }
354}
355
356forward_op_assign!(impl AddAssign, add_assign, Add, add);
357
358forward_op_assign!(impl SubAssign, sub_assign, Sub, sub);
359
360#[cfg(test)]
361mod add_sub_assign_tests {
362    use super::*;
363
364    #[test]
365    fn test_add_assign_decimal() {
366        let mut x = Decimal::<5>::new_raw(1234567);
367        x += Decimal::<4>::new_raw(1);
368        assert_eq!(x.coeff, 1234577);
369        x += Decimal::<0>::new_raw(88);
370        assert_eq!(x.coeff, 10034577);
371    }
372
373    #[test]
374    fn test_add_assign_int() {
375        let mut x = Decimal::<5>::new_raw(1234567);
376        x += 1_u32;
377        assert_eq!(x.coeff, 1334567);
378        x += -109_i8;
379        assert_eq!(x.coeff, -9565433);
380    }
381
382    #[test]
383    #[should_panic]
384    fn test_add_assign_pos_overflow() {
385        let mut x = Decimal::<4>::new_raw(i128::MAX - 19999);
386        x += Decimal::<4>::TWO;
387    }
388
389    #[test]
390    #[should_panic]
391    fn test_add_assign_neg_overflow() {
392        let mut x = Decimal::<2>::new_raw(i128::MIN + 99);
393        x += Decimal::<2>::NEG_ONE;
394    }
395
396    #[test]
397    fn test_sub_assign_decimal() {
398        let mut x = Decimal::<3>::new_raw(1234567);
399        x -= Decimal::<3>::new_raw(100);
400        assert_eq!(x.coeff, 1234467);
401        x -= Decimal::<0>::new_raw(1235);
402        assert_eq!(x.coeff, -533);
403    }
404
405    #[test]
406    fn test_sub_assign_int() {
407        let mut x = Decimal::<2>::new_raw(1234567);
408        x -= 1_u32;
409        assert_eq!(x.coeff, 1234467_i128);
410        x -= -109889_i128;
411        assert_eq!(x.coeff, 12223367_i128);
412    }
413
414    #[test]
415    #[should_panic]
416    fn test_sub_assign_neg_overflow() {
417        let mut x = Decimal::<4>::new_raw(i128::MIN + 99999);
418        x -= Decimal::<4>::TEN;
419    }
420}