rust_fixed_point_decimal/binops/
rem.rs1use 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}