1use crate::{
2 error::{assert_finite_operands, assert_limited_precision, FpError, FpResult},
3 fbig::FBig,
4 helper_macros::{self, impl_binop_assign_by_taking},
5 repr::{Context, Repr, Word},
6 round::{Round, Rounded, Rounding},
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, Sign};
11use dashu_int::{fast_div::ConstDivisor, modular::IntoRing, IBig, UBig};
12
13fn make_div_repr<const B: Word>(
17 sign_negative: bool,
18 significand: IBig,
19 exponent: isize,
20) -> Repr<B> {
21 if significand.is_zero() {
22 if sign_negative {
23 Repr::neg_zero()
24 } else {
25 Repr::zero()
26 }
27 } else {
28 Repr::new(significand, exponent)
29 }
30}
31
32macro_rules! impl_div_for_fbig {
33 (impl $op:ident, $method:ident, $repr_method:ident) => {
34 impl<R: Round, const B: Word> $op<FBig<R, B>> for FBig<R, B> {
35 type Output = FBig<R, B>;
36 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
37 let context = Context::max(self.context, rhs.context);
38 let result = context
41 .$repr_method(self.repr, rhs.repr)
42 .map(|r| r.map(|repr| FBig::new(repr, context)));
43 context.unwrap_fp(result)
44 }
45 }
46
47 impl<'l, R: Round, const B: Word> $op<FBig<R, B>> for &'l FBig<R, B> {
48 type Output = FBig<R, B>;
49 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
50 let context = Context::max(self.context, rhs.context);
51 let result = context
52 .$repr_method(self.repr.clone(), rhs.repr)
53 .map(|r| r.map(|repr| FBig::new(repr, context)));
54 context.unwrap_fp(result)
55 }
56 }
57
58 impl<'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for FBig<R, B> {
59 type Output = FBig<R, B>;
60 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
61 let context = Context::max(self.context, rhs.context);
62 let result = context
63 .$repr_method(self.repr, rhs.repr.clone())
64 .map(|r| r.map(|repr| FBig::new(repr, context)));
65 context.unwrap_fp(result)
66 }
67 }
68
69 impl<'l, 'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for &'l FBig<R, B> {
70 type Output = FBig<R, B>;
71 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
72 let context = Context::max(self.context, rhs.context);
73 let result = context
74 .$repr_method(self.repr.clone(), rhs.repr.clone())
75 .map(|r| r.map(|repr| FBig::new(repr, context)));
76 context.unwrap_fp(result)
77 }
78 }
79 };
80}
81
82macro_rules! impl_rem_for_fbig {
83 (impl $op:ident, $method:ident, $repr_method:ident) => {
84 impl<R: Round, const B: Word> $op<FBig<R, B>> for FBig<R, B> {
85 type Output = FBig<R, B>;
86 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
87 let context = Context::max(self.context, rhs.context);
88 FBig::new(context.$repr_method(self.repr, rhs.repr).value(), context)
89 }
90 }
91
92 impl<'l, R: Round, const B: Word> $op<FBig<R, B>> for &'l FBig<R, B> {
93 type Output = FBig<R, B>;
94 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
95 let context = Context::max(self.context, rhs.context);
96 FBig::new(context.$repr_method(self.repr.clone(), rhs.repr).value(), context)
97 }
98 }
99
100 impl<'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for FBig<R, B> {
101 type Output = FBig<R, B>;
102 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
103 let context = Context::max(self.context, rhs.context);
104 FBig::new(context.$repr_method(self.repr, rhs.repr.clone()).value(), context)
105 }
106 }
107
108 impl<'l, 'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for &'l FBig<R, B> {
109 type Output = FBig<R, B>;
110 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
111 let context = Context::max(self.context, rhs.context);
112 FBig::new(
113 context
114 .$repr_method(self.repr.clone(), rhs.repr.clone())
115 .value(),
116 context,
117 )
118 }
119 }
120 };
121}
122impl_div_for_fbig!(impl Div, div, repr_div);
123impl_rem_for_fbig!(impl Rem, rem, repr_rem);
124impl_binop_assign_by_taking!(impl DivAssign<Self>, div_assign, div);
125impl_binop_assign_by_taking!(impl RemAssign<Self>, rem_assign, rem);
126
127impl<R: Round, const B: Word> DivEuclid<FBig<R, B>> for FBig<R, B> {
128 type Output = IBig;
129 #[inline]
130 fn div_euclid(self, rhs: FBig<R, B>) -> Self::Output {
131 let (num, den) = align_as_int(self, rhs);
132 num.div_euclid(den)
133 }
134}
135
136impl<R: Round, const B: Word> DivEuclid<FBig<R, B>> for &FBig<R, B> {
137 type Output = IBig;
138 #[inline]
139 fn div_euclid(self, rhs: FBig<R, B>) -> Self::Output {
140 self.clone().div_euclid(rhs)
141 }
142}
143
144impl<R: Round, const B: Word> DivEuclid<&FBig<R, B>> for FBig<R, B> {
145 type Output = IBig;
146 #[inline]
147 fn div_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
148 self.div_euclid(rhs.clone())
149 }
150}
151
152impl<R: Round, const B: Word> DivEuclid<&FBig<R, B>> for &FBig<R, B> {
153 type Output = IBig;
154 #[inline]
155 fn div_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
156 self.clone().div_euclid(rhs.clone())
157 }
158}
159
160impl<R: Round, const B: Word> RemEuclid<FBig<R, B>> for FBig<R, B> {
161 type Output = FBig<R, B>;
162 #[inline]
163 fn rem_euclid(self, rhs: FBig<R, B>) -> Self::Output {
164 let r_exponent = self.repr.exponent.min(rhs.repr.exponent);
165 let context = Context::max(self.context, rhs.context);
166
167 let (num, den) = align_as_int(self, rhs);
168 let r = num.rem_euclid(den);
169 let mut r = context.convert_int(r.into()).value();
170 if !r.repr.significand.is_zero() {
171 r.repr.exponent += r_exponent;
172 }
173 r
174 }
175}
176
177impl<R: Round, const B: Word> RemEuclid<FBig<R, B>> for &FBig<R, B> {
178 type Output = FBig<R, B>;
179 #[inline]
180 fn rem_euclid(self, rhs: FBig<R, B>) -> Self::Output {
181 self.clone().rem_euclid(rhs)
182 }
183}
184
185impl<R: Round, const B: Word> RemEuclid<&FBig<R, B>> for FBig<R, B> {
186 type Output = FBig<R, B>;
187 #[inline]
188 fn rem_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
189 self.rem_euclid(rhs.clone())
190 }
191}
192
193impl<R: Round, const B: Word> RemEuclid<&FBig<R, B>> for &FBig<R, B> {
194 type Output = FBig<R, B>;
195 #[inline]
196 fn rem_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
197 self.clone().rem_euclid(rhs.clone())
198 }
199}
200
201impl<R: Round, const B: Word> DivRemEuclid<FBig<R, B>> for FBig<R, B> {
202 type OutputDiv = IBig;
203 type OutputRem = FBig<R, B>;
204 #[inline]
205 fn div_rem_euclid(self, rhs: FBig<R, B>) -> (IBig, FBig<R, B>) {
206 let r_exponent = self.repr.exponent.min(rhs.repr.exponent);
207 let context = Context::max(self.context, rhs.context);
208
209 let (num, den) = align_as_int(self, rhs);
210 let (q, r) = num.div_rem_euclid(den);
211 let mut r = context.convert_int(r.into()).value();
212 if !r.repr.significand.is_zero() {
213 r.repr.exponent += r_exponent;
214 }
215 (q, r)
216 }
217}
218
219impl<R: Round, const B: Word> DivRemEuclid<FBig<R, B>> for &FBig<R, B> {
220 type OutputDiv = IBig;
221 type OutputRem = FBig<R, B>;
222 #[inline]
223 fn div_rem_euclid(self, rhs: FBig<R, B>) -> (IBig, FBig<R, B>) {
224 self.clone().div_rem_euclid(rhs)
225 }
226}
227
228impl<R: Round, const B: Word> DivRemEuclid<&FBig<R, B>> for FBig<R, B> {
229 type OutputDiv = IBig;
230 type OutputRem = FBig<R, B>;
231 #[inline]
232 fn div_rem_euclid(self, rhs: &FBig<R, B>) -> (IBig, FBig<R, B>) {
233 self.div_rem_euclid(rhs.clone())
234 }
235}
236
237impl<R: Round, const B: Word> DivRemEuclid<&FBig<R, B>> for &FBig<R, B> {
238 type OutputDiv = IBig;
239 type OutputRem = FBig<R, B>;
240 #[inline]
241 fn div_rem_euclid(self, rhs: &FBig<R, B>) -> (IBig, FBig<R, B>) {
242 self.clone().div_rem_euclid(rhs.clone())
243 }
244}
245
246macro_rules! impl_div_primitive_with_fbig {
247 ($($t:ty)*) => {$(
248 helper_macros::impl_binop_with_primitive!(impl Div<$t>, div);
249 helper_macros::impl_binop_assign_with_primitive!(impl DivAssign<$t>, div_assign);
250 )*};
251}
252impl_div_primitive_with_fbig!(u8 u16 u32 u64 u128 usize UBig i8 i16 i32 i64 i128 isize IBig);
253impl<R: Round, const B: Word> Inverse for FBig<R, B> {
256 type Output = FBig<R, B>;
257
258 #[inline]
259 fn inv(self) -> Self::Output {
260 self.context.unwrap_fp(self.context.inv(&self.repr))
261 }
262}
263
264impl<R: Round, const B: Word> Inverse for &FBig<R, B> {
265 type Output = FBig<R, B>;
266
267 #[inline]
268 fn inv(self) -> Self::Output {
269 self.context.unwrap_fp(self.context.inv(&self.repr))
270 }
271}
272
273impl<R: Round, const B: Word> FBig<R, B> {
274 #[inline]
280 pub fn inv(&self) -> Self {
281 self.context.unwrap_fp(self.context.inv(&self.repr))
282 }
283}
284
285fn align_as_int<R: Round, const B: Word>(lhs: FBig<R, B>, rhs: FBig<R, B>) -> (IBig, IBig) {
287 let ediff = lhs.repr.exponent - rhs.repr.exponent;
288 let (mut num, mut den) = (lhs.repr.significand, rhs.repr.significand);
289 if ediff >= 0 {
290 shl_digits_in_place::<B>(&mut num, ediff as _);
291 } else {
292 shl_digits_in_place::<B>(&mut den, (-ediff) as _);
293 }
294 (num, den)
295}
296
297impl<R: Round> Context<R> {
298 pub(crate) fn repr_div<const B: Word>(&self, lhs: Repr<B>, rhs: Repr<B>) -> FpResult<Repr<B>> {
299 assert_finite_operands(&lhs, &rhs);
300 assert_limited_precision(self.precision);
301
302 let sign_negative = lhs.sign() != rhs.sign();
303 let sign = if sign_negative {
304 Sign::Negative
305 } else {
306 Sign::Positive
307 };
308
309 if rhs.significand.is_zero() {
310 if lhs.significand.is_zero() {
311 } else {
314 return Ok(Approximation::Exact(Repr::infinity_with_sign(sign)));
316 }
317 }
318
319 debug_assert!(lhs.digits() <= self.precision + rhs.digits());
321
322 let (mut q, mut r) = lhs.significand.div_rem(&rhs.significand);
323 let mut e = lhs.exponent.checked_sub(rhs.exponent).ok_or({
324 if lhs.exponent >= 0 {
325 FpError::Overflow(sign)
326 } else {
327 FpError::Underflow(sign)
328 }
329 })?;
330 if r.is_zero() {
331 return Ok(Approximation::Exact(
332 make_div_repr(sign_negative, q, e).check_finite_exponent()?,
333 ));
334 }
335
336 let ddigits = digit_len::<B>(&rhs.significand);
337 if q.is_zero() {
338 let rdigits = digit_len::<B>(&r); let shift = ddigits + self.precision - rdigits;
341 shl_digits_in_place::<B>(&mut r, shift);
342 e = e
343 .checked_sub(shift as isize)
344 .ok_or(FpError::Underflow(sign))?;
345 let (q0, r0) = r.div_rem(&rhs.significand);
346 q = q0;
347 r = r0;
348 } else {
349 let ndigits = digit_len::<B>(&q) + ddigits;
350 if ndigits < ddigits + self.precision {
351 let shift = ddigits + self.precision - ndigits;
353 shl_digits_in_place::<B>(&mut q, shift);
354 shl_digits_in_place::<B>(&mut r, shift);
355 e = e
356 .checked_sub(shift as isize)
357 .ok_or(FpError::Underflow(sign))?;
358
359 let (q0, r0) = r.div_rem(&rhs.significand);
360 q += q0;
361 r = r0;
362 }
363 }
364
365 let repr = if r.is_zero() {
366 Approximation::Exact(make_div_repr(sign_negative, q, e))
367 } else {
368 let adjust = R::round_ratio(&q, r, &rhs.significand);
369 Approximation::Inexact(make_div_repr(sign_negative, q + adjust, e), adjust)
370 };
371 Ok(repr)
372 }
373
374 pub(crate) fn repr_rem<const B: Word>(&self, lhs: Repr<B>, rhs: Repr<B>) -> Rounded<Repr<B>> {
375 assert_finite_operands(&lhs, &rhs);
376
377 let lhs_is_neg_zero = lhs.is_neg_zero();
378 let (lhs_sign, lhs_signif) = lhs.significand.into_parts();
379 let (_, rhs_signif) = rhs.significand.into_parts();
380
381 use core::cmp::Ordering;
382 let significand = match lhs.exponent.cmp(&rhs.exponent) {
383 Ordering::Equal => {
384 let r1 = lhs_signif % &rhs_signif;
385 let r2 = rhs_signif - &r1;
386 if r1 < r2 {
387 IBig::from_parts(lhs_sign, r1)
388 } else {
389 IBig::from_parts(-lhs_sign, r2)
390 }
391 }
392 Ordering::Greater => {
393 let modulo = ConstDivisor::new(rhs_signif);
396 let shift = (lhs.exponent - rhs.exponent) as usize;
397 let scaling = if B == 2 {
398 (UBig::ONE << shift).into_ring(&modulo)
399 } else {
400 UBig::from_word(B).into_ring(&modulo).pow(&shift.into())
401 };
402 let r = lhs_signif.into_ring(&modulo) * scaling;
403 let r1 = r.residue();
404 let r2 = (-r).residue();
405 if r1 < r2 {
406 IBig::from_parts(lhs_sign, r1)
407 } else {
408 IBig::from_parts(-lhs_sign, r2)
409 }
410 }
411 Ordering::Less => {
412 let shift = (rhs.exponent - lhs.exponent) as usize;
414 let (hi, lo) = split_digits::<B>(lhs_signif.into(), shift);
415
416 let mut r1 = hi % &rhs_signif;
417 let mut r2 = rhs_signif - &r1;
418
419 shl_digits_in_place::<B>(&mut r1, shift);
420 r1 += &lo;
421
422 shl_digits_in_place::<B>(&mut r2, shift);
423 r2 -= lo;
424
425 if r1 < r2 {
426 lhs_sign * r1
427 } else {
428 (-lhs_sign) * r2
429 }
430 }
431 };
432
433 let exponent = lhs.exponent.min(rhs.exponent);
434 if significand.is_zero() {
435 Approximation::Exact(if lhs_is_neg_zero {
437 Repr::neg_zero()
438 } else {
439 Repr::zero()
440 })
441 } else {
442 match Repr::new(significand, exponent).check_finite_exponent() {
443 Ok(repr) => self.repr_round(repr),
444 Err(e) => match e {
445 FpError::Overflow(sign) => {
446 Approximation::Inexact(Repr::infinity_with_sign(sign), Rounding::NoOp)
447 }
448 FpError::Underflow(sign) => {
449 Approximation::Inexact(Repr::zero_with_sign(sign), Rounding::NoOp)
450 }
451 _ => unreachable!(),
452 },
453 }
454 }
455 }
456
457 pub fn div<const B: Word>(&self, lhs: &Repr<B>, rhs: &Repr<B>) -> FpResult<FBig<R, B>> {
481 if lhs.is_infinite() || rhs.is_infinite() {
482 return Err(FpError::InfiniteInput);
483 }
484 if lhs.significand.is_zero() && rhs.significand.is_zero() {
485 return Err(FpError::Indeterminate); }
487
488 let lhs_repr = if !lhs.is_pos_zero() && lhs.digits_ub() > rhs.digits_lb() + self.precision {
489 Self::new(rhs.digits() + self.precision)
491 .repr_round_ref(lhs)
492 .value()
493 } else {
494 lhs.clone()
495 };
496 Ok(self
497 .repr_div(lhs_repr, rhs.clone())?
498 .map(|v| FBig::new(v, *self)))
499 }
500
501 pub fn rem<const B: Word>(&self, lhs: &Repr<B>, rhs: &Repr<B>) -> FpResult<FBig<R, B>> {
522 if lhs.is_infinite() || rhs.is_infinite() {
523 return Err(FpError::InfiniteInput);
524 }
525 Ok(self
526 .repr_rem(lhs.clone(), rhs.clone())
527 .map(|v| FBig::new(v, *self)))
528 }
529
530 #[inline]
545 pub fn inv<const B: Word>(&self, f: &Repr<B>) -> FpResult<FBig<R, B>> {
546 if f.is_infinite() {
547 return Err(FpError::InfiniteInput);
548 }
549 Ok(self
551 .repr_div(Repr::one(), f.clone())?
552 .map(|v| FBig::new(v, *self)))
553 }
554}
555
556#[cfg(test)]
557mod tests {
558 use super::*;
559 use crate::round::mode;
560
561 fn r2(sig: i32, exp: isize) -> Repr<2> {
562 Repr::new(sig.into(), exp)
563 }
564
565 #[test]
566 fn test_div_by_zero_is_infinity() {
567 let ctx = Context::<mode::HalfEven>::new(53);
568 let pos = ctx.div::<2>(&r2(1, 0), &Repr::<2>::zero()).unwrap().value();
570 assert!(pos.repr().is_infinite());
571 assert_eq!(pos.repr().sign(), Sign::Positive);
572
573 let neg = ctx
574 .div::<2>(&r2(-1, 0), &Repr::<2>::zero())
575 .unwrap()
576 .value();
577 assert_eq!(neg.repr().sign(), Sign::Negative);
578
579 let neg2 = ctx
581 .div::<2>(&r2(1, 0), &Repr::<2>::neg_zero())
582 .unwrap()
583 .value();
584 assert_eq!(neg2.repr().sign(), Sign::Negative);
585 }
586
587 #[test]
588 fn test_zero_over_zero_is_indeterminate() {
589 let ctx = Context::<mode::HalfEven>::new(53);
590 assert_eq!(
591 ctx.div::<2>(&Repr::<2>::zero(), &Repr::<2>::zero()),
592 Err(FpError::Indeterminate)
593 );
594 }
595
596 #[test]
597 fn test_inv_zero_is_infinity() {
598 let ctx = Context::<mode::HalfEven>::new(53);
599 let r = ctx.inv::<2>(&Repr::<2>::zero()).unwrap().value();
600 assert!(r.repr().is_infinite());
601 assert_eq!(r.repr().sign(), Sign::Positive);
602 }
603
604 #[test]
605 fn test_fbig_div_zero_produces_infinity() {
606 let one = FBig::<mode::HalfEven>::try_from(1.0f64).unwrap();
608 let zero = FBig::<mode::HalfEven>::try_from(0.0f64).unwrap();
609 let inf = one / zero;
610 assert!(inf.repr().is_infinite());
611 }
612
613 #[test]
614 #[should_panic]
615 fn test_fbig_zero_over_zero_panics() {
616 let zero = FBig::<mode::HalfEven>::try_from(0.0f64).unwrap();
618 let _ = zero.clone() / zero;
619 }
620
621 #[test]
625 fn test_div_directed_underflow() {
626 use dashu_int::IBig;
627 let p = 53;
628 let floor_up = FBig::<mode::Up, 2>::from_parts(IBig::ONE, isize::MIN)
629 .with_precision(p)
630 .value();
631 let floor_down = FBig::<mode::Down, 2>::from_parts(IBig::ONE, isize::MIN)
632 .with_precision(p)
633 .value();
634 let three_up = FBig::<mode::Up, 2>::from_parts(IBig::from(3), 0)
635 .with_precision(p)
636 .value();
637 let three_down = FBig::<mode::Down, 2>::from_parts(IBig::from(3), 0)
638 .with_precision(p)
639 .value();
640 let up = floor_up / &three_up;
641 let down = floor_down / &three_down;
642 assert_eq!(up.repr().significand(), &IBig::ONE);
643 assert_eq!(up.repr().exponent(), isize::MIN);
644 assert!(down.repr().is_pos_zero());
645 assert!(up > down);
646 }
647}