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, shl_digits_in_place, split_digits},
8};
9use core::cmp::Ordering;
10use core::ops::{Div, DivAssign, Rem, RemAssign};
11use dashu_base::{
12 AbsOrd, Approximation, DivEuclid, DivRem, DivRemEuclid, Inverse, RemEuclid, Sign, Signed,
13 UnsignedAbs,
14};
15use dashu_int::{fast_div::ConstDivisor, modular::IntoRing, IBig, UBig};
16
17fn make_div_repr<const B: Word>(
21 sign_negative: bool,
22 significand: IBig,
23 exponent: isize,
24) -> Repr<B> {
25 if significand.is_zero() {
26 if sign_negative {
27 Repr::neg_zero()
28 } else {
29 Repr::zero()
30 }
31 } else {
32 Repr::new(significand, exponent)
33 }
34}
35
36macro_rules! impl_div_for_fbig {
37 (impl $op:ident, $method:ident, $repr_method:ident) => {
38 impl<R: Round, const B: Word> $op<FBig<R, B>> for FBig<R, B> {
39 type Output = FBig<R, B>;
40 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
41 let context = Context::max(self.context, rhs.context);
42 let result = context
45 .$repr_method(self.repr, rhs.repr)
46 .map(|r| r.map(|repr| FBig::new(repr, context)));
47 context.unwrap_fp(result)
48 }
49 }
50
51 impl<'l, R: Round, const B: Word> $op<FBig<R, B>> for &'l FBig<R, B> {
52 type Output = FBig<R, B>;
53 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
54 let context = Context::max(self.context, rhs.context);
55 let result = context
56 .$repr_method(self.repr.clone(), rhs.repr)
57 .map(|r| r.map(|repr| FBig::new(repr, context)));
58 context.unwrap_fp(result)
59 }
60 }
61
62 impl<'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for FBig<R, B> {
63 type Output = FBig<R, B>;
64 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
65 let context = Context::max(self.context, rhs.context);
66 let result = context
67 .$repr_method(self.repr, rhs.repr.clone())
68 .map(|r| r.map(|repr| FBig::new(repr, context)));
69 context.unwrap_fp(result)
70 }
71 }
72
73 impl<'l, 'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for &'l FBig<R, B> {
74 type Output = FBig<R, B>;
75 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
76 let context = Context::max(self.context, rhs.context);
77 let result = context
78 .$repr_method(self.repr.clone(), rhs.repr.clone())
79 .map(|r| r.map(|repr| FBig::new(repr, context)));
80 context.unwrap_fp(result)
81 }
82 }
83 };
84}
85
86macro_rules! impl_rem_for_fbig {
87 (impl $op:ident, $method:ident, $repr_method:ident) => {
88 impl<R: Round, const B: Word> $op<FBig<R, B>> for FBig<R, B> {
89 type Output = FBig<R, B>;
90 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
91 let context = Context::max(self.context, rhs.context);
92 FBig::new(context.$repr_method(self.repr, rhs.repr).value(), context)
93 }
94 }
95
96 impl<'l, R: Round, const B: Word> $op<FBig<R, B>> for &'l FBig<R, B> {
97 type Output = FBig<R, B>;
98 fn $method(self, rhs: FBig<R, B>) -> Self::Output {
99 let context = Context::max(self.context, rhs.context);
100 FBig::new(context.$repr_method(self.repr.clone(), rhs.repr).value(), context)
101 }
102 }
103
104 impl<'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for FBig<R, B> {
105 type Output = FBig<R, B>;
106 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
107 let context = Context::max(self.context, rhs.context);
108 FBig::new(context.$repr_method(self.repr, rhs.repr.clone()).value(), context)
109 }
110 }
111
112 impl<'l, 'r, R: Round, const B: Word> $op<&'r FBig<R, B>> for &'l FBig<R, B> {
113 type Output = FBig<R, B>;
114 fn $method(self, rhs: &FBig<R, B>) -> Self::Output {
115 let context = Context::max(self.context, rhs.context);
116 FBig::new(
117 context
118 .$repr_method(self.repr.clone(), rhs.repr.clone())
119 .value(),
120 context,
121 )
122 }
123 }
124 };
125}
126impl_div_for_fbig!(impl Div, div, repr_div);
127impl_rem_for_fbig!(impl Rem, rem, repr_rem);
128impl_binop_assign_by_taking!(impl DivAssign<Self>, div_assign, div);
129impl_binop_assign_by_taking!(impl RemAssign<Self>, rem_assign, rem);
130
131impl<R: Round, const B: Word> DivEuclid<FBig<R, B>> for FBig<R, B> {
132 type Output = IBig;
133 #[inline]
134 fn div_euclid(self, rhs: FBig<R, B>) -> Self::Output {
135 let (num, den) = align_as_int(self, rhs);
136 num.div_euclid(den)
137 }
138}
139
140impl<R: Round, const B: Word> DivEuclid<FBig<R, B>> for &FBig<R, B> {
141 type Output = IBig;
142 #[inline]
143 fn div_euclid(self, rhs: FBig<R, B>) -> Self::Output {
144 self.clone().div_euclid(rhs)
145 }
146}
147
148impl<R: Round, const B: Word> DivEuclid<&FBig<R, B>> for FBig<R, B> {
149 type Output = IBig;
150 #[inline]
151 fn div_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
152 self.div_euclid(rhs.clone())
153 }
154}
155
156impl<R: Round, const B: Word> DivEuclid<&FBig<R, B>> for &FBig<R, B> {
157 type Output = IBig;
158 #[inline]
159 fn div_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
160 self.clone().div_euclid(rhs.clone())
161 }
162}
163
164impl<R: Round, const B: Word> RemEuclid<FBig<R, B>> for FBig<R, B> {
165 type Output = FBig<R, B>;
166 #[inline]
167 fn rem_euclid(self, rhs: FBig<R, B>) -> Self::Output {
168 let r_exponent = self.repr.exponent.min(rhs.repr.exponent);
169 let context = Context::max(self.context, rhs.context);
170
171 let (num, den) = align_as_int(self, rhs);
172 let r = num.rem_euclid(den);
173 let mut r = context.convert_int(r.into()).value();
174 if !r.repr.significand.is_zero() {
175 r.repr.exponent += r_exponent;
176 }
177 r
178 }
179}
180
181impl<R: Round, const B: Word> RemEuclid<FBig<R, B>> for &FBig<R, B> {
182 type Output = FBig<R, B>;
183 #[inline]
184 fn rem_euclid(self, rhs: FBig<R, B>) -> Self::Output {
185 self.clone().rem_euclid(rhs)
186 }
187}
188
189impl<R: Round, const B: Word> RemEuclid<&FBig<R, B>> for FBig<R, B> {
190 type Output = FBig<R, B>;
191 #[inline]
192 fn rem_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
193 self.rem_euclid(rhs.clone())
194 }
195}
196
197impl<R: Round, const B: Word> RemEuclid<&FBig<R, B>> for &FBig<R, B> {
198 type Output = FBig<R, B>;
199 #[inline]
200 fn rem_euclid(self, rhs: &FBig<R, B>) -> Self::Output {
201 self.clone().rem_euclid(rhs.clone())
202 }
203}
204
205impl<R: Round, const B: Word> DivRemEuclid<FBig<R, B>> for FBig<R, B> {
206 type OutputDiv = IBig;
207 type OutputRem = FBig<R, B>;
208 #[inline]
209 fn div_rem_euclid(self, rhs: FBig<R, B>) -> (IBig, FBig<R, B>) {
210 let r_exponent = self.repr.exponent.min(rhs.repr.exponent);
211 let context = Context::max(self.context, rhs.context);
212
213 let (num, den) = align_as_int(self, rhs);
214 let (q, r) = num.div_rem_euclid(den);
215 let mut r = context.convert_int(r.into()).value();
216 if !r.repr.significand.is_zero() {
217 r.repr.exponent += r_exponent;
218 }
219 (q, r)
220 }
221}
222
223impl<R: Round, const B: Word> DivRemEuclid<FBig<R, B>> for &FBig<R, B> {
224 type OutputDiv = IBig;
225 type OutputRem = FBig<R, B>;
226 #[inline]
227 fn div_rem_euclid(self, rhs: FBig<R, B>) -> (IBig, FBig<R, B>) {
228 self.clone().div_rem_euclid(rhs)
229 }
230}
231
232impl<R: Round, const B: Word> DivRemEuclid<&FBig<R, B>> for FBig<R, B> {
233 type OutputDiv = IBig;
234 type OutputRem = FBig<R, B>;
235 #[inline]
236 fn div_rem_euclid(self, rhs: &FBig<R, B>) -> (IBig, FBig<R, B>) {
237 self.div_rem_euclid(rhs.clone())
238 }
239}
240
241impl<R: Round, const B: Word> DivRemEuclid<&FBig<R, B>> for &FBig<R, B> {
242 type OutputDiv = IBig;
243 type OutputRem = FBig<R, B>;
244 #[inline]
245 fn div_rem_euclid(self, rhs: &FBig<R, B>) -> (IBig, FBig<R, B>) {
246 self.clone().div_rem_euclid(rhs.clone())
247 }
248}
249
250macro_rules! impl_div_primitive_with_fbig {
251 ($($t:ty)*) => {$(
252 helper_macros::impl_binop_with_primitive!(impl Div<$t>, div);
253 helper_macros::impl_binop_assign_with_primitive!(impl DivAssign<$t>, div_assign);
254 )*};
255}
256impl_div_primitive_with_fbig!(u8 u16 u32 u64 u128 usize UBig i8 i16 i32 i64 i128 isize IBig);
257impl<R: Round, const B: Word> Inverse for FBig<R, B> {
260 type Output = FBig<R, B>;
261
262 #[inline]
263 fn inv(self) -> Self::Output {
264 self.context.unwrap_fp(self.context.inv(&self.repr))
265 }
266}
267
268impl<R: Round, const B: Word> Inverse for &FBig<R, B> {
269 type Output = FBig<R, B>;
270
271 #[inline]
272 fn inv(self) -> Self::Output {
273 self.context.unwrap_fp(self.context.inv(&self.repr))
274 }
275}
276
277impl<R: Round, const B: Word> FBig<R, B> {
278 #[inline]
284 pub fn inv(&self) -> Self {
285 self.context.unwrap_fp(self.context.inv(&self.repr))
286 }
287}
288
289fn align_as_int<R: Round, const B: Word>(lhs: FBig<R, B>, rhs: FBig<R, B>) -> (IBig, IBig) {
291 let ediff = lhs.repr.exponent - rhs.repr.exponent;
292 let (mut num, mut den) = (lhs.repr.significand, rhs.repr.significand);
293 if ediff >= 0 {
294 shl_digits_in_place::<B>(&mut num, ediff as _);
295 } else {
296 shl_digits_in_place::<B>(&mut den, (-ediff) as _);
297 }
298 (num, den)
299}
300
301impl<R: Round> Context<R> {
302 pub(crate) fn repr_div<const B: Word>(&self, lhs: Repr<B>, rhs: Repr<B>) -> FpResult<Repr<B>> {
308 debug_assert!(
309 digit_len::<B>(&lhs.significand) <= digit_len::<B>(&rhs.significand) + self.precision
310 );
311 self.repr_div_split(lhs, rhs, IBig::ZERO, 0)
312 }
313
314 pub(crate) fn repr_div_any_width<const B: Word>(
329 &self,
330 lhs: Repr<B>,
331 rhs: Repr<B>,
332 ) -> FpResult<Repr<B>> {
333 if lhs.digits_ub() <= rhs.digits_lb() + self.precision {
335 return self.repr_div(lhs, rhs);
336 }
337
338 let ddigits = digit_len::<B>(&rhs.significand);
340 let k = digit_len::<B>(&lhs.significand).saturating_sub(ddigits + self.precision);
341 if k == 0 {
342 return self.repr_div(lhs, rhs);
343 }
344 let (hi, lo) = split_digits::<B>(lhs.significand, k);
345 debug_assert_eq!(digit_len::<B>(&hi), ddigits + self.precision);
346 let lo = rhs.significand.sign() * lo;
349 self.repr_div_split(
350 Repr {
351 significand: hi,
352 exponent: lhs.exponent,
353 },
354 rhs,
355 lo,
356 k,
357 )
358 }
359
360 fn repr_div_split<const B: Word>(
365 &self,
366 lhs: Repr<B>,
367 rhs: Repr<B>,
368 lo: IBig,
369 k: usize,
370 ) -> FpResult<Repr<B>> {
371 assert_finite_operands(&lhs, &rhs);
372 assert_limited_precision(self.precision);
373
374 let sign_negative = lhs.sign() != rhs.sign();
375 let sign = if sign_negative {
376 Sign::Negative
377 } else {
378 Sign::Positive
379 };
380
381 if rhs.significand.is_zero() {
382 if lhs.significand.is_zero() {
383 } else {
386 return Ok(Approximation::Exact(Repr::infinity_with_sign(sign)));
388 }
389 }
390
391 let (num, den) = if rhs.significand.is_positive() {
396 (lhs.significand, rhs.significand)
397 } else {
398 (-lhs.significand, -rhs.significand)
399 };
400
401 let (mut q, mut r) = num.div_rem(&den);
402 let mut e = lhs
403 .exponent
404 .checked_add(k as isize)
405 .and_then(|e| e.checked_sub(rhs.exponent))
406 .ok_or({
407 if lhs.exponent >= 0 {
410 FpError::Overflow(sign)
411 } else {
412 FpError::Underflow(sign)
413 }
414 })?;
415
416 let mut qdigits = digit_len::<B>(&q);
422
423 if r.is_zero() && k == 0 && qdigits <= self.precision {
426 return Ok(Approximation::Exact(
427 make_div_repr(sign_negative, q, e).check_finite_exponent()?,
428 ));
429 }
430
431 if q.is_zero() {
432 let ddigits = digit_len::<B>(&den);
434 let rdigits = digit_len::<B>(&r); let shift = ddigits + self.precision - rdigits;
436 shl_digits_in_place::<B>(&mut r, shift);
437 e = e
438 .checked_sub(shift as isize)
439 .ok_or(FpError::Underflow(sign))?;
440 let (q0, r0) = r.div_rem(&den);
441 q = q0;
442 r = r0;
443 qdigits = if rdigits == ddigits {
447 self.precision
448 } else {
449 digit_len::<B>(&q)
450 };
451 } else if qdigits < self.precision {
452 let shift = self.precision - qdigits;
454 shl_digits_in_place::<B>(&mut q, shift);
455 shl_digits_in_place::<B>(&mut r, shift);
456 e = e
457 .checked_sub(shift as isize)
458 .ok_or(FpError::Underflow(sign))?;
459
460 let (q0, r0) = r.div_rem(&den);
461 q += q0;
462 r = r0;
463 qdigits = self.precision;
466 }
467
468 let den_f = if k > 0 { shl_digits::<B>(&den, k) } else { den };
472 let num_f = if k > 0 {
473 shl_digits::<B>(&r, k) + lo
474 } else {
475 r
476 };
477
478 let repr = if qdigits > self.precision {
484 debug_assert_eq!(qdigits, self.precision + 1);
485 let (qh, ql) = split_digits::<B>(q, 1);
486 let e2 = e.saturating_add(1);
487
488 let ql_is_zero = ql.is_zero();
499 let low_sign = if ql_is_zero { num_f.sign() } else { ql.sign() };
500
501 if ql_is_zero && num_f.is_zero() {
502 Approximation::Exact(make_div_repr(sign_negative, qh, e2))
503 } else {
504 let adjust = R::round_low_part(&qh, low_sign, || {
505 let ql_mag: Word = ql.unsigned_abs().try_into().unwrap();
509 if ql_mag > B - ql_mag {
510 Ordering::Greater
513 } else {
514 let diff = B - ql_mag * 2; if diff % 2 == 0 {
516 match diff / 2 {
519 0 => num_f.abs_cmp(&IBig::ZERO),
520 1 => num_f.abs_cmp(&den_f),
521 f => num_f.abs_cmp(&(f * &den_f)),
522 }
523 } else {
524 (&num_f + &num_f).abs_cmp(&(diff * &den_f))
526 }
527 }
528 });
529 Approximation::Inexact(make_div_repr(sign_negative, qh + adjust, e2), adjust)
530 }
531 } else if num_f.is_zero() {
532 Approximation::Exact(make_div_repr(sign_negative, q, e))
533 } else {
534 let adjust = R::round_ratio(&q, num_f, &den_f);
535 Approximation::Inexact(make_div_repr(sign_negative, q + adjust, e), adjust)
536 };
537 let repr = match repr {
538 Approximation::Exact(v) => Approximation::Exact(v.check_finite_exponent()?),
539 Approximation::Inexact(v, flag) => {
540 Approximation::Inexact(v.check_finite_exponent()?, flag)
541 }
542 };
543 Ok(repr)
544 }
545
546 pub(crate) fn repr_rem<const B: Word>(&self, lhs: Repr<B>, rhs: Repr<B>) -> Rounded<Repr<B>> {
547 assert_finite_operands(&lhs, &rhs);
548
549 let lhs_is_neg_zero = lhs.is_neg_zero();
550 let (lhs_sign, lhs_signif) = lhs.significand.into_parts();
551 let (_, rhs_signif) = rhs.significand.into_parts();
552
553 use core::cmp::Ordering;
554 let significand = match lhs.exponent.cmp(&rhs.exponent) {
555 Ordering::Equal => {
556 let r1 = lhs_signif % &rhs_signif;
557 let r2 = rhs_signif - &r1;
558 if r1 < r2 {
559 IBig::from_parts(lhs_sign, r1)
560 } else {
561 IBig::from_parts(-lhs_sign, r2)
562 }
563 }
564 Ordering::Greater => {
565 let modulo = ConstDivisor::new(rhs_signif);
568 let shift = (lhs.exponent - rhs.exponent) as usize;
569 let scaling = if B == 2 {
570 (UBig::ONE << shift).into_ring(&modulo)
571 } else {
572 UBig::from_word(B).into_ring(&modulo).pow(&shift.into())
573 };
574 let r = lhs_signif.into_ring(&modulo) * scaling;
575 let r1 = r.residue();
576 let r2 = (-r).residue();
577 if r1 < r2 {
578 IBig::from_parts(lhs_sign, r1)
579 } else {
580 IBig::from_parts(-lhs_sign, r2)
581 }
582 }
583 Ordering::Less => {
584 let shift = (rhs.exponent - lhs.exponent) as usize;
586 let (hi, lo) = split_digits::<B>(lhs_signif.into(), shift);
587
588 let mut r1 = hi % &rhs_signif;
589 let mut r2 = rhs_signif - &r1;
590
591 shl_digits_in_place::<B>(&mut r1, shift);
592 r1 += &lo;
593
594 shl_digits_in_place::<B>(&mut r2, shift);
595 r2 -= lo;
596
597 if r1 < r2 {
598 lhs_sign * r1
599 } else {
600 (-lhs_sign) * r2
601 }
602 }
603 };
604
605 let exponent = lhs.exponent.min(rhs.exponent);
606 if significand.is_zero() {
607 Approximation::Exact(if lhs_is_neg_zero {
609 Repr::neg_zero()
610 } else {
611 Repr::zero()
612 })
613 } else {
614 match Repr::new(significand, exponent).check_finite_exponent() {
615 Ok(repr) => self.repr_round(repr),
616 Err(e) => match e {
617 FpError::Overflow(sign) => {
618 Approximation::Inexact(Repr::infinity_with_sign(sign), Rounding::NoOp)
619 }
620 FpError::Underflow(sign) => {
621 Approximation::Inexact(Repr::zero_with_sign(sign), Rounding::NoOp)
622 }
623 _ => unreachable!(),
624 },
625 }
626 }
627 }
628
629 pub fn div<const B: Word>(&self, lhs: &Repr<B>, rhs: &Repr<B>) -> FpResult<FBig<R, B>> {
653 if lhs.is_infinite() || rhs.is_infinite() {
654 return Err(FpError::InfiniteInput);
655 }
656 if lhs.significand.is_zero() && rhs.significand.is_zero() {
657 return Err(FpError::Indeterminate); }
659
660 Ok(self
667 .repr_div_any_width(lhs.clone(), rhs.clone())?
668 .map(|v| FBig::new(v, *self)))
669 }
670
671 pub fn rem<const B: Word>(&self, lhs: &Repr<B>, rhs: &Repr<B>) -> FpResult<FBig<R, B>> {
692 if lhs.is_infinite() || rhs.is_infinite() {
693 return Err(FpError::InfiniteInput);
694 }
695 Ok(self
696 .repr_rem(lhs.clone(), rhs.clone())
697 .map(|v| FBig::new(v, *self)))
698 }
699
700 #[inline]
715 pub fn inv<const B: Word>(&self, f: &Repr<B>) -> FpResult<FBig<R, B>> {
716 if f.is_infinite() {
717 return Err(FpError::InfiniteInput);
718 }
719 Ok(self
721 .repr_div(Repr::one(), f.clone())?
722 .map(|v| FBig::new(v, *self)))
723 }
724}
725
726#[cfg(test)]
727mod tests {
728 use super::*;
729 use crate::round::mode;
730 use dashu_base::Approximation::*;
731
732 fn r2(sig: i32, exp: isize) -> Repr<2> {
733 Repr::new(sig.into(), exp)
734 }
735
736 #[test]
741 fn test_div_quotient_reduced_and_flagged() {
742 let r = Context::<mode::Zero>::new(2)
745 .div(&r2(15, 0), &r2(3, 0))
746 .unwrap();
747 assert!(matches!(r, Inexact(..)), "15/3 @ p2 must be inexact");
748 assert_eq!(r.value().repr(), &r2(1, 2));
749
750 let r = Context::<mode::Zero>::new(1)
752 .div(&r2(5, 0), &r2(1, 0))
753 .unwrap();
754 assert!(matches!(r, Inexact(..)), "5/1 @ p1 must be inexact");
755 assert_eq!(r.value().repr(), &r2(1, 2));
756
757 let v = Context::<mode::Zero>::new(2)
760 .div(&r2(63, 0), &r2(3, 0))
761 .unwrap()
762 .value();
763 assert_eq!(v.repr(), &r2(1, 4));
764 }
765
766 #[test]
771 fn test_div_overwide_quotient_nearest_modes() {
772 let r10 = |sig: i32, exp: isize| Repr::<10>::new(IBig::from(sig), exp);
776 let v = Context::<mode::HalfEven>::new(1)
777 .div(&r10(-21, 0), &r10(2, 0))
778 .unwrap()
779 .value();
780 assert_eq!(v.repr(), &r10(-1, 1)); let v = Context::<mode::HalfEven>::new(1)
784 .div(&r10(21, 0), &r10(2, 0))
785 .unwrap()
786 .value();
787 assert_eq!(v.repr(), &r10(1, 1));
788
789 let v = Context::<mode::HalfEven>::new(1)
791 .div(&r10(-95, 0), &r10(2, 0))
792 .unwrap()
793 .value();
794 assert_eq!(v.repr(), &r10(-5, 1)); let v = Context::<mode::HalfEven>::new(1)
798 .div(&r10(-41, 0), &r10(2, 0))
799 .unwrap()
800 .value();
801 assert_eq!(v.repr(), &r10(-2, 1)); let r3 = |sig: i32, exp: isize| Repr::<3>::new(IBig::from(sig), exp);
807 let v = Context::<mode::HalfEven>::new(1)
808 .div(&r3(-8, 0), &r3(2, 0))
809 .unwrap()
810 .value();
811 assert_eq!(v.repr(), &r3(-1, 1)); let v = Context::<mode::HalfEven>::new(1)
813 .div(&r3(-11, 0), &r3(2, 0))
814 .unwrap()
815 .value();
816 assert_eq!(v.repr(), &r3(-2, 1)); }
818
819 #[test]
820 fn test_div_directed_modes_on_wide_dividend() {
821 let v = Context::<mode::Up>::new(1)
824 .div(&r2(7, 0), &r2(-1, 0))
825 .unwrap()
826 .value();
827 assert_eq!(v.repr(), &r2(-1, 2));
828
829 let v = Context::<mode::HalfEven>::new(1)
833 .div(&Repr::<3>::new(IBig::from(31), 0), &Repr::<3>::new(IBig::from(4), 0))
834 .unwrap()
835 .value();
836 assert_eq!(v.repr(), &Repr::<3>::new(IBig::from(1), 2));
837 }
838
839 #[test]
842 fn test_div_p1_digit_quotient_rounds_on_precision_grid() {
843 let v = Context::<mode::HalfEven>::new(2)
847 .div(&r2(3, 0), &r2(5, 0))
848 .unwrap()
849 .value();
850 assert_eq!(v.repr(), &r2(1, -1));
851
852 let v = Context::<mode::HalfEven>::new(2)
856 .div(&r2(14, 0), &r2(3, 0))
857 .unwrap()
858 .value();
859 assert_eq!(v.repr(), &r2(1, 2));
860
861 let v = Context::<mode::HalfEven>::new(2)
865 .div(&r2(7, 0), &r2(2, 0))
866 .unwrap()
867 .value();
868 assert_eq!(v.repr(), &r2(1, 2));
869 }
870
871 #[test]
874 fn test_div_sticky_low_digits() {
875 let v = Context::<mode::HalfEven>::new(1)
877 .div(&r2(15, -1), &r2(5, 0))
878 .unwrap()
879 .value();
880 assert_eq!(v.repr(), &r2(1, 1));
881
882 let v = Context::<mode::Up>::new(1)
884 .div(&r2(15, -1), &r2(5, 0))
885 .unwrap()
886 .value();
887 assert_eq!(v.repr(), &r2(1, 1));
888
889 let v = Context::<mode::HalfEven>::new(2)
893 .div(&r2(11, 0), &r2(1, 0))
894 .unwrap()
895 .value();
896 assert_eq!(v.repr(), &r2(3, 2));
897
898 let v = Context::<mode::Up>::new(1)
901 .div(&r2(25, -1), &r2(5, 0))
902 .unwrap()
903 .value();
904 assert_eq!(v.repr(), &r2(1, 2));
905 }
906
907 #[test]
908 fn test_div_by_zero_is_infinity() {
909 let ctx = Context::<mode::HalfEven>::new(53);
910 let pos = ctx.div::<2>(&r2(1, 0), &Repr::<2>::zero()).unwrap().value();
912 assert!(pos.repr().is_infinite());
913 assert_eq!(pos.repr().sign(), Sign::Positive);
914
915 let neg = ctx
916 .div::<2>(&r2(-1, 0), &Repr::<2>::zero())
917 .unwrap()
918 .value();
919 assert_eq!(neg.repr().sign(), Sign::Negative);
920
921 let neg2 = ctx
923 .div::<2>(&r2(1, 0), &Repr::<2>::neg_zero())
924 .unwrap()
925 .value();
926 assert_eq!(neg2.repr().sign(), Sign::Negative);
927 }
928
929 #[test]
930 fn test_zero_over_zero_is_indeterminate() {
931 let ctx = Context::<mode::HalfEven>::new(53);
932 assert_eq!(
933 ctx.div::<2>(&Repr::<2>::zero(), &Repr::<2>::zero()),
934 Err(FpError::Indeterminate)
935 );
936 }
937
938 #[test]
939 fn test_inv_zero_is_infinity() {
940 let ctx = Context::<mode::HalfEven>::new(53);
941 let r = ctx.inv::<2>(&Repr::<2>::zero()).unwrap().value();
942 assert!(r.repr().is_infinite());
943 assert_eq!(r.repr().sign(), Sign::Positive);
944 }
945
946 #[test]
947 fn test_fbig_div_zero_produces_infinity() {
948 let one = FBig::<mode::HalfEven>::try_from(1.0f64).unwrap();
950 let zero = FBig::<mode::HalfEven>::try_from(0.0f64).unwrap();
951 let inf = one / zero;
952 assert!(inf.repr().is_infinite());
953 }
954
955 #[test]
956 #[should_panic]
957 fn test_fbig_zero_over_zero_panics() {
958 let zero = FBig::<mode::HalfEven>::try_from(0.0f64).unwrap();
960 let _ = zero.clone() / zero;
961 }
962
963 #[test]
967 fn test_div_directed_underflow() {
968 use dashu_int::IBig;
969 let p = 53;
970 let floor_up = FBig::<mode::Up, 2>::from_parts(IBig::ONE, isize::MIN)
971 .with_precision(p)
972 .value();
973 let floor_down = FBig::<mode::Down, 2>::from_parts(IBig::ONE, isize::MIN)
974 .with_precision(p)
975 .value();
976 let three_up = FBig::<mode::Up, 2>::from_parts(IBig::from(3), 0)
977 .with_precision(p)
978 .value();
979 let three_down = FBig::<mode::Down, 2>::from_parts(IBig::from(3), 0)
980 .with_precision(p)
981 .value();
982 let up = floor_up / &three_up;
983 let down = floor_down / &three_down;
984 assert_eq!(up.repr().significand(), &IBig::ONE);
985 assert_eq!(up.repr().exponent(), isize::MIN);
986 assert!(down.repr().is_pos_zero());
987 assert!(up > down);
988 }
989}