1use 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}