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 / g) * $i,
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 / g) * $i.unsigned_abs(),
131        })
132    }};
133}
134impl_binop_with_int!(impl Div<UBig>, div, RBig, impl_rbig_div_ubig);
135impl_binop_with_int!(impl Div<IBig>, div, RBig, impl_rbig_div_ibig);
136
137macro_rules! impl_relaxed_div_ibig {
138    (
139        $a:ident, $b:ident, $i:ident,
140        $ra:ident, $rb:ident, $ri:ident, $method:ident
141    ) => {{
142        if $ri.is_zero() {
143            panic_divide_by_0()
144        }
145
146        let _unused = ($ra, $rb);
147        Relaxed::from_parts($a * $i.sign(), $b * $i.unsigned_abs())
148    }};
149}
150macro_rules! impl_relaxed_div_ubig {
151    (
152        $a:ident, $b:ident, $i:ident,
153        $ra:ident, $rb:ident, $ri:ident, $method:ident
154    ) => {{
155        if $ri.is_zero() {
156            panic_divide_by_0()
157        }
158
159        let _unused = ($ra, $rb);
160        Relaxed::from_parts($a, $b * $i)
161    }};
162}
163impl_binop_with_int!(impl Div<IBig>, div, Relaxed, impl_relaxed_div_ibig);
164impl_binop_with_int!(impl Div<UBig>, div, Relaxed, impl_relaxed_div_ubig);
165
166macro_rules! impl_euclid_div {
167    (
168        $a:ident, $b:ident, $c:ident, $d:ident,
169        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
170    ) => {{
171        if $rc.is_zero() {
172            panic_divide_by_0()
173        }
174
175        let _unused = ($ra, $rb, $rd);
176        ($a * $d).$method($b * $c)
177    }};
178}
179impl_binop_with_macro!(impl DivEuclid, div_euclid -> IBig, impl_euclid_div);
180impl_binop_with_macro!(impl DivEuclid for Relaxed, div_euclid -> IBig, impl_euclid_div);
181
182macro_rules! impl_euclid_rem_with_rbig {
183    (
184        $a:ident, $b:ident, $c:ident, $d:ident,
185        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
186    ) => {{
187        let _unused = ($ra, $rc);
188        let g_bd = Gcd::gcd($rb, $rd);
189
190        let ddg = $d / &g_bd;
191        let left = &ddg * $a;
192        let right = $rb / &g_bd * $c;
193        RBig::from_parts(left.$method(right).into(), $b * ddg)
194    }};
195}
196macro_rules! impl_euclid_rem_with_relaxed {
197    (
198        $a:ident, $b:ident, $c:ident, $d:ident,
199        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
200    ) => {{
201        let _unused = ($ra, $rc);
202
203        let (left, right) = ($a * $rd, $c * $rb);
204        Relaxed::from_parts(left.$method(right).into(), $b * $d)
205    }};
206}
207impl_binop_with_macro!(impl RemEuclid, rem_euclid, impl_euclid_rem_with_rbig);
208impl_binop_with_macro!(impl RemEuclid for Relaxed, rem_euclid, impl_euclid_rem_with_relaxed);
209
210macro_rules! impl_euclid_divrem_with_rbig {
211    (
212        $a:ident, $b:ident, $c:ident, $d:ident,
213        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
214    ) => {{
215        let _unused = ($ra, $rc);
216        let g_bd = Gcd::gcd($rb, $rd);
217
218        let ddg = $d / &g_bd;
219        let left = &ddg * $a;
220        let right = $rb / &g_bd * $c;
221        let (q, r) = left.$method(right).into();
222        (q, RBig::from_parts(r.into(), $b * ddg))
223    }};
224}
225macro_rules! impl_euclid_divrem_with_relaxed {
226    (
227        $a:ident, $b:ident, $c:ident, $d:ident,
228        $ra:ident, $rb:ident, $rc:ident, $rd:ident, $method:ident
229    ) => {{
230        let _unused = ($ra, $rc);
231
232        let (left, right) = ($a * $rd, $c * $rb);
233        let (q, r) = left.$method(right).into();
234        (q, Relaxed::from_parts(r.into(), $b * $d))
235    }};
236}
237impl_binop_with_macro!(impl DivRemEuclid for RBig, div_rem_euclid, OutputDiv = IBig, OutputRem = RBig, impl_euclid_divrem_with_rbig);
238impl_binop_with_macro!(impl DivRemEuclid for Relaxed, div_rem_euclid, OutputDiv = IBig, OutputRem = Relaxed, impl_euclid_divrem_with_relaxed);
239
240impl Inverse for Repr {
241    type Output = Repr;
242
243    #[inline]
244    fn inv(self) -> Repr {
245        let (sign, num) = self.numerator.into_parts();
246        Repr {
247            numerator: IBig::from_parts(sign, self.denominator),
248            denominator: num,
249        }
250    }
251}
252
253impl Inverse for RBig {
254    type Output = RBig;
255    #[inline]
256    fn inv(self) -> RBig {
257        RBig(self.0.inv())
258    }
259}
260
261impl Inverse for &RBig {
262    type Output = RBig;
263    #[inline]
264    fn inv(self) -> RBig {
265        RBig(self.0.clone().inv())
266    }
267}
268
269impl Inverse for Relaxed {
270    type Output = Relaxed;
271    #[inline]
272    fn inv(self) -> Relaxed {
273        Relaxed(self.0.inv())
274    }
275}
276
277impl Inverse for &Relaxed {
278    type Output = Relaxed;
279    #[inline]
280    fn inv(self) -> Relaxed {
281        Relaxed(self.0.clone().inv())
282    }
283}