1use fpdec_core::MAX_N_FRAC_DIGITS;
11
12use crate::{binops::div_rounded::checked_div_rounded, normalize, Decimal};
13
14pub trait CheckedDiv<Rhs = Self> {
18 type Output;
20 fn checked_div(self, rhs: Rhs) -> Self::Output;
23}
24
25impl CheckedDiv<Self> for Decimal {
26 type Output = Option<Self>;
27
28 fn checked_div(self, rhs: Self) -> Self::Output {
29 if rhs.eq_zero() {
30 return None;
31 }
32 if self.eq_zero() {
33 return Some(Self::ZERO);
34 }
35 if rhs.eq_one() {
36 return Some(self);
37 }
38 let mut n_frac_digits = MAX_N_FRAC_DIGITS;
39 let mut coeff = checked_div_rounded(
40 self.coeff,
41 self.n_frac_digits,
42 rhs.coeff,
43 rhs.n_frac_digits,
44 n_frac_digits,
45 )?;
46 normalize(&mut coeff, &mut n_frac_digits);
47 Some(Self {
48 coeff,
49 n_frac_digits,
50 })
51 }
52}
53
54forward_ref_binop!(impl CheckedDiv, checked_div);
55
56#[cfg(test)]
57mod checked_div_decimal_tests {
58 use fpdec_core::mul_pow_ten;
59
60 use super::*;
61
62 #[test]
63 fn test_checked_div() {
64 let x = Decimal::new_raw(17, 0);
65 let y = Decimal::new_raw(-200, 2);
66 let z = x.checked_div(y).unwrap();
67 assert_eq!(z.coefficient(), -85);
68 assert_eq!(z.n_frac_digits(), 1);
69 let x = Decimal::new_raw(17, 17);
70 let y = Decimal::new_raw(2, 0);
71 let z = x.checked_div(y).unwrap();
72 assert_eq!(z.coefficient(), 85);
73 assert_eq!(z.n_frac_digits(), 18);
74 let x = Decimal::new_raw(12345678901234567890, 2);
75 let y = Decimal::new_raw(244140625, 6);
76 let z = x.checked_div(y).unwrap();
77 assert_eq!(z.coefficient(), 5056790077945679007744);
78 assert_eq!(z.n_frac_digits(), 7);
79 }
80
81 #[test]
82 fn test_checked_div_frac_limit_exceeded() {
83 let x = Decimal::new_raw(17, 1);
84 let y = Decimal::new_raw(3, 0);
85 let z = x.checked_div(y).unwrap();
86 assert_eq!(z.coefficient(), 566666666666666667);
87 assert_eq!(z.n_frac_digits(), 18);
88 }
89
90 #[test]
91 fn test_checked_div_by_one() {
92 let x = Decimal::new_raw(17, 5);
93 let y = Decimal::ONE;
94 let z = x.checked_div(y).unwrap();
95 assert_eq!(z.coefficient(), x.coefficient());
96 let y = Decimal::new_raw(100000, 5);
97 let z = x.checked_div(y).unwrap();
98 assert_eq!(z.coefficient(), x.coefficient());
99 }
100
101 #[test]
102 fn test_checked_div_by_zero() {
103 let x = Decimal::new_raw(17, 5);
104 let y = Decimal::ZERO;
105 let z = x.checked_div(y);
106 assert!(z.is_none());
107 }
108
109 #[test]
111 fn test_checked_div_stepwise() {
112 let x = Decimal::new_raw(mul_pow_ten(17, 17), 0);
113 let y = Decimal::new_raw(20498, 5);
114 let z = x.checked_div(y).unwrap();
115 assert_eq!(
116 z.coefficient(),
117 8293492048004683383744755585910820568_i128
118 );
119 assert_eq!(z.n_frac_digits(), 18);
120 }
121
122 #[test]
123 fn test_checked_div_overflow() {
124 let x = Decimal::new_raw(mul_pow_ten(17, 20), 0);
125 let y = Decimal::new_raw(2, 18);
126 let z = x.checked_div(y);
127 assert!(z.is_none());
128 }
129
130 #[test]
131 fn test_checked_div_ref() {
132 let x = Decimal::new_raw(12345, 3);
133 let y = Decimal::new_raw(12345, 1);
134 let z = x.checked_div(y).unwrap();
135 assert_eq!(
136 z.coefficient(),
137 (&x).checked_div(y).unwrap().coefficient()
138 );
139 assert_eq!(z.coefficient(), x.checked_div(&y).unwrap().coefficient());
140 assert_eq!(
141 z.coefficient(),
142 (&x).checked_div(&y).unwrap().coefficient()
143 );
144 }
145}
146
147macro_rules! impl_div_decimal_and_int {
148 () => {
149 impl_div_decimal_and_int!(u8, i8, u16, i16, u32, i32, u64, i64, i128);
150 };
151 ($($t:ty),*) => {
152 $(
153 impl CheckedDiv<$t> for Decimal {
154 type Output = Option<Self>;
155
156 fn checked_div(self, rhs: $t) -> Self::Output {
157 if rhs == 0 {
158 return None;
159 }
160 if self.eq_zero() {
161 return Some(Self::ZERO);
162 }
163 if rhs == 1 {
164 return Some(self);
165 }
166 let mut n_frac_digits = MAX_N_FRAC_DIGITS;
167 let mut coeff = checked_div_rounded(
168 self.coeff,
169 self.n_frac_digits,
170 i128::from(rhs),
171 0,
172 n_frac_digits,
173 )?;
174 normalize(&mut coeff, &mut n_frac_digits);
175 Some(Self {
176 coeff,
177 n_frac_digits,
178 })
179 }
180 }
181
182 impl CheckedDiv<Decimal> for $t {
183 type Output = Option<Decimal>;
184
185 fn checked_div(self, rhs: Decimal) -> Self::Output {
186 if rhs.eq_zero() {
187 return None;
188 }
189 if self == 0 {
190 return Some(Decimal::ZERO);
191 }
192 if rhs.eq_one() {
193 return Some(Decimal {
194 coeff: i128::from(self),
195 n_frac_digits: 0
196 });
197 }
198 let mut n_frac_digits = MAX_N_FRAC_DIGITS;
199 let mut coeff = checked_div_rounded(
200 i128::from(self),
201 0,
202 rhs.coeff,
203 rhs.n_frac_digits,
204 n_frac_digits,
205 )?;
206 normalize(&mut coeff, &mut n_frac_digits);
207 Some(Decimal {
208 coeff,
209 n_frac_digits,
210 })
211 }
212 }
213 )*
214 }
215}
216
217impl_div_decimal_and_int!();
218forward_ref_binop_decimal_int!(impl CheckedDiv, checked_div);
219
220#[cfg(test)]
221#[allow(clippy::neg_multiply)]
222mod checked_div_integer_tests {
223 use fpdec_core::mul_pow_ten;
224
225 use super::*;
226
227 macro_rules! gen_checked_div_integer_tests {
228 ($func:ident, $t:ty, $den:expr, $p:expr, $num:expr, $q:expr,
229 $quot:expr) => {
230 #[test]
231 fn $func() {
232 let d = Decimal::new_raw($num, $p);
233 let i: $t = $den;
234 let r = d.checked_div(i).unwrap();
235 assert_eq!(r.coefficient(), $quot);
236 assert_eq!(r.n_frac_digits(), $q);
237 assert_eq!(
238 r.coefficient(),
239 (&d).checked_div(i).unwrap().coefficient()
240 );
241 assert_eq!(
242 r.coefficient(),
243 d.checked_div(&i).unwrap().coefficient()
244 );
245 assert_eq!(
246 r.coefficient(),
247 (&d).checked_div(&i).unwrap().coefficient()
248 );
249 let z = CheckedDiv::checked_div(i, d).unwrap();
250 assert_eq!(z, CheckedDiv::checked_div(1_u8, r).unwrap());
251 assert_eq!(
252 z.coefficient(),
253 CheckedDiv::checked_div(&i, d).unwrap().coefficient()
254 );
255 assert_eq!(
256 z.coefficient(),
257 CheckedDiv::checked_div(i, &d).unwrap().coefficient()
258 );
259 assert_eq!(
260 z.coefficient(),
261 CheckedDiv::checked_div(&i, &d).unwrap().coefficient()
262 );
263 }
264 };
265 }
266
267 gen_checked_div_integer_tests!(test_checked_div_u8, u8, 5, 2, -1, 3, -2);
268 gen_checked_div_integer_tests!(
269 test_checked_div_i8,
270 i8,
271 115,
272 0,
273 230,
274 0,
275 2
276 );
277 gen_checked_div_integer_tests!(
278 test_checked_div_u16,
279 u16,
280 160,
281 4,
282 80,
283 5,
284 5
285 );
286 gen_checked_div_integer_tests!(
287 test_checked_div_i16,
288 i16,
289 25,
290 4,
291 390625,
292 4,
293 15625
294 );
295 gen_checked_div_integer_tests!(
296 test_checked_div_u32,
297 u32,
298 40,
299 1,
300 10,
301 3,
302 25
303 );
304 gen_checked_div_integer_tests!(
305 test_checked_div_i32,
306 i32,
307 -100,
308 9,
309 -1000,
310 8,
311 1
312 );
313 gen_checked_div_integer_tests!(
314 test_checked_div_u64,
315 u64,
316 1250,
317 4,
318 31250,
319 4,
320 25
321 );
322 gen_checked_div_integer_tests!(
323 test_checked_div_i64,
324 i64,
325 9765625,
326 7,
327 -488281250,
328 6,
329 -5
330 );
331 gen_checked_div_integer_tests!(
332 test_checked_div_i128,
333 i128,
334 5005,
335 0,
336 2002,
337 1,
338 4
339 );
340
341 #[test]
342 fn test_checked_div_decimal_by_int_one() {
343 let x = Decimal::new_raw(17, 5);
344 let y = 1_i64;
345 let z = x.checked_div(y).unwrap();
346 assert_eq!(z.coefficient(), x.coefficient());
347 assert_eq!(z.n_frac_digits(), x.n_frac_digits());
348 let y = 1_u8;
349 let z = x.checked_div(y).unwrap();
350 assert_eq!(z.coefficient(), x.coefficient());
351 assert_eq!(z.n_frac_digits(), x.n_frac_digits());
352 }
353
354 #[test]
355 fn test_checked_div_int_by_decimal_one() {
356 let x = 17_i32;
357 let y = Decimal::ONE;
358 let z = CheckedDiv::checked_div(x, y).unwrap();
359 assert_eq!(z.coefficient(), 17);
360 assert_eq!(z.n_frac_digits(), 0);
361 let x = 1_u64;
362 let z = CheckedDiv::checked_div(x, y).unwrap();
363 assert_eq!(z.coefficient(), 1);
364 assert_eq!(z.n_frac_digits(), 0);
365 }
366
367 #[test]
368 fn test_checked_div_int_by_decimal_frac_limit_exceeded() {
369 let x = 3_i8;
370 let y = Decimal::new_raw(17, 2);
371 let z = CheckedDiv::checked_div(x, y).unwrap();
372 assert_eq!(z.coefficient(), 17647058823529411765);
373 assert_eq!(z.n_frac_digits(), 18);
374 }
375
376 #[test]
377 fn test_checked_div_decimal_by_int_frac_limit_exceeded() {
378 let x = Decimal::new_raw(17, 12);
379 let y = 3_u8;
380 let z = x.checked_div(y).unwrap();
381 assert_eq!(z.coefficient(), 5666667);
382 assert_eq!(z.n_frac_digits(), 18);
383 }
384
385 #[test]
386 fn test_checked_div_decimal_by_int_zero() {
387 let x = Decimal::new_raw(17, 5);
388 let y = 0_i32;
389 let z = x.checked_div(y);
390 assert!(z.is_none());
391 }
392
393 #[test]
394 fn test_checked_div_int_by_decimal_zero() {
395 let x = 25_i64;
396 let y = Decimal::ZERO;
397 let z = CheckedDiv::checked_div(x, y);
398 assert!(z.is_none());
399 }
400
401 #[test]
402 fn test_checked_div_int_by_decimal_overflow() {
403 let x = mul_pow_ten(17, 20);
404 let y = Decimal::new_raw(2, 18);
405 let z = CheckedDiv::checked_div(x, y);
406 assert!(z.is_none());
407 }
408}