1use std::cmp::Ordering;
11
12use num::Zero;
13use rust_fixed_point_decimal_core::ten_pow;
14
15use crate::{
16 prec_constraints::{PrecLimitCheck, True},
17 rounding::div_i128_rounded,
18 Decimal, DecimalError, MAX_PREC,
19};
20
21pub trait DivRounded<Rhs, Result = Self> {
23 fn div_rounded(self, rhs: Rhs) -> Result;
25}
26
27impl<const P: u8, const Q: u8, const R: u8> DivRounded<Decimal<Q>, Decimal<R>>
28 for Decimal<P>
29where
30 PrecLimitCheck<{ P <= MAX_PREC }>: True,
31 PrecLimitCheck<{ Q <= MAX_PREC }>: True,
32 PrecLimitCheck<{ R <= MAX_PREC }>: True,
33{
34 #[inline(always)]
35 fn div_rounded(self, other: Decimal<Q>) -> Decimal<R> {
36 if other.eq_zero() {
37 panic!("{}", DecimalError::DivisionByZero);
38 }
39 match P.cmp(&(Q + R)) {
40 Ordering::Equal => Decimal::<R> {
41 coeff: div_i128_rounded(self.coeff, other.coeff, None),
42 },
43 Ordering::Less => Decimal::<R> {
44 coeff: div_i128_rounded(
45 self.coeff * ten_pow(R + Q - P),
46 other.coeff,
47 None,
48 ),
49 },
50 Ordering::Greater => Decimal::<R> {
51 coeff: div_i128_rounded(
52 self.coeff,
53 other.coeff * ten_pow(P - Q - R),
54 None,
55 ),
56 },
57 }
58 }
59}
60
61forward_ref_binop_rounded!(impl DivRounded, div_rounded);
62
63#[cfg(test)]
64mod div_rounded_decimal_tests {
65 use rust_fixed_point_decimal_core::mul_pow_ten;
66
67 use super::*;
68
69 #[test]
70 fn test_div_rounded() {
71 let x = Decimal::<0>::new_raw(17);
72 let y = Decimal::<2>::new_raw(-201);
73 let z: Decimal<2> = x.div_rounded(y);
74 assert_eq!(z.coeff, -846);
75 let x = Decimal::<8>::new_raw(17);
76 let y = Decimal::<3>::new_raw(204);
77 let z: Decimal<8> = x.div_rounded(y);
78 assert_eq!(z.coeff, 83);
79 let x = Decimal::<2>::new_raw(12345678901234567890);
80 let y = Decimal::<6>::new_raw(244140625);
81 let z = x / y;
82 assert_eq!(z.coeff, 505679007794567900774400);
83 }
84
85 #[test]
86 fn test_div_rounded_by_one() {
87 let x = Decimal::<5>::new_raw(17);
88 let y = Decimal::<2>::ONE;
89 let z: Decimal<4> = x.div_rounded(y);
90 assert_eq!(z.coeff, 2);
91 let y = Decimal::<9>::ONE;
92 let z: Decimal<4> = x.div_rounded(y);
93 assert_eq!(z.coeff, 2);
94 }
95
96 #[test]
97 #[should_panic]
98 fn test_div_rounded_by_zero() {
99 let x = Decimal::<5>::new_raw(17);
100 let y = Decimal::<2>::ZERO;
101 let _z: Decimal<5> = x.div_rounded(y);
102 }
103
104 #[test]
105 #[should_panic]
106 fn test_div_rounded_overflow() {
107 let x = Decimal::<0>::new_raw(mul_pow_ten(17, 20));
108 let y = Decimal::<9>::new_raw(2);
109 let _z: Decimal<9> = x.div_rounded(y);
110 }
111
112 #[test]
113 fn test_div_rounded_ref() {
114 let x = Decimal::<3>::new_raw(12345);
115 let y = Decimal::<4>::new_raw(12345);
116 let z: Decimal<2> = x.div_rounded(y);
117 let a: Decimal<2> = DivRounded::div_rounded(&x, y);
118 assert_eq!(a.coeff, z.coeff);
119 let a: Decimal<2> = DivRounded::div_rounded(x, &y);
120 assert_eq!(a.coeff, z.coeff);
121 let a: Decimal<2> = DivRounded::div_rounded(&x, &y);
122 assert_eq!(a.coeff, z.coeff);
123 }
124}
125
126macro_rules! impl_div_rounded_decimal_and_int {
127 () => {
128 impl_div_rounded_decimal_and_int!(
129 u8, i8, u16, i16, u32, i32, u64, i64, i128
130 );
131 };
132 ($($t:ty),*) => {
133 $(
134 impl<const P: u8, const R: u8> DivRounded<$t, Decimal<R>> for Decimal<P>
135 where
136 PrecLimitCheck<{ P <= MAX_PREC }>: True,
137 PrecLimitCheck<{ R <= MAX_PREC }>: True,
138 {
139 fn div_rounded(self, other: $t) -> Decimal<R> {
140 if other.is_zero() {
141 panic!("{}", DecimalError::DivisionByZero);
142 }
143 match P.cmp(&R) {
144 Ordering::Equal => Decimal::<R> {
145 coeff: div_i128_rounded(
146 self.coeff,
147 other as i128,
148 None),
149 },
150 Ordering::Less => Decimal::<R> {
151 coeff: div_i128_rounded(
152 self.coeff * ten_pow(R - P),
153 other as i128,
154 None,
155 ),
156 },
157 Ordering::Greater => Decimal::<R> {
158 coeff: div_i128_rounded(
159 self.coeff,
160 other as i128 * ten_pow(P - R),
161 None,
162 ),
163 },
164 }
165 }
166 }
167
168 impl<'a, const P: u8, const R: u8> DivRounded<$t, Decimal<R>>
169 for &'a Decimal<P>
170 where
171 PrecLimitCheck<{ P <= MAX_PREC }>: True,
172 PrecLimitCheck<{ R <= MAX_PREC }>: True,
173 Decimal<P>: DivRounded<$t, Decimal<R>>,
174 {
175 #[inline(always)]
176 fn div_rounded(self, other: $t) -> Decimal<R> {
177 DivRounded::div_rounded(*self, other)
178 }
179 }
180
181 impl<const P: u8, const R: u8> DivRounded<&$t, Decimal<R>>
182 for Decimal<P>
183 where
184 PrecLimitCheck<{ P <= MAX_PREC }>: True,
185 PrecLimitCheck<{ R <= MAX_PREC }>: True,
186 Decimal<P>: DivRounded<$t, Decimal<R>>,
187 {
188 #[inline(always)]
189 fn div_rounded(self, other: &$t) -> Decimal<R> {
190 DivRounded::div_rounded(self, *other)
191 }
192 }
193
194 impl<const P: u8, const R: u8> DivRounded<&$t, Decimal<R>>
195 for &Decimal<P>
196 where
197 PrecLimitCheck<{ P <= MAX_PREC }>: True,
198 PrecLimitCheck<{ R <= MAX_PREC }>: True,
199 Decimal<P>: DivRounded<$t, Decimal<R>>,
200 {
201 #[inline(always)]
202 fn div_rounded(self, other: &$t) -> Decimal<R> {
203 DivRounded::div_rounded(*self, *other)
204 }
205 }
206
207 impl<const P: u8, const R: u8> DivRounded<Decimal<P>, Decimal<R>>
208 for $t
209 where
210 PrecLimitCheck<{ P <= MAX_PREC }>: True,
211 PrecLimitCheck<{ R <= MAX_PREC }>: True,
212 {
213 fn div_rounded(self, other: Decimal<P>) -> Decimal::<R> {
214 if other.eq_zero() {
215 panic!("{}", DecimalError::DivisionByZero);
216 }
217 Decimal::<R> {
218 coeff: div_i128_rounded(
219 self as i128 * ten_pow(P + R),
220 other.coeff,
221 None,
222 ),
223 }
224 }
225 }
226
227 impl<'a, const P: u8, const R: u8> DivRounded<Decimal<P>, Decimal<R>>
228 for &'a $t
229 where
230 PrecLimitCheck<{ P <= MAX_PREC }>: True,
231 PrecLimitCheck<{ R <= MAX_PREC }>: True,
232 $t: DivRounded<Decimal<P>, Decimal<R>>,
233 {
234 #[inline(always)]
235 fn div_rounded(self, other: Decimal<P>) -> Decimal<R> {
236 DivRounded::div_rounded(*self, other)
237 }
238 }
239
240 impl<const P: u8, const R: u8> DivRounded<&Decimal<P>, Decimal<R>>
241 for $t
242 where
243 PrecLimitCheck<{ P <= MAX_PREC }>: True,
244 PrecLimitCheck<{ R <= MAX_PREC }>: True,
245 $t: DivRounded<Decimal<P>, Decimal<R>>,
246 {
247 #[inline(always)]
248 fn div_rounded(self, other: &Decimal<P>) -> Decimal<R> {
249 DivRounded::div_rounded(self, *other)
250 }
251 }
252
253 impl<const P: u8, const R: u8> DivRounded<&Decimal<P>, Decimal<R>>
254 for &$t
255 where
256 PrecLimitCheck<{ P <= MAX_PREC }>: True,
257 PrecLimitCheck<{ R <= MAX_PREC }>: True,
258 $t: DivRounded<Decimal<P>, Decimal<R>>,
259 {
260 #[inline(always)]
261 fn div_rounded(self, other: &Decimal<P>) -> Decimal<R> {
262 DivRounded::div_rounded(*self, *other)
263 }
264 }
265 )*
266 }
267}
268
269impl_div_rounded_decimal_and_int!();
270
271#[cfg(test)]
272#[allow(clippy::neg_multiply)]
273mod div_rounded_decimal_by_int_tests {
274 use super::*;
275
276 macro_rules! gen_div_rounded_decimal_by_int_tests {
277 ($func:ident, $p:expr, $coeff:expr, $i:expr, $r:expr,
278 $res_coeff:expr) => {
279 #[test]
280 fn $func() {
281 let d = Decimal::<$p>::new_raw($coeff);
282 let i = $i;
283 let r: Decimal<$r> = d.div_rounded(i);
284 assert_eq!(r.coeff, $res_coeff);
285 let r: Decimal<$r> = (&d).div_rounded(i);
286 assert_eq!(r.coeff, $res_coeff);
287 let r: Decimal<$r> = d.div_rounded(&i);
288 assert_eq!(r.coeff, $res_coeff);
289 let r: Decimal<$r> = (&d).div_rounded(&i);
290 assert_eq!(r.coeff, $res_coeff);
291 }
292 };
293 }
294
295 gen_div_rounded_decimal_by_int_tests!(test_u8, 2, -1, 3_u8, 5, -333);
296 gen_div_rounded_decimal_by_int_tests!(test_i8, 0, -12, -3_i8, 5, 400000);
297 gen_div_rounded_decimal_by_int_tests!(test_u16, 2, -1, 3_u16, 5, -333);
298 gen_div_rounded_decimal_by_int_tests!(test_i16, 3, -12, -3_i16, 5, 400);
299 gen_div_rounded_decimal_by_int_tests!(
300 test_u32,
301 4,
302 u32::MAX as i128,
303 1_u32,
304 5,
305 u32::MAX as i128 * 10_i128
306 );
307 gen_div_rounded_decimal_by_int_tests!(
308 test_i32, 3, 12345, -328_i32, 5, -3764
309 );
310 gen_div_rounded_decimal_by_int_tests!(test_u64, 9, -1, 2_u64, 5, 0);
311 gen_div_rounded_decimal_by_int_tests!(
312 test_i64,
313 3,
314 u64::MAX as i128,
315 i64::MIN,
316 2,
317 0
318 );
319 gen_div_rounded_decimal_by_int_tests!(
320 test_i128,
321 0,
322 12345678901234567890,
323 987654321_i128,
324 5,
325 1249999988734375
326 );
327}
328
329#[cfg(test)]
330#[allow(clippy::neg_multiply)]
331mod div_rounded_int_by_decimal_tests {
332 use super::*;
333
334 macro_rules! gen_div_rounded_int_by_decimal_tests {
335 ($func:ident, $p:expr, $coeff:expr, $i:expr, $r:expr,
336 $res_coeff:expr) => {
337 #[test]
338 fn $func() {
339 let d = Decimal::<$p>::new_raw($coeff);
340 let i = $i;
341 let r: Decimal<$r> = i.div_rounded(d);
342 assert_eq!(r.coeff, $res_coeff);
343 let r: Decimal<$r> = (&i).div_rounded(d);
344 assert_eq!(r.coeff, $res_coeff);
345 let r: Decimal<$r> = i.div_rounded(&d);
346 assert_eq!(r.coeff, $res_coeff);
347 let r: Decimal<$r> = (&i).div_rounded(&d);
348 assert_eq!(r.coeff, $res_coeff);
349 }
350 };
351 }
352
353 gen_div_rounded_int_by_decimal_tests!(test_u8, 2, -14, 3_u8, 5, -2142857);
354 gen_div_rounded_int_by_decimal_tests!(test_i8, 0, -12, -3_i8, 5, 25000);
355 gen_div_rounded_int_by_decimal_tests!(test_u16, 2, -17, 3_u16, 5, -1764706);
356 gen_div_rounded_int_by_decimal_tests!(test_i16, 3, -12, -3_i16, 2, 25000);
357 gen_div_rounded_int_by_decimal_tests!(
358 test_u32,
359 4,
360 u32::MAX as i128,
361 1_u32,
362 9,
363 2328
364 );
365 gen_div_rounded_int_by_decimal_tests!(
366 test_i32, 3, 12345, -328_i32, 5, -2656946
367 );
368 gen_div_rounded_int_by_decimal_tests!(
369 test_u64,
370 9,
371 -1,
372 2_u64,
373 5,
374 -200000000000000
375 );
376 gen_div_rounded_int_by_decimal_tests!(
377 test_i64,
378 3,
379 u64::MAX as i128,
380 i64::MIN,
381 2,
382 -50000
383 );
384 gen_div_rounded_int_by_decimal_tests!(
385 test_i128,
386 0,
387 1234567890,
388 987654321987654321_i128,
389 1,
390 8000000081
391 );
392}