rust_fixed_point_decimal/binops/
rem.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/rem.rs $
8// $Revision:  $
9
10use std::{
11    cmp::Ordering,
12    ops::{Rem, RemAssign, Sub},
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, DecimalError, MAX_PREC,
22};
23
24impl<const P: u8, const Q: u8> Rem<Decimal<Q>> for Decimal<P>
25where
26    PrecLimitCheck<{ P <= MAX_PREC }>: True,
27    PrecLimitCheck<{ Q <= MAX_PREC }>: True,
28    PrecLimitCheck<{ const_max_u8(P, Q) <= MAX_PREC }>: True,
29{
30    type Output = <Decimal<P> as Sub<Decimal<Q>>>::Output;
31
32    #[inline(always)]
33    fn rem(self, other: Decimal<Q>) -> Self::Output {
34        if other.eq_zero() {
35            panic!("{}", DecimalError::DivisionByZero);
36        }
37        match P.cmp(&Q) {
38            Ordering::Equal => Self::Output {
39                coeff: self.coeff % other.coeff,
40            },
41            Ordering::Greater => Self::Output {
42                coeff: self.coeff % mul_pow_ten(other.coeff, P - Q),
43            },
44            Ordering::Less => Self::Output {
45                coeff: mul_pow_ten(self.coeff, Q - P) % other.coeff,
46            },
47        }
48    }
49}
50
51forward_ref_binop!(impl Rem, rem);
52
53#[cfg(test)]
54mod rem_decimal_tests {
55    use super::*;
56
57    #[test]
58    fn test_rem_same_prec() {
59        let x = Decimal::<2>::new_raw(702);
60        let y = Decimal::<2>::new_raw(300);
61        let r = x % y;
62        assert_eq!(r.coeff, 102);
63        let x = Decimal::<2>::new_raw(702);
64        let y = Decimal::<2>::new_raw(-307);
65        let r = x % y;
66        assert_eq!(r.coeff, 88);
67        let x = Decimal::<2>::new_raw(-702);
68        let y = Decimal::<2>::new_raw(307);
69        let r = x % y;
70        assert_eq!(r.coeff, -88);
71    }
72
73    #[test]
74    fn test_rem_diff_prec() {
75        let x = Decimal::<3>::new_raw(702);
76        let y = Decimal::<2>::new_raw(300);
77        let r = x % y;
78        assert_eq!(r.coeff, 702);
79        let x = Decimal::<2>::new_raw(702);
80        let y = Decimal::<5>::new_raw(-307);
81        let r = x % y;
82        assert_eq!(r.coeff, 198);
83        let x = Decimal::<2>::new_raw(-702);
84        let y = Decimal::<4>::new_raw(307);
85        let r = x % y;
86        assert_eq!(r.coeff, -204);
87    }
88
89    #[test]
90    fn test_rem_by_one() {
91        let x = Decimal::<2>::new_raw(702);
92        let y = Decimal::<4>::ONE;
93        let r = x % y;
94        assert_eq!(r.coeff, x.fract().coeff * 100);
95        let x = Decimal::<4>::new_raw(70389032);
96        let y = Decimal::<2>::ONE;
97        let r = x % y;
98        assert_eq!(r.coeff, x.fract().coeff);
99    }
100}
101
102macro_rules! impl_rem_decimal_and_int {
103    () => {
104        impl_rem_decimal_and_int!(u8, i8, u16, i16, u32, i32, u64, i64, i128);
105    };
106    ($($t:ty),*) => {
107        $(
108        impl<const P: u8> Rem<$t> for Decimal<P>
109        where
110            PrecLimitCheck<{ P <= MAX_PREC }>: True,
111        {
112            type Output = Decimal<P>;
113
114            fn rem(self, other: $t) -> Self::Output {
115                if other.is_zero() {
116                    panic!("{}", DecimalError::DivisionByZero);
117                }
118                if P == 0 {
119                    Self::Output { coeff: self.coeff % other as i128 }
120                } else {
121                    Self::Output {
122                        coeff: self.coeff % mul_pow_ten(other as i128, P)
123                    }
124                }
125            }
126        }
127
128        impl<const P: u8> Rem<Decimal<P>> for $t
129        where
130            PrecLimitCheck<{ P <= MAX_PREC }>: True,
131        {
132            type Output = Decimal<P>;
133
134            fn rem(self, other: Decimal<P>) -> Self::Output {
135                if other.eq_zero() {
136                    panic!("{}", DecimalError::DivisionByZero);
137                }
138                if P == 0 {
139                    Self::Output { coeff: self as i128 % other.coeff }
140                } else {
141                    Self::Output {
142                        coeff: mul_pow_ten(self as i128, P) % other.coeff
143                    }
144                }
145            }
146        }
147        )*
148    }
149}
150
151impl_rem_decimal_and_int!();
152forward_ref_binop_decimal_int!(impl Rem, rem);
153
154#[cfg(test)]
155#[allow(clippy::neg_multiply)]
156mod rem_integer_tests {
157    use num::{One, Zero};
158
159    use super::*;
160
161    macro_rules! gen_rem_integer_tests {
162        ($func:ident, $t:ty, $p:expr, $coeff:expr) => {
163            #[test]
164            fn $func() {
165                let d = Decimal::<$p>::new_raw($coeff);
166                let i: $t = 127;
167                let c = mul_pow_ten(i as i128, $p);
168                let r = d % i;
169                assert_eq!(r.precision(), $p);
170                assert_eq!(r.coeff, $coeff - c * ($coeff / c));
171                assert_eq!(r.coeff, (&d % i).coeff);
172                assert_eq!(r.coeff, (d % &i).coeff);
173                assert_eq!(r.coeff, (&d % &i).coeff);
174                let z = i % d;
175                assert_eq!(z.precision(), $p);
176                assert_eq!(z.coeff, c - $coeff * (c / $coeff));
177                assert_eq!(z.coeff, (&i % d).coeff);
178                assert_eq!(z.coeff, (i % &d).coeff);
179                assert_eq!(z.coeff, (&i % &d).coeff);
180            }
181        };
182    }
183
184    gen_rem_integer_tests!(test_rem_u8, u8, 2, -1);
185    gen_rem_integer_tests!(test_rem_i8, i8, 0, 253);
186    gen_rem_integer_tests!(test_rem_u16, u16, 4, 804);
187    gen_rem_integer_tests!(test_rem_i16, i16, 4, 390625);
188    gen_rem_integer_tests!(test_rem_u32, u32, 1, 1014);
189    gen_rem_integer_tests!(test_rem_i32, i32, 9, -1000);
190    gen_rem_integer_tests!(test_rem_u64, u64, 3, 206);
191    gen_rem_integer_tests!(test_rem_i64, i64, 7, -488281250);
192    gen_rem_integer_tests!(test_rem_i128, i128, 2, 1526281250433765);
193
194    #[test]
195    fn test_rem_decimal_by_int_one() {
196        let x = Decimal::<5>::new_raw(17294738475);
197        let y = i64::one();
198        let z = x % y;
199        assert_eq!(z.coeff, x.fract().coeff);
200        let y = u8::one();
201        let z = x % y;
202        assert_eq!(z.coeff, x.fract().coeff);
203    }
204
205    #[test]
206    fn test_rem_int_by_decimal_one() {
207        let x = 17_i32;
208        let y = Decimal::<5>::ONE;
209        let z = x % y;
210        assert_eq!(z.coeff, 0);
211        let x = u64::one();
212        let z = x % y;
213        assert_eq!(z.coeff, 0);
214    }
215
216    #[test]
217    #[should_panic]
218    fn test_rem_decimal_by_int_zero() {
219        let x = Decimal::<5>::new_raw(17);
220        let y = i32::zero();
221        let _z = x % y;
222    }
223
224    #[test]
225    #[should_panic]
226    fn test_rem_int_by_decimal_zero() {
227        let x = 25;
228        let y = Decimal::<3>::ZERO;
229        let _z = x % y;
230    }
231}
232
233forward_op_assign!(impl RemAssign, rem_assign, Rem, rem);
234
235#[cfg(test)]
236mod rem_assign_tests {
237    use super::*;
238
239    #[test]
240    fn test_rem_assign_decimal() {
241        let mut x = Decimal::<3>::new_raw(702);
242        let y = Decimal::<2>::new_raw(300);
243        x %= y;
244        assert_eq!(x.coeff, 702);
245        let z = Decimal::<2>::new_raw(-70);
246        x %= z;
247        assert_eq!(x.coeff, 2);
248    }
249
250    #[test]
251    fn test_rem_assign_int() {
252        let mut x = Decimal::<1>::new_raw(702);
253        let y = 7_u16;
254        x %= y;
255        assert_eq!(x.coeff, 2);
256        let mut x = Decimal::<5>::new_raw(-7027702);
257        let y = -33_i64;
258        x %= y;
259        assert_eq!(x.coeff, -427702);
260    }
261}