Skip to main content

dashu_ratio/
div.rs

1#![allow(clippy::suspicious_arithmetic_impl)] // Clippy doesn't like that div is implemented with mul.
2
3use core::ops::{Div, DivAssign, Rem, RemAssign};
4use dashu_base::{DivEuclid, DivRemEuclid, Gcd, Inverse, RemEuclid, UnsignedAbs};
5use dashu_int::{IBig, UBig};
6
7use crate::{
8    error::panic_divide_by_0,
9    helper_macros::{impl_binop_assign_by_taking, impl_binop_with_int, impl_binop_with_macro},
10    rbig::{RBig, Relaxed},
11    repr::Repr,
12};
13
14macro_rules! impl_div_with_rbig {
15    (
16        $a:ident, $b:ident, $c:ident, $d:ident,
17        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
18    ) => {{
19        if $rc.is_zero() {
20            panic_divide_by_0()
21        }
22
23        // a/b / c/d = (ad)/gcd(a,c)/gcd(b,d)/(bc)
24        let g_ac = $ra.gcd($rc);
25        let g_bd = $rb.gcd($rd);
26        RBig(Repr {
27            numerator: ($a / &g_ac) * ($d / &g_bd) * $c.sign(),
28            denominator: ($b / g_bd) * ($c.unsigned_abs() / g_ac),
29        })
30    }};
31}
32macro_rules! impl_div_with_relaxed {
33    (
34        $a:ident, $b:ident, $c:ident, $d:ident,
35        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
36    ) => {{
37        if $rc.is_zero() {
38            panic_divide_by_0()
39        }
40
41        let _unused = ($ra, $rb, $rd);
42        Relaxed::from_parts($a * $d * $c.sign(), $b * $c.unsigned_abs())
43    }};
44}
45
46impl_binop_with_macro!(impl Div, div, impl_div_with_rbig);
47impl_binop_with_macro!(impl Div for Relaxed, div, impl_div_with_relaxed);
48impl_binop_assign_by_taking!(impl DivAssign for RBig, div_assign, div);
49impl_binop_assign_by_taking!(impl DivAssign for Relaxed, div_assign, div);
50
51// the strategy here for Rem is consistent with dashu_float::FBig
52macro_rules! impl_rem_with_rbig {
53    (
54        $a:ident, $b:ident, $c:ident, $d:ident,
55        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
56    ) => {{
57        let _unused = ($ra, $rc);
58        let g_bd = Gcd::gcd($rb, $rd);
59
60        // a/b % c/d = (ad % bc)/bd
61        let ddg = $d / &g_bd;
62        let left = &ddg * $a;
63        let right = $rb / &g_bd * $c.unsigned_abs();
64
65        let (sign, r1) = left.$method(&right).into_parts();
66        let r2 = right - &r1;
67        let rem = if r1 < r2 {
68            IBig::from_parts(sign, r1)
69        } else {
70            IBig::from_parts(-sign, r2)
71        };
72
73        RBig::from_parts(rem, $b * ddg)
74    }};
75}
76macro_rules! impl_rem_with_relaxed {
77    (
78        $a:ident, $b:ident, $c:ident, $d:ident,
79        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
80    ) => {{
81        let _unused = ($ra, $rc);
82
83        let (left, right) = ($a * $rd, $c.unsigned_abs() * $rb);
84        let (sign, r1) = left.$method(&right).into_parts();
85        let r2 = right - &r1;
86        let rem = if r1 < r2 {
87            IBig::from_parts(sign, r1)
88        } else {
89            IBig::from_parts(-sign, r2)
90        };
91
92        Relaxed::from_parts(rem, $b * $d)
93    }};
94}
95impl_binop_with_macro!(impl Rem, rem, impl_rem_with_rbig);
96impl_binop_with_macro!(impl Rem for Relaxed, rem, impl_rem_with_relaxed);
97impl_binop_assign_by_taking!(impl RemAssign for RBig, rem_assign, rem);
98impl_binop_assign_by_taking!(impl RemAssign for Relaxed, rem_assign, rem);
99
100macro_rules! impl_rbig_div_ubig {
101    (
102        $a:ident, $b:ident, $i:ident,
103        $ra:ident, $rb:ident, $ri:ident, $method:ident
104    ) => {{
105        if $ri.is_zero() {
106            panic_divide_by_0()
107        }
108
109        let _unused = $rb;
110        let g = $ra.gcd($ri);
111        RBig(Repr {
112            numerator: $a / &g,
113            denominator: $b * ($i / g),
114        })
115    }};
116}
117macro_rules! impl_rbig_div_ibig {
118    (
119        $a:ident, $b:ident, $i:ident,
120        $ra:ident, $rb:ident, $ri:ident, $method:ident
121    ) => {{
122        if $ri.is_zero() {
123            panic_divide_by_0()
124        }
125
126        let _unused = $rb;
127        let g = $ra.gcd($ri);
128        RBig(Repr {
129            numerator: $a / &g * $i.sign(),
130            denominator: $b * ($i.unsigned_abs() / g),
131        })
132    }};
133}
134macro_rules! impl_ubig_or_ibig_div_rbig {
135    (
136        $a:ident, $b:ident, $i:ident,
137        $ra:ident, $rb:ident, $ri:ident, $method:ident
138    ) => {{
139        if $ra.is_zero() {
140            panic_divide_by_0()
141        }
142
143        let _unused = $rb;
144        let g = $ra.gcd($ri);
145        RBig(Repr {
146            numerator: $b * ($i / &g) * $a.sign(),
147            denominator: $a.unsigned_abs() / g,
148        })
149    }};
150}
151impl_binop_with_int!(impl Div<UBig>, div, RBig, impl_rbig_div_ubig);
152impl_binop_with_int!(impl Div<IBig>, div, RBig, impl_rbig_div_ibig);
153impl_binop_with_int!(impl Div for UBig, div, RBig, impl_ubig_or_ibig_div_rbig);
154impl_binop_with_int!(impl Div for IBig, div, RBig, impl_ubig_or_ibig_div_rbig);
155
156macro_rules! impl_relaxed_div_ibig {
157    (
158        $a:ident, $b:ident, $i:ident,
159        $ra:ident, $rb:ident, $ri:ident, $method:ident
160    ) => {{
161        if $ri.is_zero() {
162            panic_divide_by_0()
163        }
164
165        let _unused = ($ra, $rb);
166        Relaxed::from_parts($a * $i.sign(), $b * $i.unsigned_abs())
167    }};
168}
169macro_rules! impl_relaxed_div_ubig {
170    (
171        $a:ident, $b:ident, $i:ident,
172        $ra:ident, $rb:ident, $ri:ident, $method:ident
173    ) => {{
174        if $ri.is_zero() {
175            panic_divide_by_0()
176        }
177
178        let _unused = ($ra, $rb);
179        Relaxed::from_parts($a, $b * $i)
180    }};
181}
182macro_rules! impl_ubig_or_ibig_div_relaxed {
183    (
184        $a:ident, $b:ident, $i:ident,
185        $ra:ident, $rb:ident, $ri:ident, $method:ident
186    ) => {{
187        if $ra.is_zero() {
188            panic_divide_by_0()
189        }
190
191        let _unused = ($ra, $rb, $ri);
192        Relaxed::from_parts($b * $i * $a.sign(), $a.unsigned_abs())
193    }};
194}
195impl_binop_with_int!(impl Div<IBig>, div, Relaxed, impl_relaxed_div_ibig);
196impl_binop_with_int!(impl Div<UBig>, div, Relaxed, impl_relaxed_div_ubig);
197impl_binop_with_int!(impl Div for UBig, div, Relaxed, impl_ubig_or_ibig_div_relaxed);
198impl_binop_with_int!(impl Div for IBig, div, Relaxed, impl_ubig_or_ibig_div_relaxed);
199
200macro_rules! impl_euclid_div {
201    (
202        $a:ident, $b:ident, $c:ident, $d:ident,
203        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
204    ) => {{
205        if $rc.is_zero() {
206            panic_divide_by_0()
207        }
208
209        let _unused = ($ra, $rb, $rd);
210        ($a * $d).$method($b * $c)
211    }};
212}
213impl_binop_with_macro!(impl DivEuclid, div_euclid -> IBig, impl_euclid_div);
214impl_binop_with_macro!(impl DivEuclid for Relaxed, div_euclid -> IBig, impl_euclid_div);
215
216macro_rules! impl_euclid_rem_with_rbig {
217    (
218        $a:ident, $b:ident, $c:ident, $d:ident,
219        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
220    ) => {{
221        let _unused = ($ra, $rc);
222        let g_bd = Gcd::gcd($rb, $rd);
223
224        let ddg = $d / &g_bd;
225        let left = &ddg * $a;
226        let right = $rb / &g_bd * $c;
227        RBig::from_parts(left.$method(right).into(), $b * ddg)
228    }};
229}
230macro_rules! impl_euclid_rem_with_relaxed {
231    (
232        $a:ident, $b:ident, $c:ident, $d:ident,
233        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
234    ) => {{
235        let _unused = ($ra, $rc);
236
237        let (left, right) = ($a * $rd, $c * $rb);
238        Relaxed::from_parts(left.$method(right).into(), $b * $d)
239    }};
240}
241impl_binop_with_macro!(impl RemEuclid, rem_euclid, impl_euclid_rem_with_rbig);
242impl_binop_with_macro!(impl RemEuclid for Relaxed, rem_euclid, impl_euclid_rem_with_relaxed);
243
244macro_rules! impl_euclid_divrem_with_rbig {
245    (
246        $a:ident, $b:ident, $c:ident, $d:ident,
247        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
248    ) => {{
249        let _unused = ($ra, $rc);
250        let g_bd = Gcd::gcd($rb, $rd);
251
252        let ddg = $d / &g_bd;
253        let left = &ddg * $a;
254        let right = $rb / &g_bd * $c;
255        let (q, r) = left.$method(right).into();
256        (q, RBig::from_parts(r.into(), $b * ddg))
257    }};
258}
259macro_rules! impl_euclid_divrem_with_relaxed {
260    (
261        $a:ident, $b:ident, $c:ident, $d:ident,
262        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
263    ) => {{
264        let _unused = ($ra, $rc);
265
266        let (left, right) = ($a * $rd, $c * $rb);
267        let (q, r) = left.$method(right).into();
268        (q, Relaxed::from_parts(r.into(), $b * $d))
269    }};
270}
271impl_binop_with_macro!(impl DivRemEuclid for RBig, div_rem_euclid, OutputDiv = IBig, OutputRem = RBig, impl_euclid_divrem_with_rbig);
272impl_binop_with_macro!(impl DivRemEuclid for Relaxed, div_rem_euclid, OutputDiv = IBig, OutputRem = Relaxed, impl_euclid_divrem_with_relaxed);
273
274impl Inverse for Repr {
275    type Output = Repr;
276
277    #[inline]
278    fn inv(self) -> Repr {
279        let (sign, num) = self.numerator.into_parts();
280        Repr {
281            numerator: IBig::from_parts(sign, self.denominator),
282            denominator: num,
283        }
284    }
285}
286
287impl Inverse for RBig {
288    type Output = RBig;
289    #[inline]
290    fn inv(self) -> RBig {
291        RBig(self.0.inv())
292    }
293}
294
295impl Inverse for &RBig {
296    type Output = RBig;
297    #[inline]
298    fn inv(self) -> RBig {
299        RBig(self.0.clone().inv())
300    }
301}
302
303impl Inverse for Relaxed {
304    type Output = Relaxed;
305    #[inline]
306    fn inv(self) -> Relaxed {
307        Relaxed(self.0.inv())
308    }
309}
310
311impl Inverse for &Relaxed {
312    type Output = Relaxed;
313    #[inline]
314    fn inv(self) -> Relaxed {
315        Relaxed(self.0.clone().inv())
316    }
317}