rust_fixed_point_decimal/binops/
div.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/div.rs $
8// $Revision: 2021-09-27T14:11:23+02:00 $
9
10use std::ops::{Div, DivAssign};
11
12use num::{integer::div_mod_floor, One, Zero};
13use rust_fixed_point_decimal_core::mul_pow_ten;
14
15use crate::{
16    prec_constraints::{PrecLimitCheck, True},
17    Decimal, DecimalError, MAX_PREC,
18};
19
20impl<const P: u8, const Q: u8> Div<Decimal<Q>> for Decimal<P>
21where
22    PrecLimitCheck<{ P <= MAX_PREC }>: True,
23    PrecLimitCheck<{ Q <= MAX_PREC }>: True,
24{
25    type Output = Decimal<9>;
26
27    fn div(self, other: Decimal<Q>) -> Self::Output {
28        if other.eq_zero() {
29            panic!("{}", DecimalError::DivisionByZero);
30        }
31        if other.eq_one() {
32            return Self::Output {
33                coeff: mul_pow_ten(self.coeff, MAX_PREC - P),
34            };
35        }
36        let r = MAX_PREC + Q - P;
37        // TODO: try to avoid overflowing shifted coeff
38        let (quot, rem) =
39            div_mod_floor(mul_pow_ten(self.coeff, r), other.coeff);
40        if rem != 0 {
41            panic!("{}", DecimalError::PrecLimitExceeded);
42        }
43        Self::Output { coeff: quot }
44    }
45}
46
47forward_ref_binop!(impl Div, div);
48
49#[cfg(test)]
50mod div_decimal_tests {
51    use super::*;
52
53    #[test]
54    fn test_div() {
55        let x = Decimal::<0>::new_raw(17);
56        let y = Decimal::<2>::new_raw(-200);
57        let z = x / y;
58        assert_eq!(z.coeff, -8500000000);
59        let x = Decimal::<8>::new_raw(17);
60        let y = Decimal::<0>::new_raw(2);
61        let z = x / y;
62        assert_eq!(z.coeff, 85);
63        let x = Decimal::<2>::new_raw(12345678901234567890);
64        let y = Decimal::<6>::new_raw(244140625);
65        let z = x / y;
66        assert_eq!(z.coeff, 505679007794567900774400);
67    }
68
69    #[test]
70    fn test_div_by_one() {
71        let x = Decimal::<5>::new_raw(17);
72        let y = Decimal::<2>::ONE;
73        let z = x / y;
74        assert_eq!(z.coeff, 170000);
75        let y = Decimal::<9>::ONE;
76        let z = x / y;
77        assert_eq!(z.coeff, 170000);
78    }
79
80    #[test]
81    #[should_panic]
82    fn test_div_by_zero() {
83        let x = Decimal::<5>::new_raw(17);
84        let y = Decimal::<2>::ZERO;
85        let _z = x / y;
86    }
87
88    #[test]
89    #[should_panic]
90    fn test_div_prec_limit_exceeded() {
91        let x = Decimal::<9>::new_raw(17);
92        let y = Decimal::<0>::new_raw(2);
93        let _z = x / y;
94    }
95
96    #[test]
97    #[should_panic]
98    fn test_div_overflow() {
99        let x = Decimal::<0>::new_raw(mul_pow_ten(17, 20));
100        let y = Decimal::<9>::new_raw(2);
101        let _z = x / y;
102    }
103
104    #[test]
105    fn test_div_ref() {
106        let x = Decimal::<3>::new_raw(12345);
107        let y = Decimal::<1>::new_raw(12345);
108        let z = x / y;
109        assert_eq!(z.coeff, (&x / y).coeff);
110        assert_eq!(z.coeff, (x / &y).coeff);
111        assert_eq!(z.coeff, (&x / &y).coeff);
112    }
113}
114
115macro_rules! impl_div_decimal_and_int {
116    () => {
117        impl_div_decimal_and_int!(u8, i8, u16, i16, u32, i32, u64, i64, i128);
118    };
119    ($($t:ty),*) => {
120        $(
121        impl<const P: u8> Div<$t> for Decimal<P>
122        where
123            PrecLimitCheck<{ P <= MAX_PREC }>: True,
124        {
125            type Output = Decimal<9>;
126
127            fn div(self, other: $t) -> Self::Output {
128                if other.is_zero() {
129                    panic!("{}", DecimalError::DivisionByZero);
130                }
131                if other.is_one() {
132                    return Self::Output {
133                        coeff: mul_pow_ten(self.coeff, MAX_PREC - P),
134                    };
135                }
136                let r = MAX_PREC - P;
137                let (quot, rem) =
138                    div_mod_floor(mul_pow_ten(self.coeff, r), other as i128);
139                if rem != 0 {
140                    panic!("{}", DecimalError::PrecLimitExceeded);
141                }
142                Self::Output { coeff: quot }
143            }
144        }
145
146        impl<const P: u8> Div<Decimal<P>> for $t
147        where
148            PrecLimitCheck<{ P <= MAX_PREC }>: True,
149        {
150            type Output = Decimal<9>;
151
152            fn div(self, other: Decimal<P>) -> Self::Output {
153                if other.eq_zero() {
154                    panic!("{}", DecimalError::DivisionByZero);
155                }
156                if other.eq_one() {
157                    return Self::Output {
158                        coeff: mul_pow_ten(self as i128, MAX_PREC),
159                    };
160                }
161                let r = MAX_PREC + P;
162                let (quot, rem) =
163                    div_mod_floor(mul_pow_ten(self as i128, r), other.coeff);
164                if rem != 0 {
165                    panic!("{}", DecimalError::PrecLimitExceeded);
166                }
167                Self::Output { coeff: quot }
168            }
169        }
170        )*
171    }
172}
173
174impl_div_decimal_and_int!();
175forward_ref_binop_decimal_int!(impl Div, div);
176
177#[cfg(test)]
178#[allow(clippy::neg_multiply)]
179mod div_integer_tests {
180    use num::{One, Zero};
181    use rust_fixed_point_decimal_core::ten_pow;
182
183    use super::*;
184
185    macro_rules! gen_div_integer_tests {
186        ($func:ident, $t:ty, $p:expr, $coeff:expr) => {
187            #[test]
188            fn $func() {
189                let d = Decimal::<$p>::new_raw($coeff);
190                let i: $t = 10;
191                let r = d / i;
192                assert_eq!(r.precision(), MAX_PREC);
193                assert_eq!(
194                    r.coeff,
195                    $coeff * ten_pow(MAX_PREC - $p) / i as i128
196                );
197                assert_eq!(r.coeff, (&d / i).coeff);
198                assert_eq!(r.coeff, (d / &i).coeff);
199                assert_eq!(r.coeff, (&d / &i).coeff);
200                let z = i / d;
201                assert_eq!(z.precision(), MAX_PREC);
202                assert_eq!(
203                    z.coeff,
204                    i as i128 * ten_pow(MAX_PREC + $p) / $coeff
205                );
206                assert_eq!(z.coeff, (&i / d).coeff);
207                assert_eq!(z.coeff, (i / &d).coeff);
208                assert_eq!(z.coeff, (&i / &d).coeff);
209            }
210        };
211    }
212
213    gen_div_integer_tests!(test_div_u8, u8, 2, -1);
214    gen_div_integer_tests!(test_div_i8, i8, 0, 250);
215    gen_div_integer_tests!(test_div_u16, u16, 4, 80);
216    gen_div_integer_tests!(test_div_i16, i16, 4, 390625);
217    gen_div_integer_tests!(test_div_u32, u32, 1, 10);
218    gen_div_integer_tests!(test_div_i32, i32, 9, -1000);
219    gen_div_integer_tests!(test_div_u64, u64, 3, 20);
220    gen_div_integer_tests!(test_div_i64, i64, 7, -488281250);
221
222    #[test]
223    fn test_div_i128() {
224        let coeff = 2002_i128;
225        let d = Decimal::<4>::new_raw(coeff);
226        let i = 5005_i128;
227        let r = d / i;
228        assert_eq!(r.precision(), MAX_PREC);
229        assert_eq!(r.coeff, coeff * 100000 / i);
230        assert_eq!(r.coeff, (&d / i).coeff);
231        assert_eq!(r.coeff, (d / &i).coeff);
232        assert_eq!(r.coeff, (&d / &i).coeff);
233        let z = i / d;
234        assert_eq!(z.precision(), MAX_PREC);
235        assert_eq!(z.coeff, i * ten_pow(13) / coeff);
236        assert_eq!(z.coeff, (&i / d).coeff);
237        assert_eq!(z.coeff, (i / &d).coeff);
238        assert_eq!(z.coeff, (&i / &d).coeff);
239    }
240
241    #[test]
242    fn test_div_decimal_by_int_one() {
243        let x = Decimal::<5>::new_raw(17);
244        let y = i64::one();
245        let z = x / y;
246        assert_eq!(z.coeff, 170000);
247        let y = u8::one();
248        let z = x / y;
249        assert_eq!(z.coeff, 170000);
250    }
251
252    #[test]
253    fn test_div_int_by_decimal_one() {
254        let x = 17;
255        let y = Decimal::<5>::ONE;
256        let z: Decimal<9> = x / y;
257        assert_eq!(z.coeff, 17000000000);
258        let x = u64::one();
259        let z = x / y;
260        assert_eq!(z.coeff, 1000000000);
261    }
262
263    #[test]
264    #[should_panic]
265    fn test_div_decimal_by_int_zero() {
266        let x = Decimal::<5>::new_raw(17);
267        let y = i32::zero();
268        let _z = x / y;
269    }
270
271    #[test]
272    #[should_panic]
273    fn test_div_int_by_decimal_zero() {
274        let x = 25;
275        let y = Decimal::<3>::ZERO;
276        let _z = x / y;
277    }
278
279    #[test]
280    #[should_panic]
281    fn test_div_decimal_by_int_prec_limit_exceeded() {
282        let x = Decimal::<2>::new_raw(17);
283        let y = 3;
284        let _z = x / y;
285    }
286
287    #[test]
288    #[should_panic]
289    fn test_div_int_by_decimal_prec_limit_exceeded() {
290        let x = 3;
291        let y = Decimal::<2>::new_raw(17);
292        let _z = x / y;
293    }
294
295    #[test]
296    #[should_panic]
297    fn test_div_int_by_decimal_overflow() {
298        let x = mul_pow_ten(17, 20);
299        let y = Decimal::<9>::new_raw(2);
300        let _z = x / y;
301    }
302}
303
304forward_op_assign!(impl DivAssign, div_assign, Div, div);
305
306#[cfg(test)]
307mod div_assign_tests {
308    use super::*;
309
310    #[test]
311    fn test_div_assign_decimal() {
312        let mut x = Decimal::<9>::new_raw(1234567890);
313        x /= Decimal::<3>::new_raw(5000);
314        assert_eq!(x.coeff, 123456789 * 2);
315    }
316
317    #[test]
318    fn test_div_assign_int() {
319        let mut x = Decimal::<9>::new_raw(1234567890);
320        x /= -10_i64;
321        assert_eq!(x.coeff, -123456789);
322    }
323
324    #[test]
325    #[should_panic]
326    fn test_div_assign_decimal_by_int_zero() {
327        let mut x = Decimal::<9>::new_raw(17);
328        let y = i32::zero();
329        x /= y;
330    }
331
332    #[test]
333    #[should_panic]
334    fn test_div_assign_decimal_by_decimal_zero() {
335        let mut x = Decimal::<9>::new_raw(25);
336        let y = Decimal::<3>::ZERO;
337        x /= y;
338    }
339
340    #[test]
341    #[should_panic]
342    fn test_div_assign_decimal_by_int_prec_limit_exceeded() {
343        let mut x = Decimal::<9>::new_raw(17);
344        let y = 3;
345        x /= y;
346    }
347
348    #[test]
349    #[should_panic]
350    fn test_div_assign_decimal_by_decimal_prec_limit_exceeded() {
351        let mut x = Decimal::<9>::new_raw(17);
352        let y = Decimal::<4>::new_raw(50000);
353        x /= y;
354    }
355}