1use crate::{
2 error::{assert_finite_operands, assert_limited_precision},
3 fbig::FBig,
4 helper_macros::{self, impl_binop_assign_by_taking},
5 repr::{Context, Repr, Word},
6 round::{Round, Rounded},
7 utils::{digit_len, shl_digits_in_place, split_digits},
8};
9use core::ops::{Div, DivAssign, Rem, RemAssign};
10use dashu_base::{Approximation, DivEuclid, DivRem, DivRemEuclid, Inverse, RemEuclid};
11use dashu_int::{fast_div::ConstDivisor, modular::IntoRing, IBig, UBig};
12
13macro_rules! impl_div_or_rem_for_fbig {
14 (impl $op:ident, $method:ident, $repr_method:ident) => {
15 impl<R: Round, const B: Word> $op<FBig<R, B>> for FBig<R, B> {
16 type Output = FBig<R, B>;
17 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
18 let context = Context::max(self.context, rhs.context);
19 FBig::new(context.$repr_method(self.repr, rhs.repr).value(), context)
20 }
21 }
22
23 impl<'l, R: Round, const B: Word> $op<FBig<R, B>> for &'l FBig<R, B> {
24 type Output = FBig<R, B>;
25 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
26 let context = Context::max(self.context, rhs.context);
27 FBig::new(context.$repr_method(self.repr.clone(), rhs.repr).value(), context)
28 }
29 }
30
31 impl<'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for FBig<R, B> {
32 type Output = FBig<R, B>;
33 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
34 let context = Context::max(self.context, rhs.context);
35 FBig::new(context.$repr_method(self.repr, rhs.repr.clone()).value(), context)
36 }
37 }
38
39 impl<'l, 'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for &'l FBig<R, B> {
40 type Output = FBig<R, B>;
41 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
42 let context = Context::max(self.context, rhs.context);
43 FBig::new(
44 context
45 .$repr_method(self.repr.clone(), rhs.repr.clone())
46 .value(),
47 context,
48 )
49 }
50 }
51 };
52}
53impl_div_or_rem_for_fbig!(impl Div, div, repr_div);
54impl_div_or_rem_for_fbig!(impl Rem, rem, repr_rem);
55impl_binop_assign_by_taking!(impl DivAssign<Self>, div_assign, div);
56impl_binop_assign_by_taking!(impl RemAssign<Self>, rem_assign, rem);
57
58impl<R: Round, const B: Word> DivEuclid<FBig<R, B>> for FBig<R, B> {
59 type Output = IBig;
60 #[inline]
61 fn div_euclid(self, rhs: FBig<R, B>) -> Self::Output {
62 let (num, den) = align_as_int(self, rhs);
63 num.div_euclid(den)
64 }
65}
66
67impl<'l, R: Round, const B: Word> DivEuclid<FBig<R, B>> for &'l FBig<R, B> {
68 type Output = IBig;
69 #[inline]
70 fn div_euclid(self, rhs: FBig<R, B>) -> Self::Output {
71 self.clone().div_euclid(rhs)
72 }
73}
74
75impl<'r, R: Round, const B: Word> DivEuclid<&'r FBig<R, B>> for FBig<R, B> {
76 type Output = IBig;
77 #[inline]
78 fn div_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
79 self.div_euclid(rhs.clone())
80 }
81}
82
83impl<'l, 'r, R: Round, const B: Word> DivEuclid<&'r FBig<R, B>> for &'l FBig<R, B> {
84 type Output = IBig;
85 #[inline]
86 fn div_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
87 self.clone().div_euclid(rhs.clone())
88 }
89}
90
91impl<R: Round, const B: Word> RemEuclid<FBig<R, B>> for FBig<R, B> {
92 type Output = FBig<R, B>;
93 #[inline]
94 fn rem_euclid(self, rhs: FBig<R, B>) -> Self::Output {
95 let r_exponent = self.repr.exponent.min(rhs.repr.exponent);
96 let context = Context::max(self.context, rhs.context);
97
98 let (num, den) = align_as_int(self, rhs);
99 let r = num.rem_euclid(den);
100 let mut r = context.convert_int(r.into()).value();
101 if !r.repr.significand.is_zero() {
102 r.repr.exponent += r_exponent;
103 }
104 r
105 }
106}
107
108impl<'l, R: Round, const B: Word> RemEuclid<FBig<R, B>> for &'l FBig<R, B> {
109 type Output = FBig<R, B>;
110 #[inline]
111 fn rem_euclid(self, rhs: FBig<R, B>) -> Self::Output {
112 self.clone().rem_euclid(rhs)
113 }
114}
115
116impl<'r, R: Round, const B: Word> RemEuclid<&'r FBig<R, B>> for FBig<R, B> {
117 type Output = FBig<R, B>;
118 #[inline]
119 fn rem_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
120 self.rem_euclid(rhs.clone())
121 }
122}
123
124impl<'l, 'r, R: Round, const B: Word> RemEuclid<&'r FBig<R, B>> for &'l FBig<R, B> {
125 type Output = FBig<R, B>;
126 #[inline]
127 fn rem_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
128 self.clone().rem_euclid(rhs.clone())
129 }
130}
131
132impl<R: Round, const B: Word> DivRemEuclid<FBig<R, B>> for FBig<R, B> {
133 type OutputDiv = IBig;
134 type OutputRem = FBig<R, B>;
135 #[inline]
136 fn div_rem_euclid(self, rhs: FBig<R, B>) -> (IBig, FBig<R, B>) {
137 let r_exponent = self.repr.exponent.min(rhs.repr.exponent);
138 let context = Context::max(self.context, rhs.context);
139
140 let (num, den) = align_as_int(self, rhs);
141 let (q, r) = num.div_rem_euclid(den);
142 let mut r = context.convert_int(r.into()).value();
143 if !r.repr.significand.is_zero() {
144 r.repr.exponent += r_exponent;
145 }
146 (q, r)
147 }
148}
149
150impl<'l, R: Round, const B: Word> DivRemEuclid<FBig<R, B>> for &'l FBig<R, B> {
151 type OutputDiv = IBig;
152 type OutputRem = FBig<R, B>;
153 #[inline]
154 fn div_rem_euclid(self, rhs: FBig<R, B>) -> (IBig, FBig<R, B>) {
155 self.clone().div_rem_euclid(rhs)
156 }
157}
158
159impl<'r, R: Round, const B: Word> DivRemEuclid<&'r FBig<R, B>> for FBig<R, B> {
160 type OutputDiv = IBig;
161 type OutputRem = FBig<R, B>;
162 #[inline]
163 fn div_rem_euclid(self, rhs: &FBig<R, B>) -> (IBig, FBig<R, B>) {
164 self.div_rem_euclid(rhs.clone())
165 }
166}
167
168impl<'l, 'r, R: Round, const B: Word> DivRemEuclid<&'r FBig<R, B>> for &'l FBig<R, B> {
169 type OutputDiv = IBig;
170 type OutputRem = FBig<R, B>;
171 #[inline]
172 fn div_rem_euclid(self, rhs: &FBig<R, B>) -> (IBig, FBig<R, B>) {
173 self.clone().div_rem_euclid(rhs.clone())
174 }
175}
176
177macro_rules! impl_div_primitive_with_fbig {
178 ($($t:ty)*) => {$(
179 helper_macros::impl_binop_with_primitive!(impl Div<$t>, div);
180 helper_macros::impl_binop_assign_with_primitive!(impl DivAssign<$t>, div_assign);
181 )*};
182}
183impl_div_primitive_with_fbig!(u8 u16 u32 u64 u128 usize UBig i8 i16 i32 i64 i128 isize IBig);
184impl<R: Round, const B: Word> Inverse for FBig<R, B> {
187 type Output = FBig<R, B>;
188
189 #[inline]
190 fn inv(self) -> Self::Output {
191 self.context.inv(&self.repr).value()
192 }
193}
194
195impl<R: Round, const B: Word> Inverse for &FBig<R, B> {
196 type Output = FBig<R, B>;
197
198 #[inline]
199 fn inv(self) -> Self::Output {
200 self.context.inv(&self.repr).value()
201 }
202}
203
204fn align_as_int<R: Round, const B: Word>(lhs: FBig<R, B>, rhs: FBig<R, B>) -> (IBig, IBig) {
206 let ediff = lhs.repr.exponent - rhs.repr.exponent;
207 let (mut num, mut den) = (lhs.repr.significand, rhs.repr.significand);
208 if ediff >= 0 {
209 shl_digits_in_place::<B>(&mut num, ediff as _);
210 } else {
211 shl_digits_in_place::<B>(&mut den, (-ediff) as _);
212 }
213 (num, den)
214}
215
216impl<R: Round> Context<R> {
217 pub(crate) fn repr_div<const B: Word>(&self, lhs: Repr<B>, rhs: Repr<B>) -> Rounded<Repr<B>> {
218 assert_finite_operands(&lhs, &rhs);
219 assert_limited_precision(self.precision);
220
221 debug_assert!(lhs.digits() <= self.precision + rhs.digits());
223
224 let (mut q, mut r) = lhs.significand.div_rem(&rhs.significand);
225 let mut e = lhs.exponent - rhs.exponent;
226 if r.is_zero() {
227 return Approximation::Exact(Repr::new(q, e));
228 }
229
230 let ddigits = digit_len::<B>(&rhs.significand);
231 if q.is_zero() {
232 let rdigits = digit_len::<B>(&r); let shift = ddigits + self.precision - rdigits;
235 shl_digits_in_place::<B>(&mut r, shift);
236 e -= shift as isize;
237 let (q0, r0) = r.div_rem(&rhs.significand);
238 q = q0;
239 r = r0;
240 } else {
241 let ndigits = digit_len::<B>(&q) + ddigits;
242 if ndigits < ddigits + self.precision {
243 let shift = ddigits + self.precision - ndigits;
245 shl_digits_in_place::<B>(&mut q, shift);
246 shl_digits_in_place::<B>(&mut r, shift);
247 e -= shift as isize;
248
249 let (q0, r0) = r.div_rem(&rhs.significand);
250 q += q0;
251 r = r0;
252 }
253 }
254
255 if r.is_zero() {
256 Approximation::Exact(Repr::new(q, e))
257 } else {
258 let adjust = R::round_ratio(&q, r, &rhs.significand);
259 Approximation::Inexact(Repr::new(q + adjust, e), adjust)
260 }
261 }
262
263 pub(crate) fn repr_rem<const B: Word>(&self, lhs: Repr<B>, rhs: Repr<B>) -> Rounded<Repr<B>> {
264 assert_finite_operands(&lhs, &rhs);
265
266 let (lhs_sign, lhs_signif) = lhs.significand.into_parts();
267 let (_, rhs_signif) = rhs.significand.into_parts();
268
269 use core::cmp::Ordering;
270 let significand = match lhs.exponent.cmp(&rhs.exponent) {
271 Ordering::Equal => {
272 let r1 = lhs_signif % &rhs_signif;
273 let r2 = rhs_signif - &r1;
274 if r1 < r2 {
275 IBig::from_parts(lhs_sign, r1)
276 } else {
277 IBig::from_parts(-lhs_sign, r2)
278 }
279 }
280 Ordering::Greater => {
281 let modulo = ConstDivisor::new(rhs_signif);
284 let shift = (lhs.exponent - rhs.exponent) as usize;
285 let scaling = if B == 2 {
286 (UBig::ONE << shift).into_ring(&modulo)
287 } else {
288 UBig::from_word(B).into_ring(&modulo).pow(&shift.into())
289 };
290 let r = lhs_signif.into_ring(&modulo) * scaling;
291 let r1 = r.residue();
292 let r2 = (-r).residue();
293 if r1 < r2 {
294 IBig::from_parts(lhs_sign, r1)
295 } else {
296 IBig::from_parts(-lhs_sign, r2)
297 }
298 }
299 Ordering::Less => {
300 let shift = (rhs.exponent - lhs.exponent) as usize;
302 let (hi, lo) = split_digits::<B>(lhs_signif.into(), shift);
303
304 let mut r1 = hi % &rhs_signif;
305 let mut r2 = rhs_signif - &r1;
306
307 shl_digits_in_place::<B>(&mut r1, shift);
308 r1 += &lo;
309
310 shl_digits_in_place::<B>(&mut r2, shift);
311 r2 -= lo;
312
313 if r1 < r2 {
314 lhs_sign * r1
315 } else {
316 (-lhs_sign) * r2
317 }
318 }
319 };
320
321 let exponent = lhs.exponent.min(rhs.exponent);
322 if significand.is_zero() {
323 Approximation::Exact(Repr::zero())
324 } else {
325 self.repr_round(Repr::new(significand, exponent))
326 }
327 }
328
329 pub fn div<const B: Word>(&self, lhs: &Repr<B>, rhs: &Repr<B>) -> Rounded<FBig<R, B>> {
353 assert_finite_operands(lhs, rhs);
354
355 let lhs_repr = if !lhs.is_zero() && lhs.digits_ub() > rhs.digits_lb() + self.precision {
356 Self::new(rhs.digits() + self.precision)
358 .repr_round_ref(lhs)
359 .value()
360 } else {
361 lhs.clone()
362 };
363 self.repr_div(lhs_repr, rhs.clone())
364 .map(|v| FBig::new(v, *self))
365 }
366
367 pub fn rem<const B: Word>(&self, lhs: &Repr<B>, rhs: &Repr<B>) -> Rounded<FBig<R, B>> {
388 assert_finite_operands(lhs, rhs);
389 self.repr_rem(lhs.clone(), rhs.clone())
390 .map(|v| FBig::new(v, *self))
391 }
392
393 #[inline]
408 pub fn inv<const B: Word>(&self, f: &Repr<B>) -> Rounded<FBig<R, B>> {
409 self.repr_div(Repr::one(), f.clone())
410 .map(|v| FBig::new(v, *self))
411 }
412}